프로그래밍

반복 구조

같은 줄을 백 번 쓸 수는 없습니다.
한 번만 쓰고, 몇 번 반복할지를 따로 정합니다.

학습 목표

  1. 같은 일을 반복하는 구조가 왜 필요한지 설명하고, 반복에 필요한 요소를 시작·조건·변화로 나누어 설명할 수 있다.
  2. 변화를 빠뜨리면 반복이 왜 멈추지 않는지 설명할 수 있다.
  3. 같은 반복을 while문과 for문으로 각각 쓸 수 있고, 상황에 알맞은 반복문을 고를 수 있다.
  4. 값을 누적하는 변수를 알맞은 위치에서 초기화하고, break와 continue로 반복을 끝내거나 건너뛸 수 있다.

같은 일을 여러 번

코드에 한 줄씩 직접 쓸 수 있는 횟수에는 한계가 있습니다.

「안녕하세요」를 다섯 번 출력하려면 이렇게 하면 됩니다.

printf("안녕하세요\n");
printf("안녕하세요\n");
printf("안녕하세요\n");
printf("안녕하세요\n");
printf("안녕하세요\n");
// 100번이라면? 1000번이라면? 게다가 횟수를 사용자가 정한다면 몇 줄을 써야 할지 미리 알 수 없다

다섯 번은 괜찮습니다. 그런데 백 번이면 백 줄을 써야 하고, 「안녕하세요」를 「반갑습니다」로 바꾸려면 백 줄을 다 고쳐야 합니다. 무엇보다 횟수를 사용자가 정하면 이런 방식으로는 아예 쓸 수 없습니다. 코드를 작성하는 시점에는 그 횟수를 모르기 때문입니다.

반복 구조

같은 줄을 여러 번 쓰는 대신, 한 번만 쓰고 「몇 번 반복할지」를 따로 정하는 방식입니다. 코드의 줄 수가 반복 횟수와 상관없어지므로 백 번이든 백만 번이든 코드 길이가 같습니다.

트랙을 도는 것으로 생각합니다

운동장 트랙을 떠올리면 편합니다. 달리는 사람은 같은 트랙을 계속 돕니다. 코드도 마찬가지로 중괄호 안의 같은 문장들을 반복해서 실행합니다. 트랙 한 바퀴가 「반복 한 번」에 해당합니다.

스스로 확인

「안녕하세요」를 사용자가 입력한 횟수만큼 출력하려면, 반복보다 먼저 무엇이 필요할까요?

반복에 필요한 세 가지

셋 가운데 하나만 빠져도 반복이 의도한 대로 실행되지 않습니다.

반복에는 세 가지가 필요합니다. 어떤 값에서 시작하는가, 언제까지 반복하는가, 반복할 때마다 무엇이 달라지는가. 셋이 모두 있어야 의도한 횟수만큼 반복하고 끝납니다.

int i = 1;                    // ① 시작할 값

while (i <= 5) {              // ② 언제까지 반복할 것인가
    printf("%d번째 안녕하세요\n", i);
    i = i + 1;                // ③ 다음 반복으로 가기 전에 무엇이 달라지는가
}
요소 위 코드에서 빠뜨리면
① 시작 int i = 1; 어디서부터 세는지 알 수 없다
② 조건 i <= 5 언제 멈출지 알 수 없다
③ 변화 i = i + 1; 처음에 참이던 조건이 계속 참이라 멈추지 않는다

while문은 「조건이 참인 동안 반복하라」는 뜻입니다. 반복을 시작하기 전과 반복을 한 번 마칠 때마다 조건을 검사하고, 조건이 거짓이면 반복을 끝내고 중괄호 다음 문장으로 넘어갑니다.

변화를 빠뜨리면 반복이 끝나지 않습니다

int i = 1;

while (i <= 5) {
    printf("%d\n", i);
    // ③ 변화를 빠뜨렸다 → i가 계속 1이라 조건이 계속 참이다
}
// 이 코드는 멈추지 않는다(무한 루프). 실행하면 Ctrl+C를 눌러 강제로 종료해야 한다

i가 계속 1이므로 i <= 5도 계속 참입니다. 이렇게 끝나지 않는 반복을 무한 루프(infinite loop)라고 합니다. 화면에 1이 끝없이 출력되고, Ctrl+C를 눌러 강제로 종료해야 멈춥니다.

처음 배울 때 흔히 한 번쯤 겪습니다. 프로그램이 고장 난 것이 아니라 코드에 쓴 대로 실행된 결과이니, 당황하지 말고 ③ 변화가 있는지부터 확인하세요.

스스로 확인

i = i + 1을 i = i - 1로 바꾸면 어떻게 될까요? 조건 i <= 5는 언제 거짓이 될까요?

세 가지를 한 줄에 모은 for문

흩어져 있으면 빠뜨리기 쉽습니다. 그래서 한곳에 모아 쓰는 반복문이 따로 있습니다.

방금 본 while문에서 세 가지는 서로 다른 세 곳에 흩어져 있었습니다. ①은 반복문 앞, ②는 while 뒤 괄호 안, ③은 중괄호 안 맨 끝에 있습니다. 그래서 ③을 빠뜨리기 쉽습니다. 셋을 한 줄에 모아 쓰는 반복문이 for문입니다.

//        ①시작    ②조건    ③변화
for (int i = 1; i <= 5; i = i + 1) {
    printf("%d번째 안녕하세요\n", i);
}
// while로 쓴 것과 「완전히 같은 일」을 한다. 셋을 한 줄에 모아 둔 것뿐이다

실행 순서는 while문과 같습니다

  1. ① 시작을 딱 한 번 실행한다.
  2. ② 조건을 검사한다. 거짓이면 반복을 끝낸다.
  3. 중괄호 안의 문장을 실행한다.
  4. ③ 변화를 실행하고 2번으로 돌아간다.

③은 중괄호 안의 문장을 모두 실행한 「뒤」에 실행됩니다. 코드에서는 맨 위에 쓰지만 실행되는 시점은 중괄호 안의 문장이 끝난 뒤라는 점에 주의합니다.

while문과 for문 가운데 무엇을 쓸까

둘은 서로 바꿔 쓸 수 있고, 할 수 있는 일에 차이가 없습니다. 다만 반복 횟수가 정해져 있으면 for문이 읽기 좋습니다. 한 줄만 보면 몇 번 반복하는지 알 수 있기 때문입니다. 「언제 끝날지 미리 모를 때」는 while문이 자연스럽습니다. 사용자가 그만두겠다고 입력할 때까지 반복하는 경우가 그렇습니다.

먼저 한 번 실행하고 나서 조건을 검사하는 방법도 있습니다

int i = 10;

do {
    printf("한 번은 반드시 실행됩니다 (i=%d)\n", i);   // 조건이 처음부터 거짓인데도 출력된다
} while (i <= 5);

while문은 조건을 먼저 검사하고 중괄호 안으로 들어가므로, 조건이 처음부터 거짓이면 한 번도 실행되지 않습니다. do…while문은 중괄호 안을 먼저 실행하고 나서 조건을 검사합니다. 그래서 적어도 한 번은 실행됩니다.

메뉴를 출력하고 사용자가 고르게 하는 프로그램이 대표적입니다. 메뉴를 적어도 한 번은 출력해야 사용자가 고를 수 있기 때문입니다. while (조건) 뒤에 세미콜론을 붙인다는 점도 눈여겨보세요.

스스로 확인

사용자가 0을 입력할 때까지 계속 수를 입력받는 프로그램이라면 while, for, do…while 가운데 무엇이 알맞을까요?

값을 누적하기

누적할 변수를 반복 「밖」과 「안」 가운데 어디서 초기화하느냐에 따라 결과가 달라집니다.

반복은 반복할 때마다 나온 값을 모을 때 특히 쓸모가 많습니다. 1부터 100까지 더하는 코드를 봅시다.

int sum = 0;                       // 합을 누적할 변수를 「반복 밖」에서 초기화한다

for (int i = 1; i <= 100; i = i + 1) {
    sum = sum + i;                 // 앞 반복까지의 합에 i를 더한다
}

printf("1부터 100까지의 합은 %d입니다.\n", sum);   // 5050

sum = sum + i는 대입 규칙 그대로입니다 — 오른쪽을 먼저 계산해 왼쪽 변수에 대입합니다. 반복할 때마다 「지금까지의 합에 이번 수를 더해 다시 대입하는」 일이 일어납니다. 이렇게 값을 계속 더해 나가는 것을 누적(accumulation)이라고 합니다.

누적할 변수를 「안」에서 초기화하면 합이 쌓이지 않습니다

for (int i = 1; i <= 3; i = i + 1) {
    int sum = 0;                   // 반복 「안」에서 선언하면 반복할 때마다 0으로 초기화된다
    sum = sum + i;
    printf("%d ", sum);            // 1 2 3   합이 누적되지 않는다
}
printf("\n");

반복할 때마다 sum이 새로 선언되고 0으로 초기화되므로 앞에서 더한 값이 사라집니다. 그래서 1 3 6이 아니라 1 2 3이 출력됩니다.

누적할 변수는 반복 「밖」에서 「한 번만」 초기화합니다. 반대로 반복할 때마다 새로 시작해야 하는 값은 안에서 초기화하는 것이 맞습니다. 안이 늘 틀린 것이 아니라 무엇을 하려는지에 달렸습니다.

#include <stdio.h>

int main(void) {
    int n;
    int sum = 0;

    printf("1부터 몇까지 더할까요? ");
    scanf("%d", &n);

    for (int i = 1; i <= n; i = i + 1) {
        sum = sum + i;
    }

    printf("1부터 %d까지의 합은 %d, 평균은 %.2f입니다.\n", n, sum, (double) sum / n);
    return 0;
}

횟수를 사용자에게 입력받으면, 「같은 일을 여러 번」 절에서 코드에 미리 쓸 수 없다고 했던 일도 할 수 있습니다. 평균을 구할 때 (double)을 붙인 것에 주목하세요. 두 값이 모두 정수이면 몫만 남는 정수 나눗셈이 되므로, 강제 형 변환으로 이를 피한 것입니다.

반복 횟수를 세는 변수를 계산에도 씁니다

i는 몇 번째 반복인지 세는 데만 쓰는 것이 아닙니다. 위 코드에서는 i의 값 자체가 더할 대상이기도 합니다. 「첫 번째 반복에서는 1을, 두 번째 반복에서는 2를」 더하는 식으로 반복 횟수를 세는 변수를 그대로 계산에 쓰는 것은 반복문의 흔한 활용법입니다.

스스로 확인

곱을 누적할 변수는 0으로 초기화하면 안 됩니다. 왜일까요? 무엇으로 초기화해야 할까요?

끝내기와 건너뛰기

반복을 아예 끝내는 것과 이번 반복만 건너뛰는 것은 다릅니다.

break — 반복을 끝낸다

for (int i = 1; i <= 10; i = i + 1) {
    if (i == 4) {
        break;                     // 반복 「전체」를 끝낸다
    }
    printf("%d ", i);              // 1 2 3
}
printf("\n");

반복 전체를 끝냅니다. 남은 반복이 있어도 더 실행하지 않고 반복문 다음 문장으로 넘어갑니다.

continue — 이번 반복만 건너뛴다

for (int i = 1; i <= 10; i = i + 1) {
    if (i % 3 != 0) {
        continue;                  // 이번 반복의 남은 문장을 건너뛰고 「다음 반복으로」 간다
    }
    printf("%d ", i);              // 3 6 9
}
printf("\n");

이번 반복의 남은 문장만 건너뜁니다. 반복은 끝나지 않고 다음 반복으로 이어집니다.

for문과 while문에서 continue를 쓸 때 조심할 것

for문에서 continue를 만나면 남은 문장을 건너뛰지만 ③ 변화는 건너뛰지 않습니다. ③은 for 뒤 괄호 안에 쓰여 있으므로 그대로 실행된 다음 조건을 검사합니다. 그런데 while문에서 ③을 중괄호 「안」 맨 끝에 두었다면 continue가 그 문장까지 건너뛰므로 반복이 멈추지 않을 수 있습니다. 같은 continue라도 반복문의 구조에 따라 결과가 달라지는 경우입니다.

switch문의 break와 같습니다

switch문에서 case를 끝낼 때 쓰는 그 break입니다. 하는 일도 같습니다. break는 자신을 감싼 가장 가까운 반복문이나 switch문을 빠져나갑니다. switch문 안이면 switch문을, 반복문 안이면 반복문을 빠져나갑니다. if문의 중괄호는 여기에 해당하지 않으므로, 위 break 예시에서도 break는 if문이 아니라 for문을 빠져나갔습니다.

스스로 확인

1부터 세다가 처음으로 7의 배수를 만나면 그 수를 출력하고 멈추려면 break와 continue 가운데 무엇을 쓸까요?

핵심 정리

반복의 세 가지

시작 · 조건 · 변화. 변화를 빠뜨리면 처음에 참이던 조건이 계속 참이라 무한 루프가 된다. 반복이 멈추지 않으면 ③부터 확인한다.

while과 for

할 수 있는 일은 같다. 세 가지가 흩어져 있으면 while문, 한 줄에 모여 있으면 for문이다. 반복 횟수가 정해져 있으면 for문이 읽기 좋다.

누적할 변수

누적할 변수는 반복 밖에서 한 번만 초기화한다. 안에서 초기화하면 반복할 때마다 초기값으로 되돌아간다. 반복할 때마다 새로 시작해야 하는 값은 안에서 초기화한다.

break와 continue

break는 반복 전체를 끝내고, continue는 이번 반복의 남은 문장만 건너뛴다.

확인 퀴즈

배운 내용을 확인해 봅시다.

1. 반복이 멈추지 않고 계속 실행된다. 가장 먼저 확인할 곳은?

2. int sum = 0;을 반복 중괄호 「안」으로 옮기면?

3. 다음 중 틀린 설명은?