while과 do-while 반복문
while과 do-while의 조건 검사 시점을 비교하고 횟수를 모르는 반복과 입력 검증 흐름을 안전하게 작성합니다.
때로는 반복 횟수를 미리 알 수 없거나, 특정 조건이 만족될 때까지 계속 반복해야 하는 경우가 있습니다.
예를 들어 사용자가 올바른 값을 입력할 때까지 계속 입력받거나, 파일의 끝에 도달할 때까지 데이터를 읽는 것과 같은 상황이 있을 수 있습니다.
이런 경우에 적합한 반복문이 바로 while 문과 do-while 문입니다.
이 장에서는 이 두 반복문의 특징과 사용법을 다룹니다.
먼저 while과 do-while을 조건을 검사하는 시점이 다른 반복문으로 나눠 봅니다.
PRE-TEST · POST-TEST
둘 다 검사 결과가 true이면 반복하고 false이면 빠져나옵니다. 갈리는 지점은 첫 검사보다 본문이 앞에 있는지입니다.
while (조건) { 본문 }
본문 전에 검사하는 선검사 반복
-
조건 검사
조건 값을
bool로 문맥 변환합니다. 처음부터false면 본문을 건너뜁니다. -
true일 때 본문 실행본문의 끝에 도달하면 다음 조건 검사로 되돌아갑니다.
-
false면 종료while다음 문장으로 제어가 이어집니다.
최소 실행 횟수: 0회
do { 본문 } while (식);
본문 뒤에 검사하는 후검사 반복
-
본문 먼저 실행
진입할 때는 검사하지 않으므로 본문이 반드시 한 번 실행됩니다.
-
식 검사
본문 실행 뒤 식을
bool로 문맥 변환합니다. -
true면 반복,false면 종료반복하면 다시 본문부터 실행하고, 끝나면 다음 문장으로 이어집니다.
최소 실행 횟수: 1회
continue는 자신을 둘러싼 가장 안쪽 반복문의 반복 계속 지점으로 제어를 옮깁니다. 따라서 while에서는 다음 조건 검사로, 후검사 반복에서는 본문 뒤의 식 검사로 이어집니다. 이 장에서 다루는 문맥에서 break는 자신을 둘러싼 반복문 또는 switch 문 가운데 가장 안쪽 문을 끝냅니다.while 문: 조건이 참인 동안 반복
while 문은 주어진 조건이 참(true)인 동안 코드 블록을 반복적으로 실행합니다.
for 문과 달리 초기화 및 증감 부분이 명시적으로 분리되어 있어, 반복 횟수보다는 조건에 중점을 둔 반복에 더 적합합니다.
// (초기화는 보통 while 문 바깥에서 수행)
while (조건) {
// 조건이 true인 동안 반복적으로 실행할 코드
// 조건이 끝나도록 상태를 바꾸거나 명시적인 탈출 경로를 둠
}조건: 매 반복이 시작되기 전에 평가됩니다. 이조건이true이면 반복 몸체가 실행되고,false이면while문이 종료됩니다.{ }(중괄호): 조건이 참일 때 실행될 코드 블록입니다.while문은for문과 달리 초기화와 증감 부분이 문법에 따로 포함되어 있지 않습니다. 끝나야 하는 반복이라면 조건이 언젠가false가 되도록 상태를 갱신하거나, 도달 가능한break·return같은 탈출 경로를 두어야 합니다.
while 문의 실행 흐름
조건 부분이 평가됩니다.
만약 조건이 true이면, while 문의 반복 몸체({ } 안의 코드)가 실행됩니다.
반복 몸체 실행이 끝나면, 다시 1번 단계로 돌아가 조건 부분을 평가합니다.
조건이 false가 될 때까지 2, 3번 과정을 반복합니다.
조건이 false가 되면 while 문이 종료되고 다음 코드가 실행됩니다.
#include <iostream>
int main() {
int i = 1; // 초기화
while (i <= 5) { // 조건
std::cout << i << " ";
i++; // 증감
}
std::cout << std::endl; // 출력: 1 2 3 4 5
return 0;
}#include <iostream>
#include <limits>
int main() {
int age;
std::cout << "나이를 입력하세요 (1-120): ";
// 정수 추출에 성공하고 값이 유효한 범위에 들어올 때까지 반복
while (!(std::cin >> age) || age < 1 || age > 120) {
if (std::cin.eof()) {
std::cerr << "입력이 종료되었습니다." << std::endl;
return 1;
}
// 비정수 입력으로 설정된 오류 상태를 풀고 현재 입력 줄을 버림
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cout << "잘못된 나이입니다. 1에서 120 사이의 숫자를 다시 입력하세요: ";
}
std::cout << "입력하신 나이: " << age << std::endl; // 출력: 입력하신 나이: (사용자가 입력한 유효한 나이)
return 0;
}while (true)의 조건 자체는 항상 참이므로 조건 검사만으로는 루프가 끝나지 않습니다.
이 경우, break 문을 사용하여 특정 조건이 만족되면 루프를 강제로 종료할 수 있습니다.
#include <iostream>
#include <string>
int main() {
std::string command;
std::cout << "명령을 입력하세요 ('exit'을 입력하면 종료):" << std::endl;
while (true) { // 무한 루프
std::cout << "> ";
std::cin >> command;
if (command == "hello") {
std::cout << "안녕하세요!" << std::endl;
} else if (command == "exit") {
std::cout << "프로그램을 종료합니다." << std::endl;
break; // 'exit'이 입력되면 루프 탈출
} else {
std::cout << "알 수 없는 명령입니다." << std::endl;
}
}
return 0;
}do-while 문: 최소 한 번은 실행되는 반복
do-while 문은 while 문과 유사하지만, 조건을 반복 몸체 실행 후에 평가한다는 점에서 차이가 있습니다.
이로 인해 do-while 문의 반복 몸체는 조건의 참/거짓 여부와 관계없이 최소 한 번은 반드시 실행됩니다.
do {
// 최소 한 번은 실행될 코드
// 끝나야 하는 반복이라면 상태 갱신 또는 명시적인 탈출 경로를 둠
} while (조건); // 조건 뒤에 세미콜론(;) 필수!do블록 안의 코드가 먼저 실행됩니다.- 그 후에
while괄호 안의조건이 평가됩니다. 조건이true이면do블록으로 돌아가 다시 실행하고,false이면 반복문이 종료됩니다.while (조건);뒤에 반드시 세미콜론(;)을 붙여야 합니다.
do-while 문의 실행 흐름
do 블록의 반복 몸체({ } 안의 코드)가 실행됩니다.
조건 부분이 평가됩니다.
만약 조건이 true이면, 1번 단계로 돌아가 반복 몸체를 다시 실행합니다.
조건이 false가 되면 do-while 문이 종료되고 다음 코드가 실행됩니다.
#include <iostream>
#include <limits>
int main() {
int num;
bool valid = false;
// do-while은 최소 한 번은 실행되므로, 첫 입력 유도 메시지를 루프 안에 넣기 편리
do {
std::cout << "0에서 9 사이의 숫자를 입력하세요: ";
if (std::cin >> num) {
valid = (num >= 0 && num <= 9);
} else {
if (std::cin.eof()) {
std::cerr << "입력이 종료되었습니다." << std::endl;
return 1;
}
std::cin.clear();
valid = false;
}
// 성공 여부와 관계없이 현재 입력 줄의 나머지를 버림
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
} while (!valid); // 정수 추출에 성공하고 값이 유효할 때까지 반복
std::cout << "입력하신 숫자: " << num << std::endl;
return 0;
}만약 사용자가 첫 입력으로 5를 입력하면 valid가 true가 되고, 반복 조건 !valid가 false로 평가되어 루프가 종료됩니다.
사용자가 첫 입력으로 10을 입력하면 valid가 false이므로 !valid가 true로 평가되어 다시 입력을 요청하게 됩니다.
#include <iostream>
int main() {
int choice;
do {
std::cout << "\n--- 메뉴 ---" << std::endl;
std::cout << "1. 게임 시작" << std::endl;
std::cout << "2. 설정" << std::endl;
std::cout << "3. 종료" << std::endl;
std::cout << "선택: ";
std::cin >> choice;
switch (choice) {
case 1:
std::cout << "게임을 시작합니다!" << std::endl;
break;
case 2:
std::cout << "설정 화면으로 이동합니다." << std::endl;
break;
case 3:
std::cout << "게임을 종료합니다. 안녕히 가세요!" << std::endl;
break;
default:
std::cout << "잘못된 선택입니다. 다시 입력해주세요." << std::endl;
break;
}
} while (choice != 3); // choice가 3이 아니면 계속 반복
return 0;
}이 예시에서 메뉴는 최소 한 번은 출력되고 사용자의 입력을 받습니다.
사용자가 3을 입력하기 전까지 계속 메뉴가 반복됩니다.
for vs while vs do-while 비교
반복문을 고를 때는 반복 횟수가 정해져 있는지, 조건을 실행 전에 검사해야 하는지, 본문을 최소 한 번 실행해야 하는지를 먼저 가르면 됩니다.
| 특징 | for 문 | while 문 | do-while 문 |
|---|---|---|---|
| 초기화, 조건, 증감 | 한 줄에 명시적으로 표현 | 초기화는 외부, 증감은 내부에서 수동 관리 | 초기화는 외부, 증감은 내부에서 수동 관리 |
| 반복 실행 조건 | 루프 시작 전 조건 검사 | 루프 시작 전 조건 검사 | 루프 실행 후 조건 검사 |
| 최소 실행 횟수 | 0번 (조건이 처음부터 false이면) | 0번 (조건이 처음부터 false이면) | 1번 (조건과 관계없이) |
| 적합한 상황 | 반복 횟수가 명확할 때 (예: 배열 순회), 규칙적인 증감이 필요할 때 | 조건이 만족될 때까지 반복할 때 (예: 파일 읽기, 사용자 입력) | 최소 한 번은 실행해야 할 때 (예: 메뉴 시스템, 유효성 검사) |
| 주의사항 | 끝나야 한다면 조건·반복식·탈출 경로 확인 | 끝나야 한다면 상태 갱신 또는 도달 가능한 탈출 경로 확인 | while 뒤에 세미콜론(;) 필수, 상태 갱신 또는 도달 가능한 탈출 경로 확인 |
무한 루프 (Infinite Loops)
반복문의 조건이 항상 true로 평가되도록 작성되면 무한 루프(Infinite Loop)가 됩니다.
이는 프로그램이 끝나지 않고 계속 실행되는 상태를 만듭니다.
의도적인 경우가 아니면 버그로 이어집니다.
일반적인 무한 루프의 원인while문에서 조건을 변화시키는 코드가 없거나 잘못된 경우.for문에서 조건이 항상true인 경우 (예:for (;;)).
int counter = 0;
while (counter < 5) { // counter는 항상 0이므로 조건이 항상 true
std::cout << "반복 중..." << std::endl;
// counter++; // 이 증감 코드가 없어서 무한 루프!
}조건으로 끝내지 않는 반복은 while (true)나 for (;;)처럼 의도를 드러낼 수 있습니다. 내부에서 끝나야 하는 작업이라면 도달 가능한 break나 return 같은 별도 종료 경로가 필요하고, 계속 실행하도록 설계한 작업이라면 외부 중단 방식과 수명을 함께 정해야 합니다.
무한 루프는 조건식 자체보다 조건을 바꾸는 코드가 실제로 실행되는지에서 자주 갈립니다.
검사 시점, 갱신 위치, 탈출 경로를 함께 보면 while과 do-while의 차이도 더 선명해집니다.
PROGRESS · BACK EDGE · EXIT
조건식을 한 번 읽는 것만으로는 종료를 보장할 수 없습니다. 조건으로 되돌아가는 모든 경로에서 다음 검사 결과가 달라질 수 있는지 확인합니다.
갱신을 건너뛸 수 있음
int counter = 0;
while (counter < 5) {
if (skip()) continue;
work();
++counter;
}
skip()이 계속 참이면 continue가 ++counter를 건너뛰어 조건은 계속 참입니다.
모든 정상 경로가 진행
int counter = 0;
while (counter < 5) {
if (!skip()) work();
++counter;
}
일반 경로와 작업을 건너뛰는 경로가 모두 갱신을 지난 뒤 다음 조건 검사로 돌아갑니다.
-
검사 시점을 표시한다
while과for는 본문 전,do-while은 본문 후에 검사합니다. 어떤 값이 다음 결과를 결정하는지 적습니다. -
되돌아가는 경로를 모두 추적한다
본문의 정상 끝뿐 아니라 조건문, 중첩 블록,
continue가 필요한 갱신을 우회하지 않는지 확인합니다. -
탈출 경로의 범위와 도달성을 확인한다
조건의
false는 반복을 끝냅니다. 이 장에서 다루는 문맥에서break는 자신을 둘러싼 반복문 또는switch문 가운데 가장 안쪽 문만 끝내므로, 중첩 구조에서는 어느 문을 대상으로 하는지 따로 봅니다.
while (true)나 for (;;)는 조건으로 종료하지 않겠다는 제어 형태입니다. 내부에서 끝나야 하는 작업이라면 도달 가능한 break, return 같은 별도 종료 계약을 명시하고, 계속 실행하도록 설계한 작업이라면 그 수명과 외부 중단 방식을 문서화합니다.예시 코드에서는 counter++가 조건을 바꾸는 갱신 지점입니다.
이 줄이 빠졌거나 continue 같은 분기가 갱신을 건너뛰어 조건이 계속 참으로 남는 상황은 초기 상태, 모든 되돌아가는 경로의 갱신 위치, 탈출 조건을 함께 확인하면 빠르게 찾을 수 있습니다.