case 문에서 {} 사용. 왜?


101

문 에서 {및 사용의 요점은 무엇입니까 ? 일반적으로 명령문 에 몇 줄이 있더라도 모든 줄이 실행됩니다. 이것은 단지 구형 / 최신 컴파일러에 관한 규칙입니까, 아니면 그 뒤에 뭔가가 있습니까?}casecase

int a = 0;
switch (a) {
  case 0:{
    std::cout << "line1\n";
    std::cout << "line2\n";
    break;
  }
}

int a = 0;
switch (a) {
  case 0:
    std::cout << "line1\n";
    std::cout << "line2\n";
    break;
}

57
한 가지 용도는 case 문에 선언 된 변수의 범위를 제한하는 것입니다.
Abhishek Bansal 2013


1
들여 쓰기도 너무 많습니다. case는 switch 문의 블록 내에있는 레이블 일뿐입니다. 추가 중첩을 도입하지 않으므로 switch키워드 와 정렬해야 하며 두 번째 예제에서 포함 된 문은 한 번만 들여 쓰기됩니다. 당신이 후 드 들여 쓰기 어색한 네 개의 공간을 얼마나 주 break;.
Kaz

Jack의 의견이 지적하고 약간의 미묘함을 놓치기 때문에 받아 들여지는 대답은 부분적으로 만 정확합니다 .
Shafik Yaghmour

FYI처럼 : C ++가 아닌 C (심지어 C11)에서는 선언에 레이블을 지정할 수 없습니다. 구문 범주에 속하지 않습니다 statement. C ++에서는 가능합니다 (구문 범주의 한 구성 요소는 statement입니다 declaration statement).
Jonathan Leffler 2013

답변:


195

{}새로운 블록 나타낸다 범위 .

다음과 같은 매우 인위적인 예를 고려하십시오.

switch (a)
{
    case 42:
        int x = GetSomeValue();
        return a * x;
    case 1337:
        int x = GetSomeOtherValue(); //ERROR
        return a * x;
}

x범위에 이미 정의되어 있기 때문에 컴파일러 오류가 발생 합니다.

이를 자체 하위 범위로 분리 x하면 switch 문 외부에서 선언 할 필요가 없습니다 .

switch (a)
{
    case 42: {
        int x = GetSomeValue();
        return a * x; 
    }
    case 1337: {
        int x = GetSomeOtherValue(); //OK
        return a * x; 
    }
}

11
실제로 IMO는 변수 x의 두 번째 선언을 건너 뛰더라도 컴파일러 오류가 발생합니다.
Abhishek Bansal 2013

1
이 스타일을 과도하게 사용하고 switch 문 안에 큰 블록을 넣으면 사례를 따르기 어렵습니다. 나는 진술을 작게 유지하는 것을 선호합니다.
masoud

2
@MatthieuM. 나는 MS Visual Studio 2010이 Abhishek이 나타내는 동작을 가질 것이라는 사실을 알고 있습니다 : 그것은 케이스 안에 어떤 변수 선언도 컴파일하지 않을 것입니다 (중괄호를 사용하여 그 케이스 안에서 새로운 범위를 나타내지 않는 한). 그것이 표준과 일치하는지 여부는 모르겠습니다.
KRyan 2013

1
@KRyan : 그렇지는 않지만 훨씬 더 안전한 대안이어서 이것을 시행하는 것에 대해 비난 할 수 없습니다.
Matthieu M.

6
표준의 섹션 6.7 (3) (2005 초안 번호 지정)은 초기화를 건너 뛸 수 없으므로 케이스 블록에서 초기화를 가질 수 없다고 지정합니다.
Jack Aidley 2013

23

TL; DR

이니셜 라이저 또는 케이스 내에서 사소하지 않은 개체를 사용하여 변수를 선언 할 수있는 유일한 방법 은 루프 또는 if 문과 같은 자체 범위가있는 다른 제어 구조를 사용하여 블록 범위 를 도입하는 {}것 입니다.

피투성이 세부 사항

우리는 볼 수있는 경우가 바로되어 제표 표시라벨 와 함께 사용 고토 문 ( 이은으로 덮여 표준 초안 C ++ 6.1 레이블 문 ) 우리는 절에서 볼 수있는 6.73 많은 경우에 허용되지 않습니다 점프 선언을 통과하는 것이 , 초기화 포함 :

블록으로 전송할 수 있지만 초기화를 통해 선언을 우회하는 방식은 아닙니다. 자동 저장 기간이있는 변수가 범위 내에 있지 않은 지점에서 범위 내에있는 지점으로 87 을 점프하는 프로그램 은 변수에 스칼라 유형, 사소한 기본 생성자가있는 클래스 유형 및 사소한 소멸자가 없으면 형식이 잘못되었습니다. 이 유형 중 하나의 cv 규정 버전 또는 이전 유형 중 하나의 배열이며 이니셜 라이저없이 선언됩니다 (8.5).

이 예제를 제공합니다.

void f() {
 // ...
 goto lx; // ill-formed: jump into scope of a

 ly:
  X a = 1;
 // ...
 lx:
  goto ly; // OK, jump implies destructor
          // call for a followed by construction
          // again immediately following label ly
}

여기에는 몇 가지 미묘한 점이 있습니다. 초기화가없는 스칼라 선언건너 뛸 수 있습니다. 예를 들면 다음과 같습니다.

switch( n ) 
{
    int x ;
    //int x  = 10 ; 
    case 0:
      x = 0 ;
      break;
    case 1:
      x = 1 ;
      break;
    default:
      x = 100 ;
      break ;
}

완벽하게 유효합니다 ( 라이브 예제 ). 물론 각 경우에 동일한 변수를 선언하려면 각각 고유 한 범위가 필요하지만 switch 문 외부에서도 동일한 방식으로 작동 하므로 큰 놀라지 않아야합니다.

초기화를 넘어서 점프를 허용하지 않는 이유에 대해서는 약간 다른 문제를 다루지 만 결함 보고서 467자동 변수에 대한 합리적인 사례를 제공 합니다 .

[...] 자동 변수는 명시 적으로 초기화되지 않은 경우 트랩 표현을 포함하여 불확실한 ( "가비지") 값을 가질 수 있습니다. [...]

스위치 내에서 범위 를 여러 경우로 확장하는 경우를 살펴 보는 것이 더 흥미로울 것입니다 . 가장 유명한 예는 다음과 같은 Duff의 장치 일 것입니다 .

void send( int *to, const int *from, int  count)
{
        int n = (count + 7) / 8;
        switch(count % 8) 
        {
            case 0: do {    *to = *from++;   // <- Scope start
            case 7:         *to = *from++;
            case 6:         *to = *from++;
            case 5:         *to = *from++;
            case 4:         *to = *from++;
            case 3:         *to = *from++;
            case 2:         *to = *from++;
            case 1:         *to = *from++;
                        } while(--n > 0);    // <- Scope end
        }
}

6

결과 소멸자 (또는 범위 충돌)가있는 변수 선언을 case절 에 삽입 할 수있는 습관입니다 . 그것을 보는 또 다른 방법은 그들이 원하는 언어로 작성하는 것입니다. 모든 흐름 제어는 문장의 시퀀스가 ​​아니라 블록으로 구성됩니다.


4

이것이 기본적인 컴파일러 제한인지 확인하면 무슨 일이 일어나는지 궁금해 할 것입니다.

int c;
c=1;

switch(c)
{
    case 1:
    //{
        int *i = new int;
        *i =1;
        cout<<*i;
        break;
    //}

    default : cout<<"def";
}

그러면 오류가 발생합니다.

error: jump to case label [-fpermissive]
error:   crosses initialization of int* i

이것은 다음을 수행하지 않습니다.

int c;
c=1;

switch(c)
{
    case 1:
    {
        int *i = new int;
        *i =1;
        cout<<*i;
        break;
    }

    default : cout<<"def";
}

1

스위치에서 대괄호를 사용하는 것은 Rotem이 말한 것처럼 새로운 범위 블록을 나타냅니다.

그러나 읽을 때 명확하게 할 수도 있습니다. 조건부 중단이있을 수 있으므로 케이스가 중지되는 위치를 파악합니다.


0

이유는 다음과 같습니다.

  1. 가독성, 범위가 지정된 섹션으로 각 사례를 시각적으로 향상시킵니다.
  2. 여러 스위치 케이스에 대해 동일한 이름으로 다른 변수를 선언합니다.
  3. 마이크로 최적화-케이스의 범위를 벗어나 자마자 파괴하려는 정말 값 비싼 리소스 할당 변수의 범위 또는 "GOTO"명령 사용에 대한 더 스파게티 시나리오.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.