goto 문

goto 문

조건 없이 제어 흐름을 다른 위치로 옮기는 goto 문을 알아볼게요. 다른 문장으로는 도저히 원하는 위치로 제어를 옮길 수 없을 때 쓰는 문장입니다.

출처: cppreference

본문

어디에 쓰나요?

보통 gotobreakcontinue로는 빠져나가기 어려운 다중(중첩) 루프를 한 번에 탈출하거나, 함수 안에서 아주 특수한 위치로 점프해야 할 때 사용해요. 다만 필요할 때만 쓰는 게 좋습니다.

문법

attr (optional) goto label ;

설명

goto 문은 label로 지정된 위치로 제어를 옮겨요. goto 문은 참조하는 레이블과 반드시 같은 함수 안에 있어야 하며, 레이블보다 앞에 있어도 뒤에 있어도 괜찮습니다.

제어가 어떤 자동 변수(automatic variable)의 스코프를 벗어나는 경우, 예를 들어 그 변수의 선언 지점보다 앞으로 점프하거나 변수가 스코프된 복합문 밖으로 점프하는 경우에는, 스코프를 벗어난 모든 변수의 소멸자(destructor)가 그 변수들이 생성된 순서의 반대 순서로 호출돼요.

goto 문은 제어 흐름이 제한된 문장(control-flow-limited statement) 안으로 제어를 옮길 수는 없지만, 그런 문장 밖으로 옮기는 것은 가능합니다(위에서 말한 자동 변수 스코프 규칙은 그대로 따릅니다).

제어가 어떤 자동 변수의 스코프 안으로 들어가는 경우 — 예를 들어 선언문을 건너뛰어 앞으로 점프하는 경우 — 다음 유형에 해당하는 변수만 있을 때 제외하면 프로그램은 ill-formed(컴파일 불가)가 돼요:

  • 초기화 없이 선언된 스칼라 타입
  • 기본 생성자와 소멸자가 trivial하고 초기화 없이 선언된 클래스 타입
  • 위 유형의 cv-한정(cv-qualified) 버전
  • 위 유형의 배열

(참고: 이 규칙은 모든 형태의 제어 이동에 동일하게 적용됩니다.)

참고 사항

C 언어에서는 goto 문에 제약이 더 적어서, 가변 길이 배열이나 가변 수정 포인터 외에는 어떤 변수의 스코프로든 들어갈 수 있어요.

키워드

goto

예제

이 예제에서는 ObjectTrivial 두 구조체를 만들어 소멸자 호출 시점과 스코프 진입 규칙을 확인해 볼게요.

#include <iostream>

struct Object
{
    // non-trivial destructor
    ~Object() { std::cout << 'd'; }
};

struct Trivial
{
    double d1;
    double d2;
}; // trivial ctor and dtor

int main()
{
    int a = 10;
    
    // loop using goto
label:
    Object obj;
    std::cout << a << ' ';
    a -= 2;
    
    if (a != 0)
        goto label;  // jumps out of scope of obj, calls obj destructor
    std::cout << '\n';
    
    // goto can be used to efficiently leave a multi-level (nested) loops
    for (int x = 0; x < 3; ++x)
        for (int y = 0; y < 3; ++y)
        {
            std::cout << '(' << x << ',' << y << ") " << '\n';
            if (x + y >= 3)
                goto endloop;
        }
    
endloop:
    std::cout << '\n';
    
    goto label2; // jumps into the scope of n and t
    
    [[maybe_unused]] int n; // no initializer
    
    [[maybe_unused]] Trivial t; // trivial ctor/dtor, no initializer
    
//  int x = 1;   // error: has initializer
//  Object obj2; // error: non-trivial dtor
    
label2:
    {
        Object obj3;
        goto label3; // jumps forward, out of scope of obj3
    }
    
label3:
    std::cout << '\n';
}

출력:

10 d8 d6 d4 d2
(0,0)
(0,1)
(0,2)
(1,0)
(1,1)
(1,2)

d
d

더 알아보기 (Learn more)