for 반복문
for문의 초기화·조건·증감·본문이 순환하는 과정을 익히고 중첩 반복과 범위 기반 반복을 적절히 사용합니다.
1부터 100까지 더하거나, 배열의 모든 원소(element)를 출력하거나, 사용자가 종료를 입력할 때까지 입력을 받는 일은 같은 문장을 여러 번 실행해야 합니다. 이런 일을 하는 문장을 반복문(loop statement)이라고 하며, C++에는 for, while, do-while의 세 가지가 있습니다.
이 글은 그중 반복 변수의 시작, 계속 조건, 다음 값으로 넘어가는 규칙을 한 줄에 모아 쓰는 for 문을 다룹니다. 반복을 따라가려면 변수 선언과 초기화에서 본 값의 변경과 관계 연산자와 논리 연산자의 비교식을 함께 읽어야 합니다. 뒤에서는 C++11의 범위 기반 for로 배열과 컨테이너의 원소를 차례로 다루는 방법도 봅니다.
for 문의 네 부분
for 문은 괄호 안의 세 부분과 본문으로 이루어집니다. 세 부분은 세미콜론 두 개로 나뉘는데, 한 번만 실행되는 부분과 반복마다 실행되는 부분이 섞여 있어서 적힌 순서와 실행되는 순서가 다릅니다.
for (초기화; 조건; 반복식) {
// 조건이 true인 동안 되풀이할 본문
}초기화는 for 문에 들어설 때 단 한 번 실행되며, 보통 int i = 1처럼 반복 변수를 선언합니다. 여기서 선언한 변수는 조건, 반복식, 본문에서 쓸 수 있고 for 문이 끝나면 범위를 벗어납니다. 조건은 본문을 실행하기 전마다 검사되어, 참이면 본문을 실행하고 거짓이면 for 문을 끝냅니다.
반복식은 본문이 끝난 뒤, 다음 조건 검사 전에 실행됩니다. 보통 ++i나 i += 2처럼 반복 변수를 바꾸어 다음 검사에 쓸 값을 만듭니다. 본문 안에서 break나 return으로 루프를 빠져나가면 반복식은 실행되지 않고, break와 continue에서 볼 continue는 본문의 나머지를 건너뛰고 반복식으로 갑니다.
실행 순서 따라가기
다음 프로그램은 1부터 5까지 출력합니다. 숫자마다 뒤에 공백을 붙여 한 줄에 출력하고, 줄바꿈은 루프가 끝난 뒤에 한 번만 합니다. 이 짧은 루프에서 네 부분이 어떤 순서로 몇 번씩 실행되는지를 출력 뒤에서 따라갑니다.
#include <iostream>
int main() {
for (int i = 1; i <= 5; ++i) {
std::cout << i << ' ';
}
std::cout << '\n';
}1 2 3 4 5아래 그림처럼 초기화로 i가 1이 된 뒤에는 조건, 본문, 반복식, 다시 조건의 순서가 되풀이됩니다. i가 5일 때 본문을 실행하고 반복식으로 6이 되면, 조건 6 <= 5가 거짓이어서 본문과 반복식을 건너뛰고 루프 다음 문장으로 갑니다. 그래서 조건은 참인 다섯 번과 끝을 확인하는 거짓 한 번을 합쳐 모두 여섯 번 검사됩니다.
for는 초기화 한 번 뒤 조건으로 되돌아간다
i=1 초기화, i<=5 검사, 출력, 증가, 재검사와 i=6 종료라는 실제 반복 상태를 보여 줄 수 있다.
INIT ONCE · TEST BEFORE EACH PASS
for는 초기화 뒤 조건·본문·반복식을 순환한다
for (초기화; 조건; 반복식)은 왼쪽부터 한 번씩 끝내는 목록이 아닙니다. 초기화만 한 번 실행하고, 이후에는 조건 → 본문 → 반복식 → 조건을 순환합니다.
-
초기화는 진입할 때 한 번
선언 또는 표현식으로 시작 상태를 만듭니다. 여기서 선언한 변수는 조건, 반복식, 본문에서 사용할 수 있고
for문을 벗어나면 범위를 벗어납니다. -
조건은 본문보다 먼저 검사
true이면 본문으로,false이면 루프 다음 문장으로 갑니다. 조건을 생략한for (;;)는 이 검사가true인 것과 같습니다. -
본문이 한 번의 작업을 수행
본문이 정상적으로 끝나면 반복식으로 갑니다. 중괄호를 쓰면 한 반복의 경계와 제어 이전 지점을 더 분명하게 읽을 수 있습니다.
-
반복식 뒤 조건을 다시 검사
++i나i += 2처럼 다음 상태를 만든 뒤 2단계로 돌아갑니다. 반복식을 생략했다면 필요한 상태 변화는 다른 곳에서 일어나야 합니다.
본문 예시 · 1부터 5까지 출력
for (int i = 1; i <= 5; ++i) {
print(i);
}i = 1, 2, 3, 4, 5: 본문 실행 → ++i → 재검사
i가 6이 되면 조건이 false이므로 본문과 반복식 모두 실행하지 않고 종료합니다.
정상 완료
본문 → 반복식 → 조건 순서로 진행합니다.
현재 for의 continue
이 for를 직접 대상으로 하면 본문의 나머지를 건너뛰고 반복식을 실행한 뒤 조건을 다시 검사합니다.
현재 for의 break
더 안쪽 반복문이나 switch가 없어 이 for를 직접 대상으로 하면 반복식과 다음 조건 검사를 건너뛰고 루프 다음으로 이동합니다.
switch 안의 점프문은 가장 가까운 해당 구조를 대상으로 합니다. 반복 횟수는 시작값만으로 정해지지 않으므로 조건의 경계·방향과 반복식의 변화량, return·예외 경로까지 함께 확인합니다.이 예의 조건
- 본문과 원 도표에 제시된 사례의 입력·조건을 사용합니다. 설명용 수치나 독립 비교 경로는 각 단계에 표시합니다.
- 현재 상태·값과 전환 근거의 위치는 모든 단계에서 같습니다. 조건이 맞지 않거나 실행되지 않은 경로를 성공한 동작으로 해석하지 않습니다.
| 관찰·상황 | 값·보충 정보 | 변화·이유 |
|---|---|---|
| 초기·첫 회차 i=1에서는 i<=5가 참이어서 출력한다. |
|
|
| 갱신과 반복 출력 뒤 증가하고 조건을 다시 확인한다. |
|
|
| 종료 검사 i=6에서는 i<=5가 거짓이어서 종료한다. |
|
|
for는 초기화 한 번 뒤 조건으로 되돌아간다
i=1 초기화, i<=5 검사, 출력, 증가, 재검사와 i=6 종료라는 실제 반복 상태를 보여 줄 수 있다.
INIT ONCE · TEST BEFORE EACH PASS
for는 초기화 뒤 조건·본문·반복식을 순환한다
for (초기화; 조건; 반복식)은 왼쪽부터 한 번씩 끝내는 목록이 아닙니다. 초기화만 한 번 실행하고, 이후에는 조건 → 본문 → 반복식 → 조건을 순환합니다.
-
초기화는 진입할 때 한 번
선언 또는 표현식으로 시작 상태를 만듭니다. 여기서 선언한 변수는 조건, 반복식, 본문에서 사용할 수 있고
for문을 벗어나면 범위를 벗어납니다. -
조건은 본문보다 먼저 검사
true이면 본문으로,false이면 루프 다음 문장으로 갑니다. 조건을 생략한for (;;)는 이 검사가true인 것과 같습니다. -
본문이 한 번의 작업을 수행
본문이 정상적으로 끝나면 반복식으로 갑니다. 중괄호를 쓰면 한 반복의 경계와 제어 이전 지점을 더 분명하게 읽을 수 있습니다.
-
반복식 뒤 조건을 다시 검사
++i나i += 2처럼 다음 상태를 만든 뒤 2단계로 돌아갑니다. 반복식을 생략했다면 필요한 상태 변화는 다른 곳에서 일어나야 합니다.
본문 예시 · 1부터 5까지 출력
for (int i = 1; i <= 5; ++i) {
print(i);
}i = 1, 2, 3, 4, 5: 본문 실행 → ++i → 재검사
i가 6이 되면 조건이 false이므로 본문과 반복식 모두 실행하지 않고 종료합니다.
정상 완료
본문 → 반복식 → 조건 순서로 진행합니다.
현재 for의 continue
이 for를 직접 대상으로 하면 본문의 나머지를 건너뛰고 반복식을 실행한 뒤 조건을 다시 검사합니다.
현재 for의 break
더 안쪽 반복문이나 switch가 없어 이 for를 직접 대상으로 하면 반복식과 다음 조건 검사를 건너뛰고 루프 다음으로 이동합니다.
switch 안의 점프문은 가장 가까운 해당 구조를 대상으로 합니다. 반복 횟수는 시작값만으로 정해지지 않으므로 조건의 경계·방향과 반복식의 변화량, return·예외 경로까지 함께 확인합니다.이 예의 조건
- 본문과 원 도표에 제시된 사례의 입력·조건을 사용합니다. 설명용 수치나 독립 비교 경로는 각 단계에 표시합니다.
- 현재 상태·값과 전환 근거의 위치는 모든 단계에서 같습니다. 조건이 맞지 않거나 실행되지 않은 경로를 성공한 동작으로 해석하지 않습니다.
| 관찰·상황 | 값·보충 정보 | 변화·이유 |
|---|---|---|
| 초기·첫 회차 i=1에서는 i<=5가 참이어서 출력한다. |
|
|
| 갱신과 반복 출력 뒤 증가하고 조건을 다시 확인한다. |
|
|
| 종료 검사 i=6에서는 i<=5가 거짓이어서 종료한다. |
|
|
반복이 길어지면 회차마다 조건에서 읽은 값, 본문이 바꾼 값, 반복식 뒤의 값을 표로 적으면 헷갈리지 않습니다. 다음 프로그램은 본문을 실행한 횟수와 누적 합을 따로 셉니다.
#include <iostream>
int main() {
int count{};
int sum{};
for (int i{1}; i <= 4; ++i) {
++count;
sum += i;
}
std::cout << count << ' ' << sum << '\n';
}조건에서 읽은 i | 본문 뒤 count | 본문 뒤 sum | 반복식 뒤 i |
|---|---|---|---|
| 1 | 1 | 1 | 2 |
| 2 | 2 | 3 | 3 |
| 3 | 3 | 6 | 4 |
| 4 | 4 | 10 | 5 |
다섯 번째 검사 5 <= 4는 거짓이어서 본문을 실행하지 않으므로, 출력은 4 10입니다. i는 이 for에서 선언했으므로, 루프가 끝난 뒤의 출력문에서는 쓸 수 없습니다.
진행 규칙 바꾸기
반복식은 1씩 늘리는 것만이 아닙니다. 간격을 바꾸거나 거꾸로 셀 수도 있습니다.
#include <iostream>
int main() {
for (int i = 2; i <= 10; i += 2) {
std::cout << i << ' ';
}
std::cout << '\n';
for (int i = 5; i >= 1; --i) {
std::cout << i << ' ';
}
std::cout << '\n';
}2 4 6 8 10
5 4 3 2 1반복 횟수는 시작값, 비교 연산자, 끝값, 변화량이 함께 정합니다. for (int i = 0; i < n; ++i)는 0부터 n-1까지 n번, i <= n으로 쓰면 n+1번 실행되므로, 한 번 더 돌거나 덜 도는 오프 바이 원(off-by-one) 오류는 대부분 이 넷이 서로 맞지 않아서 생깁니다. 배열의 인덱스처럼 0부터 세는 값에는 i < n 형태가 잘 맞습니다.
거꾸로 셀 때 반복 변수를 unsigned로 두면 끝나지 않는 루프가 되기 쉽습니다. for (unsigned i = 3; i >= 0; --i)에서 unsigned 값은 언제나 0 이상이므로 조건이 항상 참이고, i가 0일 때 --i는 산술 연산자에서 본 대로 가장 큰 값으로 돌아갑니다. GCC는 -Wextra에 포함된 -Wtype-limits로 항상 참인 이런 비교를 경고합니다. 거꾸로 셀 때는 int를 쓰거나, for (unsigned i = 4; i-- > 0; )처럼 검사한 뒤에 줄이는 형태를 씁니다.
반복 변수로 실수를 쓰는 것도 피합니다. 기본 데이터 타입에서 본 것처럼 0.1은 이진 실수로 정확히 표현되지 않으므로, for (double x = 0.0; x != 1.0; x += 0.1)의 x는 1.0과 정확히 같아지지 않아 루프가 끝나지 않을 수 있습니다. 반복 횟수는 정수로 세고, 필요한 실수 값은 x = i * 0.1처럼 본문에서 계산합니다.
여러 변수와 생략
초기화에는 같은 타입의 변수를 쉼표로 여러 개 선언할 수 있고, 반복식에는 쉼표 연산자로 두 식을 차례로 쓸 수 있습니다. 같은 쉼표이지만 초기화의 쉼표는 선언을 나누는 구분자이고, 반복식의 쉼표는 기타 연산자에서 본 쉼표 연산자입니다. 아래 코드에서 i는 0부터 늘고 j는 10부터 줄며, 반복을 끝낼지는 조건 i < 3 하나만 정합니다.
#include <iostream>
int main() {
for (int i = 0, j = 10; i < 3; ++i, --j) {
std::cout << i << ' ' << j << '\n';
}
}0 10
1 9
2 8세 부분은 필요에 따라 비워 둘 수 있지만 두 세미콜론은 남겨야 합니다. 반복 변수를 루프 앞에서 이미 만들었다면 초기화를 비우고, 본문에서 변수를 바꾼다면 반복식을 비웁니다. 조건을 비운 for (;;)는 조건이 항상 참인 것과 같아서, 본문 안의 break나 return으로만 빠져나갈 수 있습니다.
#include <iostream>
int main() {
int tries{0};
for (;;) {
++tries;
std::cout << "try " << tries << '\n';
if (tries >= 3) {
break;
}
}
}try 1
try 2
try 3tries가 3이 되면 break로 루프를 빠져나옵니다. 조건 없는 루프는 끝내는 조건이 본문 속에 숨기 쉬우므로, 이렇게 빠져나가는 지점을 한눈에 보이는 곳에 둡니다.
중첩 for
for 문의 본문에 다시 for 문을 둘 수 있습니다. 바깥 루프가 한 번 돌 때마다 안쪽 루프는 초기화부터 다시 시작합니다.
#include <iostream>
int main() {
for (int i = 2; i <= 4; ++i) {
for (int j = 1; j <= 3; ++j) {
std::cout << i * j << (j < 3 ? ' ' : '\n');
}
}
for (int row = 0; row < 4; ++row) {
for (int col = 0; col <= row; ++col) {
std::cout << '*';
}
std::cout << '\n';
}
}2 4 6
3 6 9
4 8 12
*
**
***
****첫 중첩 루프에서 바깥의 i는 2, 3, 4의 세 값을 갖고, 그때마다 안쪽의 j가 1부터 3까지 돌므로 안쪽 본문은 3×3인 9번 실행됩니다. 둘째 루프는 안쪽 조건이 col <= row여서 줄마다 별의 개수가 1, 2, 3, 4로 늘어나며, 안쪽 본문은 모두 10번 실행됩니다. 안쪽의 횟수가 바깥 변수에 따라 달라지면 곱셈 대신 회차마다의 횟수를 더해야 합니다.
범위 기반 for
배열이나 컨테이너의 모든 원소를 처음부터 끝까지 차례로 다룰 때는 C++11의 범위 기반 for(range-based for)가 간결합니다. 인덱스를 직접 만들지 않으므로 끝값을 잘못 정하는 실수가 줄어듭니다. 배열은 1차원 배열에서, std::vector는 그 뒤의 컨테이너에서 자세히 다루며, 여기서는 원소를 복사로 받는지 참조로 받는지만 구별합니다.
#include <iostream>
#include <string>
#include <vector>
int main() {
int numbers[]{10, 20, 30};
for (int n : numbers) { n += 100; }
for (int& n : numbers) { n += 1; }
for (int n : numbers) { std::cout << n << ' '; }
std::cout << '\n';
std::vector<std::string> fruits{"apple", "banana", "cherry"};
for (const auto& fruit : fruits) {
std::cout << fruit.size() << ' ';
}
std::cout << '\n';
}11 21 31
5 6 6첫 루프의 int n은 원소를 복사한 새 변수여서 100을 더해도 배열은 바뀌지 않습니다. 둘째 루프의 int& n은 원본 원소를 가리키는 참조여서 1을 더한 결과가 배열에 남고, 셋째 루프가 11 21 31을 출력합니다. 문자열처럼 복사 비용이 있는 원소를 읽기만 할 때는 const auto&로 받아 복사 없이 읽으면서 실수로 바꾸는 일도 막습니다.
아래 그림은 일반 for와 범위 기반 for를 고르는 기준과, 원소(그림에서는 요소)를 받는 세 방식을 비교합니다. 인덱스가 필요하거나, 일부 구간만 돌거나, 거꾸로 또는 몇 칸씩 건너뛰어야 하면 일반 for가 알맞습니다. 그림의 설명대로 std::vector<bool>처럼 원소 대신 프록시 객체를 돌려주는 범위에서는 auto로 받아도 원본이 바뀔 수 있으므로, 이 비교는 배열처럼 실제 원소의 참조를 돌려주는 범위를 기준으로 합니다.
CONTROL SHAPE · ELEMENT BINDING
순회 목적을 고른 뒤 요소의 복사·참조를 정한다
먼저 위치와 진행 규칙을 직접 제어할지, 모든 요소를 차례로 받을지 고릅니다. 아래 바인딩 비교는 배열처럼 역참조 결과가 실제 요소 참조인 일반 값 컨테이너를 기준으로 합니다.
| 선택 | 알맞은 상황 | 직접 제어하는 것 | 주의할 의미 |
|---|---|---|---|
일반 for |
인덱스, 두 변수, 부분 범위, 역순, 간격이 문제의 의미일 때 | 초기 상태, 계속 조건, 반복마다의 상태 변화 | 경계와 변화 방향이 맞아야 하며, 컨테이너 인덱스는 유효 범위를 벗어나지 않아야 합니다. |
range-for + auto |
실제 요소 참조를 반환하는 범위에서 각 요소 값을 받을 때 | 범위만 제시하며 순회 위치는 구현이 관리 | 일반 값 컨테이너에서는 새 값 변수를 바꿔도 원본 요소가 바뀌지 않습니다. 프록시 참조 범위는 예외일 수 있습니다. |
range-for + const auto& |
일반 값 컨테이너의 실제 요소를 복사 없이 읽을 때 | 읽기 전용 참조로 현재 요소에 접근 | 큰 객체의 불필요한 복사를 피하면서 루프에서 원본을 수정하지 못하게 합니다. |
range-for + auto& |
수정 가능한 실제 요소 참조를 반환하는 범위를 순회할 때 | 수정 가능한 참조로 현재 요소에 접근 | 변경이 컨테이너에 남습니다. 순회 중 컨테이너 자체를 무효화할 수 있는 변경은 별도로 검토합니다. |
- 일반
for - 인덱스·부분 범위·역순·간격이 필요할 때 씁니다. 시작, 경계, 변화 방향을 함께 검증합니다.
- range-for +
auto - 일반 값 컨테이너에서는 새 값 변수를 받으므로 바꿔도 원본은 바뀌지 않습니다. 프록시 참조 범위는 예외일 수 있습니다.
- range-for +
constauto& - 일반 값 컨테이너의 실제 요소를 복사 없이 읽고, 루프에서 그 참조를 통한 수정은 허용하지 않습니다.
- range-for +
auto& - 수정 가능한 실제 요소 참조를 받아 제자리에서 바꿉니다. 순회를 무효화하는 컨테이너 변경은 피합니다.
std::vector<bool>처럼 프록시 참조를 쓰는 범위에서는 auto의 복사·수정 효과가 달라질 수 있어 반복자의 참조 타입을 확인합니다.범위 기반 for는 범위를 나타내는 식을 처음에 한 번 평가하고, 그 범위의 시작과 끝을 얻어 순회합니다. 그래서 순회하는 도중에 std::vector에 원소를 추가하는 것처럼 범위 자체를 바꾸는 코드는 잘못된 위치를 가리키게 될 수 있습니다. 순회 중에 컨테이너의 크기를 바꿔야 한다면 따로 검토해야 합니다.
더 깊이: 범위 기반 for를 펼친 모양과 임시 객체의 수명
표준은 범위 기반 for를 일반 for로 펼친 모양으로 정의합니다. 범위를 나타내는 식은 auto&& __range = 범위식;처럼 이름 없는 참조에 한 번 묶이고, begin과 end로 얻은 두 반복자를 __begin != __end인 동안 비교하며 ++__begin으로 나아갑니다. 클래스에 begin과 end 멤버가 있으면 그것을, 없으면 인자 의존 조회(ADL)로 찾은 begin()과 end()를 씁니다. C++17부터는 __begin과 __end의 타입이 달라도 되어, 끝을 반복자 대신 '널 문자를 만났는가' 같은 조건으로 나타낼 수도 있습니다.
이 펼친 모양에서 임시 객체의 함정이 생깁니다. 범위 식이 돌려준 임시 객체는 __range에 묶여 루프가 끝날 때까지 살지만, C++23 이전에는 범위 식 안의 다른 임시 객체가 그 식이 끝날 때 사라졌습니다. 그래서 foo()가 값을 돌려줄 때 for (auto& x : foo().items())는 foo()가 만든 임시 객체가 루프를 시작하기도 전에 사라지고, items()가 돌려준 참조로 사라진 객체를 순회하는 정의되지 않은 동작이었습니다.
C++23은 P2718R0으로 범위 식 안의 임시 객체도 루프가 끝날 때까지 살도록 바꾸었고, GCC는 15에서 이를 구현했습니다. 하지만 이 교재가 기준으로 삼는 C++20에서는 여전히 위험하므로, C++20의 초기화 문을 써서 for (auto thing = foo(); auto& x : thing.items())처럼 임시 객체에 이름을 붙여 두는 편이 안전합니다. 자세한 규칙은 cppreference의 범위 기반 for에 있습니다.
정리
for는 초기화를 한 번 실행한 뒤 조건, 본문, 반복식, 다시 조건의 순서를 되풀이하고, 조건이 거짓이면 본문과 반복식을 건너뛰고 끝납니다.- 초기화에서 선언한 변수는
for문 안에서만 쓸 수 있습니다. - 반복 횟수는 시작값, 비교 연산자, 끝값, 변화량이 함께 정하며, 0부터 셀 때는
i < n형태가 잘 맞습니다. - 거꾸로 셀 때
unsigned와i >= 0을 함께 쓰면 끝나지 않으며, 반복 변수로 실수를 쓰지 않습니다. - 중첩 루프에서 안쪽 루프는 바깥 회차마다 다시 시작하므로, 안쪽 본문의 실행 횟수는 회차별 횟수의 합입니다.
- 범위 기반
for에서auto는 복사,auto&는 원본 수정,const auto&는 복사 없는 읽기입니다.
연습 문제
1. 조건과 반복식 바꾸기
count_and_sum.cpp에서 조건만 i < 4로 바꾸면 출력은 무엇인가요? 원래 조건으로 되돌리고 반복식만 i += 2로 바꾸면 무엇인가요?
해설 보기
i < 4이면 1, 2, 3에서만 본문을 실행하므로 3 6입니다. i += 2이면 i가 1, 3일 때 본문을 실행하고 5에서 조건이 거짓이 되므로 2 4입니다. 시작값과 끝값의 차이만 보지 말고, 조건을 만족한 값을 차례로 적어 보면 횟수가 정확해집니다.
2. 별의 개수
nested_for.cpp의 둘째 루프에서 바깥 조건을 row < 6으로 바꾸면 별은 모두 몇 개 출력되나요? 안쪽 조건을 col < row로 바꾸면 첫 줄은 어떻게 되나요?
해설 보기
row가 0부터 5까지 여섯 줄이 되고 줄마다 row + 1개를 출력하므로, 별은 1 + 2 + 3 + 4 + 5 + 6 = 21개입니다. 안쪽 조건을 col < row로 바꾸면 row가 0인 첫 줄에서는 0 < 0이 거짓이어서 안쪽 본문이 한 번도 실행되지 않고, 빈 줄만 출력됩니다.
3. 복사와 참조
int values[]{1, 2, 3}; 다음에 for (auto v : values) { v *= 2; }를 실행하고 배열을 출력하면 무엇이 나오나요? auto를 auto&로 바꾸면 어떻게 되나요?
해설 보기
auto v는 원소의 복사본이어서 복사본만 두 배가 되고, 배열은 1 2 3 그대로입니다. auto& v로 바꾸면 v가 각 원소를 가리키는 참조가 되어 배열이 2 4 6으로 바뀝니다.
다음 글 while과 do-while 반복문에서는 반복 횟수를 미리 알 수 없을 때, 조건만으로 반복을 이어 가는 while과 본문을 먼저 한 번 실행하는 do-while을 다룹니다.