반복문의 흐름을 바꾸는 두 가지 방법
반복문의 조건이 언제 끝날지를 결정하지만, 진짜 답이 본문 중간에서 나오는 경우도 있습니다. 배열을 검색하다가 세 번째 요소에서 찾았다면 나머지 997개를 왜 확인하겠습니까? 목록을 처리하다가 한 항목이 비어 있다면, 건너뛰고 계속 가면 됩니다.
C에는 각 경우를 위한 키워드가 하나씩 있습니다.
break- 지금 반복문을 떠납니다. 실행은 그 바로 뒤 문장에서 이어집니다.continue- 이번 반복만 포기합니다. 다음 반복으로 넘어갑니다.
첫 번째 반복문은 1 2 3 4를 출력하고 멈춥니다. 두 번째는 1 2 3 4 6 7 8 9 10을 출력합니다. 값 하나를 건너뛰고 계속 진행한 것이죠.
반복문 안의 break
가장 전형적인 쓰임새는 성공하는 즉시 멈추는 검색입니다.
found가 break 앞에서 설정되므로, 반복문 뒤의 코드는 "찾았다"와 "요소를 다 썼다"를 구분할 수 있습니다. 결과 변수 하나에 break를 짝지은 이 조합이 C의 표준적인 검색 관용구입니다. 반복 카운터 자체는 헤더에서 선언한 경우 닫는 중괄호에서 범위를 벗어나므로 나중에 쓸 수 없습니다.
break는 언제나 하나의 반복문만 빠져나옵니다. 가장 가깝게 감싸고 있는 for, while, do-while, 또는 switch입니다.
반복문 안의 continue
continue는 작업을 멈출 정도는 아니고 그냥 건너뛰어야 하는 항목을 위한 것입니다.
continue가 어디로 점프하는지는 반복문의 종류에 따라 다릅니다.
for -> 헤더의 갱신 부분 (그래서 카운터는 여전히 증가함)
while -> 조건 검사 (본문의 나머지는 실행되지 않음)
do-while -> 아래쪽의 조건 검사
이 차이는 탁상공론이 아닙니다. for 반복문에서는 증가가 헤더에 있으니 continue가 카운터 증가를 건너뛸 수 없습니다. while 반복문에서는 가능합니다.
/* 버그: continue가 i++를 건너뛰어 첫 짝수에서 멈춰 버립니다 */
int i = 0;
while (i < 10) {
if (i % 2 == 0) {
continue;
}
printf("%d ", i);
i++;
}
i++를 continue 위로 옮기거나, 애초에 틀릴 수 없도록 헤더가 보장해 주는 for 반복문으로 쓰세요.
switch 안의 break는 반복문의 break가 아닙니다
break는 가장 가깝게 감싸고 있는 반복문 또는 switch를 끝냅니다. 더 가까운 쪽이 이깁니다. 반복문 안에 switch를 넣으면 그 안의 모든 break는 switch의 것입니다.
q 명령은 메시지를 출력하지만 반복문은 cc까지 계속 돕니다. 종료가 반복문을 끝내야 하는 것이었다면, 저 break는 그 일을 하지 못한 겁니다. 해결책은 플래그, goto, 또는 반복문을 함수로 옮기고 return을 쓰는 것이며, 모두 아래에서 보여 드립니다.
중첩 반복문 탈출하기
break 하나는 한 단계만 빠져나옵니다. 두 겹으로 중첩된 반복문 안에서는 안쪽만 끝내고, 바깥 반복문은 아무 일도 없었다는 듯 다음 반복을 시작합니다.
C에는 break 2도 없고 레이블 붙은 break도 없습니다. 정직한 선택지는 셋입니다.
플래그. 이식성이 좋고 명확하지만 코드가 약간 지저분해집니다.
플래그가 바깥 조건과 대입, 두 군데에 등장하는 것이 그 대가입니다. 중첩이 세 겹이 되면 정말로 괴로워집니다.
goto. 그토록 욕을 먹는 C의 점프문이지만, 숙련된 C 프로그래머들이 변명 없이 쓰는 유일한 경우가 바로 이것입니다.
반복문 바로 뒤의 레이블로 향하는 전방 goto는 다른 언어의 레이블 붙은 break가 하는 일을 정확히, 한 줄로, 동기화할 플래그 없이 해냅니다. goto의 악명은 후방 점프와 다른 블록 한가운데로 뛰어드는 점프가 벌어들인 것이고, 그런 것들은 실제로 읽을 수 없는 제어 흐름을 만듭니다. 중첩 반복문에서 한 번 앞으로 뛰어나오는 것은 그와 다릅니다. 리눅스 커널도 정리(cleanup) 경로에 같은 패턴을 씁니다. 의도적으로, 아껴서 쓰세요. 금기 때문에 읽기 더 어려운 플래그 세 개짜리 대안으로 밀려나지는 마세요.
함수와 return. 검색에는 이름을 붙일 만하니, 대체로 이것이 최선의 답입니다.
return은 함수 안의 모든 반복문을 한 번에 풀어내며, 플래그도 레이블도 필요 없고, 검색 코드에는 이제 이름이 생겨 따로 테스트할 수도 있습니다. 중첩이 너무 깊어서 빠져나오는 것 자체가 문제가 된다면, 그 블록이 애초에 함수였어야 한다는 신호인 경우가 많습니다.
도움이 될 때와 해가 될 때
break와 continue는 본문 전체를 if로 감싸는 대신 "이 경우는 끝났다"를 위쪽에서 한 번 말하게 해 줄 때 제값을 합니다. 비교해 보세요.
/* continue 사용: 진짜 작업이 들여쓰기에 파묻히지 않음 */
for (int i = 0; i < n; i++) {
if (!isValid(a[i])) continue;
if (isDuplicate(a[i])) continue;
process(a[i]);
}
/* 미사용: 검사를 하나 추가할 때마다 단계가 깊어짐 */
for (int i = 0; i < n; i++) {
if (isValid(a[i])) {
if (!isDuplicate(a[i])) {
process(a[i]);
}
}
}
반면 개수가 많아지면 해가 됩니다. break 다섯 개와 continue 세 개가 여기저기 흩어진 반복문은 종료 조건이 어디에도 적혀 있지 않은 셈이라, 읽는 사람이 언제 멈추는지 알려면 본문 전체를 머릿속으로 돌려 봐야 합니다. 잘 통하는 두 가지 지침이 있습니다. continue 가드는 본문 맨 위에 모아 두세요. 그리고 반복문에 break가 한두 개를 넘는다면, 헤더의 조건이 그 일을 했어야 하는 건 아닌지 따져 보세요.
작은 함정 두 가지. do-while의 continue는 아래쪽 검사로 점프하는데 어차피 그다음 차례가 그것이라 무해하지만, 사람들을 놀라게 합니다. 그리고 반복문이나 switch 밖의 break는 컴파일 오류인데, 여기서 컴파일러가 대신 잡아 주는 유일한 실수입니다.
자주 묻는 질문
C에서 break와 continue의 차이는 무엇인가요?
break는 반복문을 완전히 끝내고 그다음 문장부터 실행을 이어 갑니다. continue는 현재 반복만 포기하고 다음 반복으로 넘어갑니다. for 반복문에서는 갱신 부분으로, while이나 do-while에서는 곧바로 조건 검사로 이동합니다.
C에서 중첩 반복문을 빠져나오려면 어떻게 하나요?
break 하나는 가장 안쪽 반복문만 빠져나옵니다. 둘 다 빠져나오려면 플래그를 세워 바깥 반복문의 조건에서 검사하거나, goto로 반복문 뒤의 레이블로 점프하거나, 대개 가장 깔끔한 방법으로 반복문을 함수로 옮기고 return하면 됩니다.
break는 반복문을 빠져나오나요, 아니면 switch만 빠져나오나요?
가장 가깝게 감싸고 있는 쪽입니다. 반복문 안의 switch 안에 있는 break는 switch만 끝내고 반복문은 계속 돕니다. switch 안에서 반복문을 빠져나오려면 플래그나 goto, 또는 return이 필요합니다.
왜 제 while 반복문에서 continue가 무한 루프를 만드나요?
continue가 조건으로 점프하면서 본문의 나머지를 건너뛰기 때문입니다. 카운터 증가가 본문 끝에 있다면 그것까지 건너뜁니다. for 반복문에서는 갱신이 헤더에 있어 항상 실행되지만, while에서는 그렇지 않습니다. 갱신을 continue 앞으로 옮기거나 for를 쓰세요.