26. goto
1. 키워드
goto
- 레이블(Label)
2. goto
로 프로그램의 흐름을 원하는 대로 바꾸기
- 보통 프로그램을 작성하다보면 중간의 코드는 무시하고 원하는 부분으로 건너뛰어야 하는 상황이 생기기도 한다.
- 이런 경우에 사용하는 제어문이
goto
이다.
스파게티 코드
- 스파게티 코드는
goto
를 과도하게 사용해서 프로그램의 흐름이 마치 스파게티 면발처럼 꼬여있다는데서 붙여진 이름이다. - 그래서 스파게티 코드는 가독성이 떨어지고 유지보수가 매우 힘들다.
3. goto
와 레이블 사용하기
goto
는 레이블을 지정해서 사용한다.- 레이블은
:
(콜론)을 붙이며 레이블 이름을 짓는 규칙은 변수와 같다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int num1;
scanf("%d", &num1);
if (num1 == 1) // num1이 1이면
goto ONE; // 레이블 ONE으로 즉시 이동
else if (num1 == 2) // num1이 2이면
goto TWO; // 레이블 TWO로 즉시 이동
else // 1도 아니고 2도 아니면
goto EXIT; // 레이블 EXIT로 즉시 이동
ONE: // 레이블 ONE
printf("1입니다.\n");
goto EXIT; // 레이블 EXIT로 즉시 이동
TWO: // 레이블 TWO
printf("2입니다.\n");
goto EXIT; // 레이블 EXIT로 즉시 이동
EXIT: // 레이블 EXIT
return 0;
}
// 1 (입력)
// 1입니다.
goto
에 레이블을 지정하면 중간에 있는 코드는 무시하고 해당 레이블로 즉시 이동한다.
if (num1 == 1) // num1이 1이면
goto ONE; // 레이블 ONE으로 즉시 이동
else if (num1 == 2) // num1이 2이면
goto TWO; // 레이블 TWO로 즉시 이동
else // 1도 아니고 2도 아니면
goto EXIT; // 레이블 EXIT로 즉시 이동
- 다음과 같이 레이블을 지정한 뒤 실행할 코드를 작성한다.
- 여기서는
"1입니다."
를 출력한 뒤"2입니다."
가 출력되지 않도록 다시 레이블EXIT
로 건너뛴다.
ONE : // 레이블 ONE
printf("1입니다.\n");
goto EXIT; // 레이블 EXIT로 즉시 이동
TWO : // 레이블 TWO
printf("2입니다.\n");
goto EXIT; // 레이블 EXIT로 즉시 이동
EXIT : // 레이블 EXIT
return 0;
- 예제 코드를
if
,else
로만 표현하면 다음과 같이 된다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int num1;
scanf("%d", &num1);
if (num1 == 1) // num1이 1이면
printf("1입니다.\n");
else if (num1 == 2) // num1이 2이면
printf("2입니다.\n");
return 0;
}
4. 중첩 루프 빠져나오기
- 다음은 변수
num1
이20
이 되면 중첩 루프를 빠져나온다.
#include <stdio.h>
#include <stdbool.h>
int main()
{
int num1 = 0;
bool exitOuterLoop = false; // 바깥쪽 루프를 빠져나올지 결정하는 변수
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
if (num1 == 20) // num1이 20이라면
{
exitOuterLoop = true; // 바깥쪽 루프도 빠져나가겠음
break; // 안쪽 루프를 끝냄
}
num1++;
}
if (exitOuterLoop == true) // 바깥쪽 루프도 빠져나오겠다고 결정했으면
break; // 바깥쪽 루프를 끝냄
}
printf("%d\n", num1); // 20
return 0;
}
// 20
break
의 가장 큰 특징은 현재 루프만 끝낸다는 점이다.- 따라서 중첩 루프의 안쪽 루프에서
break
를 사용하면 안쪽 루프만 끝낼 뿐 바깥쪽 루프는 계속 반복된다. - 그러다 보니 여기서는 변수
exitOuterLoop
를 사용해서 바깥쪽 루프도 끝내겠다고 결정을 해주고 있다.
- 다음과 같이
goto
를 사용하면 간단하게 빠져나올 수 있다.
#include <stdio.h>
int main()
{
int num1 = 0;
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
if (num1 == 20) // num1이 20이라면
goto EXIT; // 레이블 EXIT로 즉시 이동
num1++;
}
}
EXIT: // 레이블 EXIT
printf("%d\n", num1); // 20
return 0;
}
// 20
- 변수
num1
이20
이 되면goto
를 사용하여 레이블EXIT
로 즉시 이동한다. - 따라서 안쪽과 바깥쪽 루프를
break
로 끝낼 필요가 없다. - 여기서는
for
반복문을 예로 들었지만while
,do while
로 된 중첩 루프도 빠져나올 수 있다.
5. goto
와 에러 처리 패턴
- 예를 들어 가상의 통지서가 있는데 이 통지서를 집집마다 방문하여
"자가 주택을 소유한 30대 남자"
에게 전달한다고 해보자.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int gender; // 성별: 남자 1, 여자 2
int age; // 나이
int isOwner; // 주택 소유 여부: 자가 1, 전월세 0
scanf("%d %d %d", &gender, &age, &isOwner);
printf("안녕하세요.\n");
printf("문을 연다.\n");
if (gender == 2) // 여자라면
{
printf("안녕히계세요.\n"); // 중복 코드
printf("문을 닫는다.\n"); // 중복 코드
return 0; // 프로그램 종료
}
if (age < 30) // 30세 미만이라면
{
printf("안녕히계세요.\n"); // 중복 코드
printf("문을 닫는다.\n"); // 중복 코드
return 0; // 프로그램 종료
}
if (isOwner == 0) // 전월세라면
{
printf("안녕히계세요.\n"); // 중복 코드
printf("문을 닫는다.\n"); // 중복 코드
return 0; // 프로그램 종료
}
return 0; // 프로그램 종료
}
- 문을 열고 성별, 나이, 주택 소유 여부를 확인한 뒤
"자가 주택을 소유한 30대 남자"
에 하나라도 해당하지 않는다면 인사를 한 뒤 문을 닫는다. - 이렇게 주어진 조건에 해당하지 않아서 프로그램을 종료하는 상황을 에러라고 할 수 있다.
- 코드를 잘 보면 에러 조건마다 인사하는 코드와 문을 닫는 코드가 중복된다.
goto
를 사용하면 에러 상황 때 항상 실행해야 하는 중복 코드를 하나로 모을 수 있다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int gender; // 성별: 남자 1, 여자 2
int age; // 나이
int isOwner; // 주택 소유 여부: 자가 1, 전월세 0
scanf("%d %d %d", &gender, &age, &isOwner);
printf("안녕하세요.\n");
printf("문을 연다.\n");
if (gender == 2)
goto EXIT; // 에러가 발생했으므로 EXIT로 이동
if (age < 30)
goto EXIT; // 에러가 발생했으므로 EXIT로 이동
if (isOwner == 0)
goto EXIT; // 에러가 발생했으므로 EXIT로 이동
EXIT:
printf("안녕히계세요.\n"); // 에러 처리 코드를
printf("문을 닫는다.\n"); // 한 번만 사용함
return 0; // 프로그램 종료
}
- 이제 조건이 맞지 않아 에러가 발생할 때마다
EXIT
로 이동하여 에러 처리 코드를 실행한다. - 즉, 종료 직전에 항상 실행해야 하는 에러 처리 코드들은 한 곳에 모아놓고
goto
를 사용하면 중복 코드를 없앨 수 있고, 코드의 의도도 좀 더 명확해진다.