이번 글에서는 Chapter 5 내용을 정리해보려고 합니다.
Chapter 5.1 제어 흐름 개요 (Control Flow)
코드 흐름을 제어하는 방법에 대해서 다룬다는 intro 강의이다.
중단, 점프, 조건 분기, 반복 까지는 이번 챕터에서 다루고, 예외 처리는 뒤에서 다룬다고 한다.
std::cout << "I love you " << std::endl;
exit(0); // 프로그램을 강제 종료
std::cout << "I love you " << std::endl;
exit(0);를 사용하면, 강제로 해당 프로그램을 원하는 위치에서 강제종료 할 수 있다.
배포되는 코드에 사용되기보다는, 프로그래밍을 분석하거나 디버깅하는 용도로 사용한다고 보면 된다.
Chapter 5.2 조건문 if
이번 강의에서는 조건문 if에 대해서 다룬다. 다른 언어를 다뤄보신 분이라면 C++도 큰 그림에서는 그다지 차이가 없기 때문에 손쉽게 익힐 수 있다.
예시 코드 1
int x;
cin >> x;
if (x > 10)
{
cout << x << " is greater than 10" << endl;
}
else
{
cout << x << " is not greater than 10" << endl;
}
if (x > 10)
{
cout << "x is greater than 10" << endl;
}
else if (x < 10)
{
cout << "x is less than 10" << endl;
}
else
{
cout << "x is 10" << endl;
}
기본적인 사용법인데, if 안에 조건을 걸어주고 해당 조건을 만족하면 그 아래 코드가 실행되는 식이다.
{ }를 사용하지 않아도 if 문을 사용할 수 있지만, 그럴 경우 1줄만 실행되며 여러 줄 실행이 불가능하다.
필자는 이미 { }를 사용하는 것에 너무 익숙해져서 기본적으로는 다 { }로 묶어주고 있다. C++는 특히 scope가 중요해서 { }로 묶어서 표현하는 게 인지하기가 편하다고 생각하고 있다.
기본적으로는 if / else 로 분기할 수 있고, 조금 더 세부적으로 사용하는 경우 if /else if / else로 분기할 수 있다.
예시 코드 2
int x;
cin >> x;
if (x > 10)
{
cout << "A" << endl;
}
else if (x == -1)
{
exit(0);
}
else if (x < 0)
{
cout << "B " << endl;
}
cout << "Hello " << endl;
if (x > 10)
; // null statement. 아무것도 하지 않음
if (x = 0) // x = 0; -> if (x) 판단. 주의해야함.
cout << x << endl;
if 문을 활용해서 어떤 조건을 만족시킬 때 프로그램이 강제 종료되도록 만들 수 있다. 이전 강의에서 언급된 exit(0)를 활용하는 방식이다.
그리고 if 문에 아무것도 하지 않게끔 코드를 작성할 수도 있다고 한다. 단, 이렇게 코드에 아무것도 없는 경우는 다른 사람들이 봤을 때 그 이유를 알 수 있도록 주석을 작성해 주는 게 바람직해 보인다.
만약 if (x = 0)으로 작성해버리면, 우선 x에 0을 할당하고 if (x)를 판단하게 된다. 따라서 실제로는 해당 if 문 안에 작성된 코드는 실행이 되지 않는다.
일반적으로는 if (x == 0)를 작성하다가 실수로 =를 빼먹는 경우이기는 하지만, if (x = 0)인 경우 어떻게 코드가 돌아가는지는 알아두는 것이 좋다고 한다.
Chapter 5.3 switch-case
이번 강의에서는 if문과 비슷한 듯 하지만 특정 케이스에서 더 깔끔하게 코드를 만들어주는 방식인 switch-case문에 대해서 알아본다.
예제 코드 1
#include <iostream>
using namespace std;
enum class Colors
{
BLACK,
WHITE,
RED,
GREEN,
BLUE
};
void printColorName(Colors color)
{
switch (static_cast<int>(color))
{
case 0:
cout << "Black" << endl;
break;
case 1:
cout << "White" << endl;
break;
case 2:
cout << "Red" << endl;
break;
case 3:
cout << "Green" << endl;
break;
case 4:
cout << "Blue" << endl;
break;
}
}
int main()
{
printColorName(Colors::BLACK);
}
색깔을 나타내는 enum class가 있는 상황에서, 색깔을 input으로 받아서 색을 출력해 주는 함수인 printColorName 함수를 실행하려고 한다.
이때, 들어온 input에 따라서 각각에 대응하는 출력을 해줄 수 있도록, switch 문에 color를 static cast로 int로 변환해 준 뒤, case 옆에 각각 0, 1, 2, 3, 4에 따라 어떤 내용을 출력해 줄지를 작성해 준다.
주의해야 할 점은 break;를 적지 않으면 그다음 case문도 순차적으로 실행된다.
예를 들어서, switch의 값이 2였을 때, case 2에 있는 내용이 실행되고, break가 없는 경우 그 뒤에 case 3과 case 4도 작동하게 된다. 따라서 이를 감안해서 case 2만 작동해야 한다면 반드시 break;를 걸어줘야 한다.
예시 코드 2
int x;
cin >> x;
switch (x)
{
int a; // 선언은 가능하지만
// int b = 5; // 초기화는 불가능. (메모리 할당 불가, case문 다음에서만 할 수 있음)
case 0:
{
int y = 5;
y = y + x;
cout << y << endl;
break;
}
case 1:
{
int y = 10;
y = y + x;
cout << "y: " << y << endl;
break;
}
default: // case에서 정의되지 않는 모든 경우
cout << "Undefined input: " << x << endl;
// break; // default는 아래에 다른 코드가 없으므로 break가 필요 없음.
}
switch 문에서 특이한 부분이 있다. 우선 swtich문 안에서 변수에 대한 선언은 가능하지만, 초기화는 불가능하다는 점이다. case 문이 오기 전에 int b = 5;처럼 변수를 초기화해주면 메모리 할당이 불가능하여 빌드가 안된다.
그리고 case문 안에서 지역 변수 따로 선언해서 사용할 수 있으며, case 문에 해당되지 않는 케이스(2, 3, 4...)에 대응하는 방안으로는 default:를 사용하는 방법이 있다.
Chapter 5.4 Goto
원하는 소스 코드의 위치로 이동할 수 있는 방법인 goto에 대해서 알아본다.
예시 코드 1
#include <iostream>
#include <cmath>
using namespace std;
int main()
{
double x;
tryAgain : // label
cout << "Enter a non-negative number" << endl;
cin >> x;
if (x < 0.0)
{
goto tryAgain;
}
cout << sqrt(x) << endl;
}
goto는 원하는 코드 위치로 이동할 수 있는 구문이다.
특이하게 goto를 사용할 때 선언하는 변수는 일반 코드와 indentation이 조금 다르다.
goto문을 사용하면, 특정 조건일 때 원하는 코드 위치로 이동할 수 있으며 이는 for문이나 while문과 같이 반복문 형태와 비슷하게 만들 수 있다.
Chapter 5.5 반복문 while
이번 강의에서는 while을 사용해서 반복하는 방법에 대해서 알아본다.
예제 코드 1
while (1)
{
static int count = 0; // static 사용하면 계속 올라감.
cout << count << endl;
++count;
if (count == 10) break;
}
while은 괄호 안에 조건을 만족시키는 경우만 반복하도록 하는 문법이다. 위 코드처럼 while (1)로 정의하면 무한 루프가 된다.
앞에서 배운 내용이지만 static을 선언하면 변수를 정적 메모리 영역에 가지고 있게 되므로 지역 변수이지만 계속 값이 더해지는 것을 확인할 수 있다.
예제 코드 2
unsigned int count = 10;
while (count >= 0)
{
if (count == 0)
{
cout << "Zero";
}
else
{
cout << count << " ";
}
count--;
}
count 변수가 unsigned int이기 때문에, 계속해서 값을 빼게 되면 0 이후로는 이상한 값이 되어버린다. 즉 overflow 현상이 발생하게 된다. 따라서 변수를 반복적으로 감소하거나 증가시키는 경우에는 이 부분을 주의해야 한다.
예제 코드 3
// 연습문제 1
// 1
// 1 2
// 1 2 3
// 1 2 3 4
// 1 2 3 4 5 만들기
int i = 1;
while (i <= 5)
{
int j = 1;
while (j <= i)
{
cout << j << " ";
j++;
}
i++;
cout << endl;
}
while을 두 개 반복해서 사용해 출력해 보는 연습문제이다.
위에서 주석으로 표시했듯이,
1
1 2
1 2 3
1 2 3 4
1 2 3 4 5 를 출력해 보는 문제다.
전체적으로 보면 5번을 반복해야 하니 바깥쪽에 while문이 하나 있어야 하고, row를 기준으로 보면 그 안에서도 반복문을 돌면서 출력해야 하므로 이중 while문이 요구되는 것을 알 수 있다.
첫 번째 줄에서는 row 기준으로는 한 번만 출력하고, 두 번째 줄에서는 두 번만 출력하니 i 번째 줄에서 i 번만 출력하도록 만들어야 한다는 것이 중요 포인트라고 볼 수 있다.
예제 코드 4
// 연습문제 2
// 5
// 5 4
// 5 4 3
// 5 4 3 2
// 5 4 3 2 1 만들기
int a = 5;
while (a > 0)
{
int b = 5;
while (b >= a)
{
cout << b << " ";
b--;
}
a--;
cout << endl;
}
예제 코드 3번과 반대의 경우이다.
이번에는 반대로 값이 줄어드는 형태이므로, --를 사용해야 한다는 점을 알고 있으면 된다.
예제 코드 5번
// 연습문제 3
// 1 x x x x
// 1 2 x x x
// 1 2 3 x x
// 1 2 3 4 x
// 1 2 3 4 5 만들기
int a_ = 1;
while (a_ <= 5)
{
int b_ = 1;
while (b_ <= 5)
{
if (b_ <= a_)
{
cout << b_ << " ";
}
else
{
cout << "x" << " ";
}
b_++;
}
a_++;
cout << endl;
}
이번에는 예제 코드 3번 하고 비슷하지만 빈 공간만큼을 x로 채우는 코드이다.
전체적으로 예제 코드 3번과 로직은 비슷하지만, 안쪽 while 문에서 if로 분기해서 i 번째 줄에서 i 번째 까지는 숫자를 출력하고, 그 외에는 x를 출력하도록 만들기만 하면 된다.
Chapter 5.6 반복문 do-while
반복문이지만 반드시 한 번은 실행해야 하는 경우에 사용하는 do while문에 대한 내용이다.
예제 코드 1
#include <iostream>
using namespace std;
int main()
{
int selection;
do
{
cout << "1. add" << endl;
cout << "2. sub" << endl;
cout << "3. mult" << endl;
cout << "4. div" << endl;
cin >> selection;
} while (selection <= 0 || selection >= 5);
cout << "You selected " << selection << endl;
}
do while문은 while 하고 사용 방식이 조금 달라서, 잘 확인해야 할 것 같다.
특이한 게 while이 뒤에 오고 기존 while 하고 동일하게 조건이 오며, 마지막에 ; 를 꼭 찍어줘야 한다.
Chapter 5.7 반복문 for
이번 강의에서는 while과 더불어서 가장 흔하게 쓰이는 while문에 대해서 알아본다.
예제 코드 1
for (int cnt = 0; cnt < 10; cnt++)
{
cout << cnt << endl;
}
for (int count = 9; count >= 0; count -= 2)
{
cout << count << endl;
}
for (int i = 0, j = 0; (i+j) < 10; ++i, j+=2)
{
cout << i << " " << j << endl;
}
우선 기본적인 for 문의 사용은 총 세 개 부분으로 나뉘는데, 카운터로 선언할 변수를 선언하는 부분, 반복 조건, 카운터의 변화를 정의하는 부분으로 나눠져 있다.
맨 처음 코드인 for (int cnt = 0; cnt < 10; cnt ++)이 가장 basic 한 쓰임이라고 볼 수 있다.
두 번째 코드는 카운터의 변화를 단순히 ++나 --만 사용하는 것이 아닌 다양하게 사용할 수 있음을 보여준다.
세 번째 코드는 여러 개의 변수를 카운터로 사용할 수 있음을 보여준다.
예제 코드 2
int cnt = 0;
for (; cnt < 10; cnt++)
//for(;;++cnt) // 무한루프
{
cout << cnt << endl;
}
for (int j = 2; j < 10; ++j)
{
for (int i = 1; i < 10; ++i)
{
cout << j << "*" << i << "=" << j * i << endl;
}
}
다음은 카운터에 해당하는 변수를 for문의 조건 쪽에서 선언하는 것이 아니라 밖에서 따로 선언하는 경우를 보여준다.
그리고 while에서 while (true)를 해주듯이 for문으로도 무한루프를 만들 수 있는데, for (;;++cnt) 이런 식으로 만들어주면 반복 조건이 true가 되면서 제약이 사라지게 되고 무한 루프에 빠지게 된다.
아래쪽 코드는 이중 for문을 활용해서 구구단을 만드는 예시이다. for문을 활용해서 해보는 예제 문제 중에 가장 대표적인 예제문제라고 볼 수 있다.
Chapter 5.8 break, continue
이번 챕터에서는 반복문에서 탈출할 수 있도록 만들어주는 break와, 특정 조건에서 반복문의 처음 지점으로 다시 돌아가게 해주는 기능을 가지고 있는 continue에 대해서 알아본다.
예제 코드 1
// break 예제 //
int count = 0;
while (true)
{
cout << count << endl;
if (count == 10)
{
break;
}
count++;
}
// continue 예제 //
for (int i = 0; i < 10; i++)
{
if (i % 2 == 0)
{
continue;
}
cout << i << endl;
}
break는 특정 조건을 만족할 때 반복문을 탈출할 수 있는 기능을 가지고 있다.
break 예제 코드에서, 현재 while (true)로 인해 무한 loop를 돌도록 만들어져 있는데, count가 10일 때 break를 선언하여 반복문에서 빠져나오게 된다.
continue는 특정 조건을 만족할 때 반복문 내 나머지 실행문을 생략하고, 다시 반복문의 초입으로 돌아가는 기능을 가지고 있다.
continue 예제 코드에서, i를 2로 나눴을 때 0인 경우는 짝수인 경우인데, 짝수인 경우는 continue를 걸어두어서 i가 홀수인 경우만 cout을 하도록 되어 있다.
예제 코드 2
// do while 문과 continue의 결합
int count = 0;
do
{
if (count == 5)
{
continue;
}
cout << count << endl;
} while (++count < 10);
// while 문에서 break를 사용하지 않고 break와 같은 효과를 내는 경우
bool escape_flag = false;
while (!escape_flag)
{
char ch;
cin >> ch;
cout << ch << " " << count++ << endl;
if (ch == 'x')
{
escape_flag = true;
}
}
do while은 앞에서 다룬 구문인데, while 문을 실행하지만 반드시 한 번은 실행해야 하는 경우에 사용한다.
while 문과는 다르게, while의 조건이 do의 끝쪽에 붙는 특징을 가지고 있다.
do 문에서 count가 5인 경우 continue 하도록 되어 있고, 그 외에는 count 변수를 cout 하도록 되어 있다.
while 에는 단순 조건뿐만 아니라 ++count를 넣어두어 count가 계속 증가하도록 만들어두었다.
따라서 해당 코드에서는 0부터 9까지는 cout을 하되, count가 5인 경우는 continue를 해서 cout을 하지 않는다.
아래쪽 코드는 break문을 사용하지 않고 break와 같은 효과를 내도록 만든 코드이다.
별도의 bool 자료형의 flag를 만든 다음, 만약 char 타입의 변수 ch가 x인 경우 escape_flag를 true로 만들어서 while 문이 더 이상 작동하지 않도록 만들어진 코드이다.
break문을 사용하지 않으려고 하다 보니, 별도의 bool 자료형의 변수가 하나 더 필요하고 이를 관리해야 된다는 점에서 비효율이 발생한다고 볼 수 있다.
flag를 사용하지 않고, while은 그냥 while (true)로 사용하면서 ch가 x일 때 break;를 하도록 코드를 작성하면 훨씬 편하게 동일한 기능을 구현할 수 있다.
Chapter 5.9 난수 만들기
이번 챕터에서는 C++에서 난수를 만드는 방법에 대해서 다룬다.
예제 코드 1
// 난수를 만드는 원리를 설명
unsigned int PRNG()
{
static unsigned int seed = 5523;
seed = 8253729 * seed + 2396403;
return seed % 32768;
}
for (int cnt = 1; cnt <= 100; ++cnt)
{
cout << PRNG() << "\t";
}
컴퓨터는 실제로 난수를 만들 수 있는 능력이 없다고 한다. 따라서 엄밀하게 따지면 난수를 만드는 것이 아니라, 난수처럼 보이는 숫자들을 연속적으로 계산해 내는 것이라고 한다.
PRNG 함수를 보면, 처음 정해진 seed 값에 임의의 값들을 더해주고 곱해준 다음, 이를 임의의 숫자로 나눈 값을 return 해주고 있다. seed가 static 변수이므로, seed 변수는 PRNG 함수가 호출될 때마다 계속 값이 변한다고 볼 수 있다.
신기하게도 100번을 돌리면 들쭉날쭉하게 숫자들이 생성되는데, 정말 난수처럼 보인다.
예제 코드 2
#include <cstdlib> // std::rand(), std::srand()
int getRandomNumber(int min, int max)
{
static const double fraction = 1.0 / (RAND_MAX + 1.0);
return min + static_cast<int>((max - min + 1) * (std::rand() * fraction));
}
for (int cnt = 1; cnt <= 100; ++cnt)
{
cout << getRandomNumber(5, 8); << "\t";
if (cnt % 5 == 0) cout << endl;
}
특정한 범위 안에서 난수를 만들 수 있을까? 예제 코드 2번은 이에 대해서 설명해 준다.
이 함수의 return은 크게 두 부분으로 나눠져 있다.
우선 min이 기본 값이 된다. 왜냐하면 최소한 min 값은 보장해야 하기 때문이다. 뒤 항이 0이 되는 경우를 생각하면 된다.
두 번째 항을 보면, 앞에 static_cast <int>가 보인다. 즉 뒤에 나오는 값을 int로 casting 할 예정이다.
내부를 보면 두 가지 term으로 나눠지는데, (max - min + 1) 과 (std::rand() * fraction)의 곱이다.
(max - min + 1)을 getRandomNumber(5, 8) 케이스에서 생각해 보면 (8 - 5 + 1)가 되며 값은 4가 된다.
std::rand()는 0부터 RAND_MAX까지의 값 중 하나가 나오게 된다.
그래서 std::rand() * fraction의 값은 0보다 크거나 같고, std::rand()가 RAND_MAX가 되는 경우, RAND_MAX * (1 / RAND_MAX + 1)이 되는 것이니 1보다 아주 약간 작은 값이 된다.
즉 뒤쪽 항인 std::rand() * fraction의 범위를 수식으로 표현하자면 0 <= std::rand() * fraction < 1가 된다.
여기에 (max - min + 1)을 곱해주면, 0 <= (max - min + 1) * (std::rand() * fraction) < 4가 된다.
이를 static_cast<int> 해주면, 내림으로 처리해야 하니 int 값 기준 0, 1, 2, 3만 가질 수 있다. 여기에 min만 더해주면 return 될 수 있는 정수는 5, 6, 7, 8이 된다.
예제 코드 3
#include <cstdlib> // std::rand(), std::srand()
#include <ctime> // std::time()
#include <random>
int main()
{
std::srand(5323); // seed를 설정해주는 역할
//std::srand(static_cast<unsigned int>(std::time(0))); // seed가 계속 변경되는 케이스
for (int cnt = 1; cnt <= 100; ++cnt)
{
cout << std::rand() << "\t"; // 난수 생성 케이스
cout << rand() % 4 + 5 << "\t"; // 5부터 8 사이의 난수 생성 케이스
}
}
std::srand는 난수 생성 시 seed를 고정해 주는 역할을 한다. 따라서 srand가 같은 값이면 계속 같은 값들이 난수로 생성되게 된다.
디버깅하는 경우는 시드가 계속 바뀌면 오히려 디버깅이 안되니, 고정해야 한다.
만약 시드를 고정하지 않고 계속 변경해서 난수가 생성되도록 하고 싶다면, seed를 현재 시간을 기준으로 정하게 하면 코드를 작동할 때마다 계속 다른 seed가 적용되도록 만들 수 있다.
std::rand()를 사용하면 난수를 생성할 수 있으며, 만약 특정 난수 생성 범위를 정해주고 싶다면 rand() % 4 + 5와 같은 방식을 통해 생성 범위를 만들 수 있다. 단, 나눠주는 숫자(현 케이스에서는 4)가 작으면 괜찮으나 큰 경우 난수가 생성될 때 특정 범위에 숫자가 몰리는 케이스가 생길 수 있다고 한다.
예제 코드 4
std::random_device rd;
std::mt19937 mersenne(rd()); // create a mersenne twister,
std::uniform_int_distribution<> dice(1, 6); // 1포함 6 이하
for (int count = 1; count <= 20; ++count)
{
cout << dice(mersenne) << endl;
}
해당 코드는 1) 랜덤 디바이스를 생성하고, 2) 랜덤 디바이스를 이용해서 생성기를 만들고, 3) 생성기가 어떤 분포를 따르는지 결정해서 원하는 분포를 따르는 숫자를 만드는 예시이다.
std::mt19937 mersenne는 매우 질이 좋은 난수를 빠르게 생성할 수 있도록 만들어진 유사난수 생성기라고 한다.
Chapter 5.10 std::cin 더 잘 쓰기
이번 챕터에서는 std::cin을 사용할 때, 다양한 안전장치를 통해서 사용자가 다양하게 입력을 줄 때 거기에 대응할 수 있는 코드를 사용하는 방법을 다룬다.
예제 코드 1
#include <iostream>
using namespace std;
int getInt()
{
while (1)
{
cout << "Enter a integer number : ";
int x;
cin >> x;
// 입력 스트림에 오류가 발생했을 때 true
if (std::cin.fail())
{
std::cin.clear(); // std::cin.fail()이 true가 되는 경우, 오류 플래그가 설정되어 있어 cin 사용 불가. clear()를 하면 오류 플래그 초기화.
std::cin.ignore(32767, '\n'); // cin은 공백을 기준으로 입력을 구분하고, 나머지 입력 버퍼에 남은 내용을 건너뛰게 해줌.
cout << "Invalid number, please try again" << endl;
}
else
{
std::cin.ignore(32767, '\n');
return x;
}
}
}
char getOperator()
{
while (1)
{
cout << "Enter an operator (+, -, *) :";
char op;
cin >> op;
std::cin.ignore(32767, '\n');
if (op == '+' || op == '-' || op == '*') return op;
else cout << "Invalid operator, please try again" << endl;
}
}
void printResult(int x, char op, int y)
{
switch (op)
{
case '+':
cout << x + y << endl;
break;
case '-':
cout << x - y << endl;
break;
case '*':
cout << x * y << endl;
break;
default:
cout << "Invalid operator" << endl;
}
}
int main()
{
int x = getInt();
char op = getOperator();
int y = getInt();
printResult(x, op, y);
}
입력을 받는 함수의 경우, while문을 활용해서 유효한 값이 입력될 때까지 계속 반복이 되도록 해주는 것이 중요하다.
그리고 std::cin.fail()을 이용해서 입력 스트림에 오류가 발생했는지 여부를 파악하고, 만약 오류가 발생했다면 std::cin.clear()를 통해 오류 플래그를 초기화시켜 다시 std::cin 기능이 정상적으로 작동할 수 있도록 설정한다.
해당 코드에서는 정수를 1개 받아야 하는데, 사용자가 "1 2" 와 같은 방식으로 입력하면, std::cin의 경우 공백을 기준으로 입력을 구분하기 때문에 1은 받되 공백 뒤에 있는 2는 입력 버퍼에 남겨두게 된다. 이러면 그다음 입력에 영향을 끼친다. 따라서 이러한 문제를 방지하고자 std::cin.ignore를 사용하여, 문장의 끝을 나타내는 '\n'이 나오는 경우 나머지 입력 버퍼에 남은 내용을 건너뛰게 만들어 문제 발생을 방지할 수 있다.
여기까지 해서 Chapter 5를 정리하였다.
Chapter 4를 정리한 게 벌써 2달 전인데, 여러 가지 일로 계속 미뤄지다 보니 글 작성이 많이 지연되었다.
워낙 강의가 꼼꼼하고 양이 많다 보니 완강하려면 열심히 봐야겠다..!
'C++ > 따라하며 배우는 C++' 카테고리의 다른 글
홍정모의 따라하며 배우는 C++ - Chapter 6(6.1 ~ 6.10, 전반부) (0) | 2025.05.25 |
---|---|
홍정모의 따라하며 배우는 C++ - Chapter 4 (0) | 2025.03.16 |
홍정모의 따라하며 배우는 C++ - Chapter 3 (0) | 2025.02.09 |
C++ 공부 관련 정리글을 업로드 해보려고 합니다. (0) | 2025.02.02 |