요약

프로그램은 책처럼 위에서 아래로 한 줄씩 읽힌다. 제어문은 그 길에 놓는 갈림길과 되돌아가는 길이다. 조건에 따라 한 덩어리를 건너뛰거나 둘 중 하나를 고르고, 같은 덩어리를 여러 번 되풀이한다. 덕분에 같은 코드를 백 번 적지 않아도 된다. 대신 여러 갈래 중 하나를 고르는 문에서 “여기서 나가라”는 표시를 빼먹으면 아래 갈래까지 줄줄이 실행되고, 소수로 횟수를 세면 한 번이 모자라거나 남는다.

예시로 보기

1부터 100까지 더하는 프로그램을 제어문 없이 쓰면 sum = sum + 1;부터 sum = sum + 100;까지 백 줄이다. 더할 수가 실행 중에 정해진다면 아예 쓸 수 없다. 반복문 하나면 세 줄이다.

int sum = 0;
for (int i = 1; i <= 100; i++) {
    sum += i;
}
// sum = 5050

실행은 main() 메서드의 여는 중괄호 {에서 닫는 중괄호 }까지 위에서 아래로 흐른다. 이 흐름을 원하는 쪽으로 바꾸는 문장이 제어문이다. 조건이 맞을 때만 실행하는 것(조건문)과, 끝까지 간 뒤 처음으로 되돌아가 다시 실행하는 것(반복문)이 있다.

검증: 등급 예와 경계 점수, break가 있는 switch와 없는 switch의 출력, for 추적 표, 1 ~ 100의 합 5050, float로 센 횟수 9번, do-while 1번, 안쪽 break의 출력, continue의 출력, 카드 C1·C3 — 06_java-control-flow_impl.java

조건문

if

if는 조건의 결과를 보고 블록을 실행할지 정한다. 조건 자리에는 true나 false가 되는 식(또는 boolean 변수)이 온다. true면 블록을 실행하고 false면 건너뛴다. else를 붙이면 false일 때 대신 실행할 블록을 둘 수 있다.

if (score >= 90) {
    grade = "A";
} else if (score >= 80) {
    grade = "B";
} else {
    grade = "C";
}

조건은 위에서부터 차례로 본다. 처음 맞는 블록 하나만 실행하고 나머지는 보지 않는다. 85점이면 첫 조건이 false, 둘째가 true라 "B"다. 95점은 둘째 조건에도 맞지만 첫 블록에서 이미 끝났으므로 "A"다1.

switch

switch는 괄호 안 변수의 값과 같은 case를 찾아 그 아래 문장을 실행한다. 맞는 case가 없으면 default로 간다. default는 없어도 된다.

switch (n) {
    case 1:
        System.out.print("one ");
        break;
    case 2:
        System.out.print("two ");
        break;
    default:
        System.out.print("other ");
}

break는 switch를 빠져나가라는 표시다. break를 빼면 다음 case의 문장까지 이어서 실행한다. case는 “여기서부터 시작하라”는 입구일 뿐 칸막이가 아니다. 위 코드에서 break 둘을 지우면 출력이 이렇게 달라진다1.

n break가 있을 때 break를 지웠을 때
1 one one two other
2 two two other
9 other other

반복문

for

for는 문장을 여러 번 되풀이한다. 괄호 안에 세 가지를 적는다.

for (①초기화식; ②조건식; ④증감식) {
    ③실행문;
}
flowchart TD
    A["1 초기화식 (딱 한 번)"] --> B{"2 조건식이 true인가"}
    B -->|"true"| C["3 실행문"]
    C --> D["4 증감식"]
    D --> B
    B -->|"false"| E["for 문을 끝내고 다음 문장으로"]
  1. 초기화식을 먼저 한 번 실행한다.
  2. 조건식을 본다. false면 for를 끝내고 블록을 건너뛴다.
  3. true면 실행문을 실행한다.
  4. 증감식을 실행하고 2로 돌아간다. 2 → 3 → 4가 조건이 false가 될 때까지 되풀이된다.

for (int i = 1; i <= 3; i++) { sum += i; }를 sum = 0에서 따라가 본다1.

바퀴 조건을 볼 때의 i i <= 3 실행문 뒤 sum 증감식 뒤 i
1 1 true 1 2
2 2 true 3 3
3 3 true 6 4
4 4 false 실행 안 함 —

조건식은 네 번, 실행문은 세 번 실행된다. 조건이 처음부터 false면 실행문은 한 번도 실행되지 않는다.

초기화식에 float를 쓰지 않는다. 0.1부터 1.0까지 0.1씩 올리면 열 번 돌 것 같지만 아홉 번 돈다1.

int count = 0;
for (float x = 0.1f; x <= 1.0f; x += 0.1f) {
    count++;
}
// count = 9

0.1f는 정확한 0.1이 아니다. 열 번 더한 값이 1.0000001이 되어 마지막 바퀴의 조건 x <= 1.0f가 false가 된다(부동소수점). 횟수는 정수로 센다.

while과 do-while

while과 do-while은 횟수 없이 조건만 보고 도는 반복문이다. 모양은 다음과 같다2.

반복문 모양 조건을 보는 때 최소 실행 횟수
while while (조건) { ... } 블록 앞 0번
do-while do { ... } while (조건); 블록 뒤 1번

횟수가 정해져 있으면 for, 조건만 있으면 while이 읽기 쉽다.

break와 continue

문장 하는 일 쓸 수 있는 곳
break 반복문이나 switch를 그 자리에서 끝낸다 for, while, do-while, switch
continue 이번 바퀴의 남은 문장을 건너뛰고 다음 바퀴로 간다. for에서는 증감식으로, while과 do-while에서는 조건식으로 간다 for, while, do-while만

continue는 break와 달리 반복문을 끝내지 않는다.

for (int j = 1; j <= 7; j++) {
    if (j % 2 == 0) continue;    // 짝수면 아래를 건너뛰고 j++로 간다
    System.out.print(j + " ");
}
// 1 3 5 7

반복문이 겹쳐 있을 때 break는 자기를 감싼 가장 안쪽 반복문 하나만 끝낸다1.

for (int a = 1; a <= 3; a++) {
    for (int b = 1; b <= 3; b++) {
        if (b == 2) break;       // 안쪽 for만 끝난다
        System.out.print(a + "" + b + " ");
    }
}
// 11 21 31

활용

  • 값 하나를 여러 정해진 값과 견줄 때는 switch, 범위나 여러 변수가 얽힌 조건은 if가 맞다. switch의 괄호에는 정수, 글자, 문자열을 넣을 수 있다2.
  • 흔한 실수: switch에서 break를 빼먹는다. 일부러 뺀 것이면 주석으로 남긴다. 같은 일을 하는 case 여럿을 case "토": case "일":처럼 붙여 쓰는 것은 이 성질을 일부러 쓰는 예다.
  • 흔한 실수: 중괄호 없이 if 아래에 두 줄을 들여 쓴다. 중괄호가 없으면 if는 바로 다음 한 문장만 잡는다. 둘째 줄은 들여쓰기와 상관없이 늘 실행된다1.
  • 흔한 실수: while 안에서 continue보다 뒤에 증가 문장을 둔다. continue가 증가를 건너뛰어 같은 값으로 끝없이 돈다. for는 증감식이 따로 있어 이 문제가 없다.
  • 흔한 실수: i <= 길이로 배열을 돈다. 마지막 번호는 길이보다 1 작다(배열).

연결

확인 문제

C1 다음 코드의 출력은?
for (int j = 0; j < 6; j++) {
    if (j == 1) continue;
    if (j == 4) break;
    System.out.print(j);
}

답: 023
이유: 0을 찍는다. 1에서 continue라 찍지 않고 다음 바퀴로 간다. 2, 3을 찍는다. 4에서 break라 반복문이 끝난다. 5는 가 보지도 않는다. 0235는 break를 continue처럼 읽은 오답이다.

C2 반복문 안에서 ① 찾던 값을 발견해 더 볼 필요가 없다 ② 이번 값만 처리하지 않고 넘어가고 싶다. 각각 break와 continue 중 무엇을 쓰는가? 다른 쪽을 쓰면 어떻게 되는가?

답: ①은 break, ②는 continue.
이유: ①에 continue를 쓰면 답을 찾고도 끝까지 헛돈다. ②에 break를 쓰면 그 뒤의 값들을 하나도 처리하지 못한다. break는 반복문 전체를, continue는 이번 바퀴만 끝낸다.

C3 아래 switch에서 n이 1일 때와 2일 때의 출력을 쓰라.
switch (n) {
    case 1: System.out.print("one ");
    case 2: System.out.print("two ");
    default: System.out.print("other ");
}

답: 1일 때 one two other, 2일 때 two other.
이유: break가 없어서 맞는 case부터 아래로 끝까지 실행한다. case는 들어가는 곳만 정한다.

C4 for (float x = 0.1f; x <= 1.0f; x += 0.1f)는 왜 열 번이 아니라 아홉 번 도는가? 열 번 돌게 하려면 어떻게 쓰는가?

답: 0.1f가 정확한 0.1이 아니라서 열 번째 바퀴에 들어갈 때 x가 1.0000001이 되고, x <= 1.0f가 false다. for (int i = 1; i <= 10; i++)로 정수로 세고, 필요하면 블록 안에서 float x = i * 0.1f;로 만든다.
이유: 정수 덧셈에는 오차가 없다. 소수는 더할 때마다 오차가 쌓여 경계에서 한 번이 어긋난다.

  1. 출력과 값은 구현 코드로 확인했다. ↩ ↩2 ↩3 ↩4 ↩5 ↩6

  2. Java Language Specification §14.11(switch), §14.12(while), §14.13(do)에 있고, 구현 코드로 확인했다. ↩ ↩2