Skip to content

26. goto


1. 키워드

  • goto
  • 레이블(Label)


2. goto로 프로그램의 흐름을 원하는 대로 바꾸기

  • 보통 프로그램을 작성하다보면 중간의 코드는 무시하고 원하는 부분으로 건너뛰어야 하는 상황이 생기기도 한다.
  • 이런 경우에 사용하는 제어문이 goto이다.


001


스파게티 코드

  • 스파게티 코드는 goto를 과도하게 사용해서 프로그램의 흐름이 마치 스파게티 면발처럼 꼬여있다는데서 붙여진 이름이다.
  • 그래서 스파게티 코드는 가독성이 떨어지고 유지보수가 매우 힘들다.


3. goto와 레이블 사용하기

  • 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. 중첩 루프 빠져나오기

  • 다음은 변수 num120이 되면 중첩 루프를 빠져나온다.


#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


  • 변수 num120이 되면 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를 사용하면 중복 코드를 없앨 수 있고, 코드의 의도도 좀 더 명확해진다.

References