왜 for 반복문인가
switch는 하나의 분기를 골라 한 번 실행합니다. 하지만 실제 프로그램은 무언가를 반복해서 해야 합니다. 모든 점수를 출력하거나, 숫자 목록을 합산하거나, 격자의 10행을 그리는 식입니다. for 반복문은 정해진 횟수만큼 코드를 반복하는 C++의 일꾼이며, 직접 제어하는 내장 카운터를 가지고 있습니다.
for 반복문에 필요한 모든 것이 하나의 간결한 헤더에 담겨 있어서, "몇 번, 어떻게"라는 전체 이야기를 한눈에 볼 수 있습니다.
세 부분으로 된 헤더
for 반복문의 헤더에는 세미콜론으로 구분된 세 부분이 있습니다. 초기화자, 조건, 갱신입니다.
for (initializer; condition; update) {
// 본문 - 조건이 참인 동안 실행됨
}
이들은 정해진 순서로 실행됩니다. 초기화자는 시작할 때 한 번 실행됩니다. 그다음 조건은 매 반복 전에 검사됩니다. 본문은 조건이 true일 때만 실행됩니다. 그리고 갱신은 매 반복의 끝, 조건이 다시 검사되기 직전에 실행됩니다.
여기서 int i = 0은 한 번 실행됩니다. 그다음 i < 5가 검사되어, 성립하는 동안 본문이 출력하고 i++가 카운터를 올립니다. i가 5에 도달하면 조건이 false가 되어 반복문을 빠져나가고 done이 출력됩니다. 본문은 정확히 5번 실행되며, i는 0부터 4까지의 값을 가집니다.
카운터를 헤더 안에서 선언하면(int i = 0) i의 범위가 반복문 안으로 한정됩니다. 닫는 중괄호 뒤에는 존재하지 않으며, 이는 바로 우리가 원하는 바입니다.
증가, 감소, 그리고 단계별 카운트
갱신 부분은 i++에만 국한되지 않습니다. 거꾸로 셀 수도 있고, 임의의 양만큼 건너뛸 수도 있으며, 배열 인덱스를 순회할 수도 있습니다.
첫 번째 반복문은 i > 0인 동안 실행되며 매번 감소하므로 5 4 3 2 1을 출력합니다. 두 번째는 매번 2를 더하고, 10이 포함하고 싶은 값이므로 <= 10을 씁니다. 조건을 갱신과 맞추세요. 거꾸로 세기는 > 또는 >=와, 올려 세기는 < 또는 <=와 짝을 이룹니다.
배열 순회
카운트 반복문의 가장 흔한 용도는 배열을 인덱스로 훑는 것입니다. 카운터는 읽는 위치 역할도 겸합니다.
조건이 i <= n이 아니라 i < n이라는 점에 주목하세요. 5개짜리 배열의 유효한 인덱스는 0부터 4까지이며, 인덱스 5는 끝을 넘어섭니다. scores[5]를 읽는 것은 미정의 동작으로, 쓰레기 값을 출력하거나, 크래시가 나거나, 동작하는 것처럼 보이면서 조용히 메모리를 망가뜨릴 수 있습니다. i < n 패턴은 0부터 시작하는 모든 배열에 대한 안전한 기본값입니다.
인덱스가 아니라 값만 필요하다면 범위 기반 for가 더 깔끔합니다. 실제로 위치가 필요할 때는 고전적인 인덱스 반복문을 꺼내 쓰세요.
break와 continue
두 개의 키워드로 반복문 도중에 흐름을 바꿀 수 있습니다. break는 반복문을 즉시 빠져나갑니다. continue는 현재 반복의 나머지를 건너뛰고 갱신으로 점프합니다.
첫 번째 반복문은 7을 찾는 순간 멈추고 나머지는 검사하지 않습니다. 두 번째는 i가 짝수일 때마다 본문의 출력을 건너뛰기 위해 continue를 씁니다. 갱신 i++는 여전히 실행되므로 반복문은 계속 진행됩니다. 미묘한 함정: continue는 갱신으로 점프합니다. 따라서 카운터가 헤더가 아니라 본문 안에서 갱신되는 반복문에서 continue에 의존하면, 그 갱신을 실수로 건너뛰고 영원히 돌 수 있습니다.
중첩 반복문
격자, 표, 쌍을 다루려면 for를 다른 for 안에 넣습니다. 안쪽 반복문은 바깥쪽 반복문의 한 단계마다 완전히 실행됩니다.
이것은 3x3 곱셈 격자를 출력합니다. 바깥쪽 반복문이 row를 고정하고, 안쪽 반복문이 그 행의 모든 col을 훑은 다음, 줄바꿈이 행을 마무리합니다. 카운터에는 서로 다른 이름을 주세요(i/i가 아니라 row/col). 같은 이름을 재사용하면 바깥쪽을 가려서 영문 모를 버그를 만듭니다. 비용도 주의하세요. n 반복문을 n 반복문 안에 중첩하면 본문이 n * n번 실행되어 빠르게 불어납니다.
흔한 함정
몇 가지 함정이 C++의 for 반복문 버그 대부분을 차지합니다.
- off-by-one: 0부터 시작하는 크기에서
i <= n은 끝을 넘어 한 요소를 읽습니다.i < n을 쓰세요. - 부호 없는 언더플로: 부호 없는 타입으로 거꾸로 세면 결코 음수가 되지 않습니다.
for (size_t i = n - 1; i >= 0; i--)는 영원히 반복합니다. 부호 없는 값에 대해i >= 0은 항상 참이기 때문입니다.i가0일 때i--는 거대한 양수로 되돌아갑니다(wrap). 내려가는 카운트에는 부호 있는int를 쓰거나 조건을 다시 작성하세요. - 본문 안에서 카운터 수정: 헤더에 더해 본문에서도
i를 바꾸면 반복 횟수를 예측할 수 없게 됩니다. 한 곳을 정하세요.
// BUG: infinite loop - unsigned i is never < 0
for (size_t i = n - 1; i >= 0; i--) {
process(arr[i]);
}
부동소수점 카운터는 또 다른 조용한 위험입니다. for (double x = 0.0; x != 1.0; x += 0.1)는 0.1을 정확히 저장할 수 없기 때문에 정확히 1.0에 도달하지 못할 수 있습니다. 정수 카운트로 반복하면서 값은 내부에서 계산하거나, != 대신 <를 쓰세요.
다음: while 반복문
for 반복문은 횟수를 미리 알 때 빛납니다. 하지만 때로는 고정된 단계 수 없이 조건이 바뀔 때까지 반복해야 합니다. 파일 끝까지 입력을 읽거나, 성공할 때까지 재시도하는 식입니다. 그것이 헤더를 조건 하나로 줄인 while 반복문의 역할입니다. 다음 페이지입니다.
자주 묻는 질문
C++에서 for 반복문은 어떻게 작성하나요?
헤더에 세미콜론으로 구분된 세 부분, 즉 초기화자, 조건, 갱신을 넣습니다. for (int i = 0; i < 5; i++) { cout << i; }는 i가 0, 1, 2, 3, 4로 바뀌면서 본문을 실행합니다. 조건이 false가 되는 즉시 반복문은 멈춥니다.
C++에서 for 반복문과 범위 기반 for 반복문의 차이는 무엇인가요?
고전적인 for는 직접 제어하는 인덱스 카운터(for (int i = 0; i < n; i++))를 줍니다. 위치가 필요하거나 사용자 정의 방식으로 진행하고 싶을 때 필요합니다. 범위 기반 for(for (int x : v))는 인덱스를 감추고 각 요소를 그대로 건네줍니다. 값만 필요할 때 더 깔끔합니다.
왜 제 C++ for 반복문이 한 번 더 또는 한 번 덜 실행되나요?
그게 바로 고전적인 off-by-one(하나 차이) 버그입니다. 0부터 시작하는 크기에서 < 대신 <=를 쓰면 반복을 한 번 더 하면서 배열의 끝을 넘어 읽습니다. 마지막 값을 포함하려 했는데 <를 쓰면 한 번 부족하게 실행됩니다. 크기가 n인 배열에서 안전한 패턴은 for (int i = 0; i < n; i++)입니다.