[c++] switch 문에서 변수를 선언 할 수없는 이유는 무엇입니까?

나는 항상 이것을 궁금해했다-왜 switch 문에서 case label 다음에 변수를 선언 할 수 없습니까? C ++에서는 거의 모든 곳에서 변수를 선언 할 수 있으며 (처음 사용에 가깝게 선언하는 것이 좋습니다) 여전히 다음과 같이 작동하지 않습니다.

switch (val)  
{  
case VAL:  
  // This won't work
  int newVal = 42;  
  break;
case ANOTHER_VAL:  
  ...
  break;
}  

위의 오류는 다음과 같습니다 (MSC).

‘case’레이블로 ‘newVal’의 초기화를 건너 뜁니다.

이것은 다른 언어에서도 제한적인 것으로 보입니다. 왜 이런 문제가 있습니까?



답변

Case문장은 레이블 일뿐 입니다. 이것은 컴파일러가 이것을 레이블로 직접 점프하는 것으로 해석한다는 것을 의미합니다. C ++에서 문제는 범위 중 하나입니다. 중괄호는 범위를 switch명령문 내의 모든 것으로 정의합니다 . 이는 초기화를 건너 뛰는 코드로 점프를 더 수행 할 수있는 범위에 있다는 것을 의미합니다.

이를 처리하는 올바른 방법은 해당 case명령문에 특정한 범위를 정의하고 그 안에 변수를 정의하는 것입니다.

switch (val)
{
case VAL:
{
  // This will work
  int newVal = 42;
  break;
}
case ANOTHER_VAL:
...
break;
}


답변

이 질문 원래 [C]와 [C ++]로 동시에 태그되었습니다. 원래 코드는 실제로 C와 C ++ 모두에서 유효하지 않지만 완전히 다른 관련이 없습니다.

  • C ++에서는 case ANOTHER_VAL:레이블 newVal이 초기화를 무시하고 변수 범위로 이동 하기 때문에이 코드는 유효하지 않습니다 . C ++에서 자동 객체의 초기화를 우회하는 점프는 불법입니다. 이 문제의 측면은 대부분의 답변으로 올바르게 해결됩니다.

  • 그러나 C 언어에서 변수 초기화를 우회하는 것은 오류가 아닙니다. 초기화에서 변수의 범위로 뛰어 들어가는 것은 C에서 합법적입니다. 이는 단순히 변수가 초기화되지 않은 상태임을 의미합니다. 원래 코드는 완전히 다른 이유로 C로 컴파일되지 않습니다. case VAL:원래 코드의 레이블 은 variable 선언에 첨부됩니다 newVal. C 언어 선언은 진술이 아닙니다. 라벨을 붙일 수 없습니다. 그리고 이것이이 코드가 C 코드로 해석 될 때 오류를 일으키는 것입니다.

    switch (val)
    {
    case VAL:             /* <- C error is here */
      int newVal = 42;
      break;
    case ANOTHER_VAL:     /* <- C++ error is here */
      ...
      break;
    }

추가 {}블록을 추가하면 이러한 문제가 매우 다르더라도 C ++ 및 C 문제가 해결됩니다. 는 C ++ 측면에서 그것은의 범위를 제한하고 newVal있는지 확인하고, case ANOTHER_VAL:더 이상은 C ++ 문제를 제거하는 범위로 이동합니다. C 측에서는 추가 {}로 복합 문장을 도입하므로 case VAL:레이블을 문장에 적용하여 C 문제를 제거합니다.

  • C의 경우에는 문제없이 쉽게 해결할 수 있습니다 {}. case VAL:레이블 뒤에 빈 문장을 추가하면 코드가 유효 해집니다.

    switch (val)
    {
    case VAL:;            /* Now it works in C! */
      int newVal = 42;
      break;
    case ANOTHER_VAL:
      ...
      break;
    }

    이제 C 관점에서는 유효하지만 C ++ 관점에서는 유효하지 않습니다.

  • 대칭 적으로 C ++의 경우 {}. 없이 문제를 쉽게 해결할 수 있습니다 . 변수 선언에서 이니셜 라이저를 제거하면 코드가 유효 해집니다.

    switch (val)
    {
    case VAL:
      int newVal;
      newVal = 42;
      break;
    case ANOTHER_VAL:     /* Now it works in C++! */
      ...
      break;
    }

    이제 C ++ 관점에서는 유효하지만 C 관점에서는 유효하지 않습니다.


답변

확인. 이것을 명확히하기 위해 선언과는 전혀 관련이 없습니다. “초기화를 통한 점프”(ISO C ++ ’03 6.7 / 3)에만 해당

여기에서 많은 게시물은 선언을 뛰어 넘으면 변수가 선언되지 않을 수 있다고 언급했습니다. 사실이 아닙니다. POD 객체는 이니셜 라이저없이 선언 될 수 있지만 불확실한 값을 갖습니다. 예를 들면 다음과 같습니다.

switch (i)
{
   case 0:
     int j; // 'j' has indeterminate value
     j = 0; // 'j' initialized to 0, but this statement
            // is jumped when 'i == 1'
     break;
   case 1:
     ++j;   // 'j' is in scope here - but it has an indeterminate value
     break;
}

객체가 비 POD 또는 집계 인 경우 컴파일러는 암시 적으로 초기화자를 추가하므로 이러한 선언을 뛰어 넘을 수 없습니다.

class A {
public:
  A ();
};

switch (i)  // Error - jumping over initialization of 'A'
{
   case 0:
     A j;   // Compiler implicitly calls default constructor
     break;
   case 1:
     break;
}

이 제한은 switch 문으로 제한되지 않습니다. ‘goto’를 사용하여 초기화를 건너 뛰는 것도 오류입니다.

goto LABEL;    // Error jumping over initialization
int j = 0;
LABEL:
  ;

약간의 사소한 점은 이것이 C ++과 C의 차이라는 것입니다. C에서는 초기화를 건너 뛰는 것이 오류가 아닙니다.

다른 사람들이 언급했듯이, 솔루션은 변수의 수명이 개별 사례 레이블로 제한되도록 중첩 블록을 추가하는 것입니다.


답변

전체 switch 문은 동일한 범위에 있습니다. 해결하려면 다음을 수행하십시오.

switch (val)
{
    case VAL:
    {
        // This **will** work
        int newVal = 42;
    }
    break;

    case ANOTHER_VAL:
      ...
    break;
}

괄호에 유의 하십시오.


답변

모든 답변과 더 많은 연구를 읽은 후에 몇 가지를 얻습니다.

Case statements are only 'labels'

C에서는 사양에 따라

§6.8.1 표시 문 :

labeled-statement:
    identifier : statement
    case constant-expression : statement
    default : statement

C에는 “labeled declaration”을 허용하는 절이 없습니다. 그것은 언어의 일부가 아닙니다.

그래서

case 1: int x=10;
        printf(" x is %d",x);
break;

컴파일되지 않습니다 참조 http://codepad.org/YiyLQTYw을 . GCC에서 오류가 발생했습니다 :

label can only be a part of statement and declaration is not a statement

조차

  case 1: int x;
          x=10;
            printf(" x is %d",x);
    break;

이것도 컴파일되지 않습니다 . http://codepad.org/BXnRD3bu를 참조 하십시오 . 여기에도 같은 오류가 발생합니다.


C ++에서는 사양에 따라

label-declaration은 허용되지만 label-initialization은 허용되지 않습니다.

http://codepad.org/ZmQ0IyDG를 참조 하십시오 .


그러한 조건에 대한 해결책은 두 가지입니다

  1. {}를 사용하여 새 범위를 사용하십시오.

    case 1:
           {
               int x=10;
               printf(" x is %d", x);
           }
    break;
  2. 또는 레이블이있는 더미 문을 사용하십시오.

    case 1: ;
               int x=10;
               printf(" x is %d",x);
    break;
  3. switch () 전에 변수를 선언하고 요구 사항을 충족하는 경우 case 문에서 다른 값으로 초기화하십시오.

    main()
    {
        int x;   // Declare before
        switch(a)
        {
        case 1: x=10;
            break;
    
        case 2: x=20;
            break;
        }
    }

switch 문으로 더 많은 것들

레이블에 포함되지 않은 명령문은 스위치에 절대로 쓰지 마십시오.

switch(a)
{
    printf("This will never print"); // This will never executed

    case 1:
        printf(" 1");
        break;

    default:
        break;
}

http://codepad.org/PA1quYX3을 참조 하십시오 .


답변

당신은 이것을 할 수 없기 때문에 case레이블은 실제로 포함 블록의 시작점이므로이 .

이것은 Duff의 장치에 의해 가장 명확하게 설명됩니다 . Wikipedia의 코드는 다음과 같습니다.

strcpy(char *to, char *from, size_t count) {
    int n = (count + 7) / 8;
    switch (count % 8) {
    case 0: do { *to = *from++;
    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);
    }
}

case레이블이 블록 경계를 완전히 무시 하는 방법에 주목하십시오 . 예, 이것은 악합니다. 그러나 이것이 코드 예제가 작동하지 않는 이유입니다. A를 점프 case라벨은 사용하는 것과 같습니다goto 당신은 생성자와 지역 변수를 통해 점프 할 수 없습니다, 그래서.

다른 여러 포스터에서 알 수 있듯이 자신의 블록을 넣어야합니다.

switch (...) {
    case FOO: {
        MyObject x(...);
        ...
        break;
    }
    ...
 }


답변

지금까지 대부분의 답장은 한 측면에서 잘못되었습니다 . case 문 다음에 변수 선언 할 수는 있지만 초기화 할 수는 없습니다 .

case 1:
    int x; // Works
    int y = 0; // Error, initialization is skipped by case
    break;
case 2:
    ...

앞에서 언급했듯이이 문제를 해결하는 좋은 방법은 중괄호를 사용하여 사례의 범위를 만드는 것입니다.