Menu

C 언어 for 반복문: 문법, 예제, 흔한 실수

C의 for 반복문으로 코드를 반복하는 방법 - 세 부분으로 된 헤더, 올려 세기와 내려 세기, sizeof 요령으로 배열 순회하기, 중첩 반복문, 무한 반복문, 그리고 누구나 한 번쯤 걸리는 off-by-one 및 부호 없는 타입 버그.

이 페이지에는 실행 가능한 에디터가 있습니다 - 편집하고 실행하면 결과를 바로 볼 수 있습니다.

왜 for 반복문인가

ifswitch는 분기 하나를 골라 한 번 실행합니다. 하지만 실제 프로그램은 무언가를 반복해서 해야 합니다. 모든 점수를 출력하거나, 숫자 목록을 합산하거나, 격자의 10행을 그리거나, 문자열의 각 문자를 훑는 식입니다. for 반복문은 정해진 횟수만큼 코드를 반복하는 C의 일꾼이며, 직접 제어하는 카운터를 가지고 있습니다.

for 반복문에 필요한 모든 것이 하나의 간결한 헤더에 담겨 있어서, "몇 번, 어떻게"가 한 줄에 드러납니다.

세 부분으로 된 헤더

for 헤더에는 세미콜론으로 구분된 세 부분이 있습니다. 초기화자, 조건, 갱신입니다.

for (initializer; condition; update) {
    // 본문 - 조건이 0이 아닌 동안 실행됨
}

이들은 정해진 순서로 실행되며, 그 순서를 알면 앞으로 읽게 될 모든 for 반복문이 설명됩니다.

  1. 초기화자가 다른 무엇보다 먼저 한 번 실행됩니다.
  2. 조건이 검사됩니다. 0이면 반복문이 즉시 끝납니다.
  3. 본문이 실행됩니다.
  4. 갱신이 실행됩니다.
  5. 다시 2번으로.

위 반복문을 이 목록에 따라 따라가 봅시다. int i = 0이 한 번 실행됩니다. 0 < 5가 성립하므로 본문이 i = 0을 출력하고, i++i1로 만듭니다. 조건이 다시 검사되고, 이런 식으로 이어집니다. i5에 도달하면 검사가 실패하여 본문을 건너뛰고 완료가 출력됩니다. 본문은 정확히 다섯 번 실행되었고, i0부터 4까지의 값을 가졌습니다.

여기서 두 가지 결론이 바로 따라옵니다. 조건은 첫 통과 전에 검사되므로, 조건이 처음부터 거짓인 반복문은 0번 실행됩니다 - for (int i = 10; i < 5; i++)는 본문에 아예 들어가지 않습니다. 그리고 갱신은 본문 뒤에 실행되므로, 본문 전체에서 i는 여전히 이전 값을 가지고 있습니다.

카운터를 헤더 안에서 선언하는 것(int i = 0)은 C99부터 합법이며 올바른 기본값입니다. i가 반복문 안에서만 존재하므로 그 뒤의 무엇과도 충돌할 수 없고, 함수 안의 각 반복문이 같은 이름을 자유롭게 재사용할 수 있습니다.

올려 세기, 내려 세기, 단계별 세기

갱신은 i++에만 국한되지 않습니다. 거꾸로 셀 수도, 임의의 양만큼 건너뛸 수도, 매번 두 배로 만들 수도 있습니다.

조건을 갱신과 맞추세요. 올려 세기는 < 또는 <=와, 내려 세기는 > 또는 >=와 짝을 이룹니다. 이 짝을 잘못 맞추는 것이 끝나지 않는 반복문을 쓰는 방법입니다.

두 번째 반복문이 <= 10을 쓰는 것은 10이 포함하고 싶은 값이기 때문이고, 첫 예제가 < 5를 쓴 것은 5가 그렇지 않기 때문입니다. off-by-one 버그는 바로 그 선택에서 태어나므로 의식적으로 고르세요. i < n은 0부터 시작해 n번, i <= nn + 1번 실행됩니다.

배열 순회하기

카운트 반복문의 가장 흔한 용도는 배열을 인덱스로 훑는 것입니다. 카운터는 읽는 위치 역할도 겸합니다.

여기서 가져갈 것이 두 가지 있습니다.

sizeof 요령. sizeof(scores)는 배열 전체의 바이트 크기이고 sizeof(scores[0])은 원소 하나의 크기이므로, 그 몫이 원소 개수입니다. 하드코딩한 5와 달리 원소를 추가해도 자동으로 따라옵니다. 함정은 - 그리고 이건 큰 함정입니다 - 배열 자체가 스코프 안에 있을 때만 통한다는 점입니다. 배열을 함수에 넘기면 포인터로 붕괴하므로, 그 안에서의 sizeof는 포인터의 크기를 줍니다. 함수 안에서는 항상 길이를 별도의 매개변수로 받으세요.

조건은 i <= n이 아니라 i < n입니다. 원소가 다섯 개인 배열의 유효한 인덱스는 0부터 4까지입니다. scores[5]를 읽는 것은 미정의 동작입니다. 쓰레기 값을 출력하거나, 크래시가 나거나, 동작하는 것처럼 보이면서 조용히 다른 무언가를 망가뜨릴 수 있습니다. C는 경계 검사를 전혀 하지 않으므로, 이건 전적으로 여러분 책임입니다.

배열을 거꾸로 훑는 것도 같은 생각을 뒤집은 것입니다.

n - 1(마지막 유효 인덱스)에서 시작해 0을 포함해서 멈추므로 >가 아니라 >=입니다. 여기서 i는 부호 있는 int로 두세요. 이유는 다음 절에서 설명합니다.

중첩 반복문

격자, 표, 또는 모든 항목 쌍을 다루려면 for를 다른 for 안에 넣습니다. 안쪽 반복문은 바깥쪽 반복문의 한 단계마다 끝까지 실행됩니다.

바깥쪽 반복문이 row를 고정하고, 안쪽 반복문이 그 행의 모든 col을 훑고, 안쪽 반복문 뒤의 printf("\n")이 줄을 마무리합니다. 그 줄바꿈을 안쪽 반복문 안으로 옮기면 표 전체가 한 열이 됩니다 - 중첩 구조를 눈에 보이게 만들어 주므로 한 번 해 볼 만합니다.

카운터에는 서로 다른 이름을 주세요. row/col이나 i/j면 충분합니다. 둘 다 i를 쓰면 바깥쪽 카운터를 가려서 영문 모를 결과를 만듭니다. 그리고 비용에 주의하세요. n 반복문 안의 n 반복문은 본문을 n * n번 실행하므로, 1,000회짜리 반복문 두 개는 백만 번의 통과입니다.

다음은 실제 일을 하는 중첩 반복문입니다. 안쪽 반복문의 상한이 바깥쪽 카운터에 의존하는 별 삼각형입니다.

break와 continue

두 개의 키워드가 반복문 도중에 흐름을 바꿉니다. break는 반복문을 즉시 떠나고, continue는 현재 통과의 나머지를 건너뛰고 갱신으로 점프합니다.

첫 번째 반복문은 7을 찾는 순간 멈추고 남은 93개의 값은 검사하지 않습니다. 두 번째는 짝수일 때 출력을 건너뛰기 위해 continue를 씁니다. 헤더의 i++는 여전히 실행되므로 반복문은 계속 나아갑니다. 둘에 대한 더 많은 내용은 break와 continue에 있으며, break 하나로는 할 수 없는 중첩 반복문 탈출 방법도 다룹니다.

무한 반복문과 빈 부분

헤더의 세 부분은 모두 선택 사항입니다. 조건을 빼면 영구히 참인 것으로 취급됩니다.

for (;;) {
    /* 영원히 실행됨 - break 또는 return으로 빠져나감 */
}

for (;;)는 C의 관용적인 무한 반복문이며, 세미콜론은 여전히 필요합니다. 안에서 break, return, exit 할 수 있는 무언가가 있는 한 실수가 아닙니다. 이벤트 루프나 "입력이 유효해질 때까지 계속 묻기" 반복문은 정확히 이렇게 작성됩니다.

다른 부분도 뺄 수 있습니다. 카운터가 이미 있으면 초기화자를 건너뛰고, 본문이 카운터를 전진시키면 갱신을 건너뜁니다.

int i = 0;
for (; i < n; ) {
    /* ... */
    i += step;
}

이것은 합법이지만, 사실상 그것이 실제로 무엇인지 - while 반복문 - 보다 읽기 나쁩니다. 카운터, 그 한계, 그 단계가 헤더에 함께 속할 때는 for를, 그렇지 않을 때는 while 반복문을 택하세요.

헤더 안의 콤마 연산자

초기화자와 갱신은 각각 콤마로 구분된 여러 표현식을 담을 수 있으며, 이것이 카운터 두 개를 동시에 돌리는 방법입니다.

int i = 0, j = len - 1은 두 카운터를 선언하고, i++, j--는 둘 다 전진시킵니다. 이것이 콤마 연산자로, 피연산자를 순서대로 평가합니다. 진짜로 나란히 가는 카운터에 쓰세요. 관련 없는 일을 헤더에 욱여넣는 것은 그 일을 감출 뿐입니다.

흔한 함정

몇 가지 함정이 C의 for 반복문 버그 대부분을 차지합니다.

off-by-one. 0부터 시작하는 배열에서 i <= n은 끝을 넘어 한 원소를 읽습니다. "0부터 시작해 n번"에는 i < n을 쓰세요.

헤더 뒤의 세미콜론. 이것은 컴파일되지만 거의 절대 의도한 바가 아닙니다.

/* 버그: 반복문 본문이 빈 문장이고, printf는 한 번만 실행됨 */
for (int i = 0; i < 5; i++);
{
    printf("%d\n", i);
}

;가 본문이 되므로 반복문은 아무 일도 하지 않고 다섯 번 돌고, 아래의 중괄호는 평범한 블록으로 한 번 실행됩니다. gcc -Wall이 이에 대해 경고합니다.

내려 세는 부호 없는 카운터. 이것은 무한 반복문입니다.

/* 버그: 부호 없는 값은 결코 < 0 이 아니다 */
for (size_t i = n - 1; i >= 0; i--) {
    process(a[i]);
}

부호 없는 타입에서 i >= 0은 항상 참입니다. i0에 도달하면 i--가 거대한 양수로 되돌아가고(wrap), 반복문은 자기 것이 아닌 메모리로 계속 나아갑니다. 내려 세기에는 부호 있는 int를 쓰거나, 조건을 for (size_t i = n; i-- > 0; )로 쓰세요. 이 형태는 감소와 검사를 한 단계로 하면서 0에서 올바르게 멈춥니다.

두 곳에서 카운터 수정하기. 헤더뿐 아니라 본문에서도 i를 바꾸면 반복 횟수를 예측할 수 없게 됩니다. 한 곳을 정하세요.

부동소수점 카운터. for (double x = 0.0; x != 1.0; x += 0.1)는 끝나지 않을 수 있습니다. 0.1은 정확한 이진 표현이 없어서 합이 1.0을 맞히지 못하고 지나칠 수 있기 때문입니다. 정수 카운트로 반복하고 값은 안에서 계산하세요.

for (int i = 0; i <= 10; i++) {
    double x = i / 10.0;
    /* ... */
}

반복 중에 배열의 길이 바꾸기. 조건 i < n은 매 통과마다 n을 다시 읽으므로, 본문이 컬렉션을 줄인다면 둘이 어긋나게 두지 말고 인덱스를 의도적으로 조정하세요.

자주 묻는 질문

C에서 for 반복문은 어떻게 작성하나요?

헤더에 세미콜론으로 구분된 세 부분, 즉 초기화자, 조건, 갱신을 넣습니다. for (int i = 0; i < 5; i++) { printf("%d\n", i); }i가 0부터 4까지의 값을 가지면서 본문을 다섯 번 실행하고, 조건이 거짓이 되는 즉시 멈춥니다.

C에서 배열을 반복하려면 어떻게 하나요?

인덱스를 0부터 길이 직전까지 셉니다. for (int i = 0; i < n; i++) { sum += a[i]; }처럼 씁니다. 같은 함수 안에서 선언된 배열이라면 sizeof(a) / sizeof(a[0])으로 길이를 계산할 수 있습니다. 배열을 함수에 넘기고 나면 이 요령은 더 이상 통하지 않으므로, 길이를 매개변수로 함께 넘기세요.

왜 제 C for 반복문이 한 번 더 실행되나요?

그게 바로 off-by-one(하나 차이) 버그입니다. 크기가 n인 0부터 시작하는 배열에서 유효한 인덱스는 0부터 n - 1까지이므로, i <= n은 반복을 한 번 더 하면서 끝을 넘어 읽습니다 - 미정의 동작입니다. 안전한 기본값은 i < n입니다.

C의 for 반복문 안에서 반복 변수를 선언할 수 있나요?

네, C99부터 가능합니다. for (int i = 0; i < n; i++)처럼 쓰면 그 변수는 반복문 안에서만 존재하며, 그것이 바람직한 모습입니다. gcc -std=c99 이상으로 컴파일하세요(요즘 GCC와 clang은 기본이 C17이라 그냥 됩니다). 예전 C89 스타일은 i를 반복문 앞에서 선언했습니다.

Coddy programming languages illustration

Coddy로 코딩 배우기

시작하기