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


944

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

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

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

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

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



다음은 일반적으로 스위치 문 및 레이블 (ABC :)에 대한 유용한 정보입니다.
Etherealone

4
'왜 선언되지 않고 스위치 문에서 변수를 초기화 할 수 없습니까?'라고 말하고 변수를 선언하면 MSVC에서 경고 만 표시합니다.
ZoomIn

답변:


1141

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

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

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

94
새로운 범위를 여는 것과 비교하여 코드의 가독성과 일관성을 선호합니다. 예전에는 "추가"스택 프레임을 자동으로 얻었을 수도 있지만 이제는 최적화 된 컴파일러의 경우에는 해당되지 않습니다.
Tall Jeff

10
나는 Jeff에 동의합니다. 대부분의 사람들이 사용하는 들여 쓰기 스타일로 인해 switch 문을 읽을 때 범위를 "가정"하기가 너무 쉽습니다. 내 자신의 스타일은 항상 한 줄 이상인 경우 각 사례 / 기본값에 대해 새 범위를 여는 것입니다.
입찰

39
workmad3-새로운 변수를 선언하지 않으면 새로운 스택 프레임을 생성하는 C ++ 컴파일러를 찾을 수 있습니까? 잠깐 걱정했지만 G ++ 3.1, Visual C ++ 7 또는 Intel C ++ 8 중 어느 것도 변수를 선언하지 않는 새로운 범위에 대한 코드를 생성하지 않습니다.
Chris Jefferson

10
@ workmad3는 새로운 중괄호 블록을 입력해도 새로운 스택 프레임이 발생하지 않습니다 stackoverflow.com/questions/2759371/…
MTVS

3
@TallJef 나는 당신이 말하는 '오래된 일들'을 모른다. 40 년 동안 메소드가 입력 될 때 메소드의 모든 스택 공간 할당 되지 않은 컴파일러를 본 적이 없습니다 .
user207421

331

이 질문 원래 [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 관점에서는 유효하지 않습니다.


4
@AnT : C ++를 수정 한 것이 C에 적용되지 않는 이유를 이해합니다. 그러나 처음에는 초기화를 건너 뛰는 C ++ 문제를 해결하는 방법을 이해할 수 없습니까? 여전히 newVal점프 할 때 의 선언과 할당을 건너 뛰지 ANOTHER_VAL않습니까?
legends2k

13
@ legends2k : 예, 여전히 건너 뜁니다. 그러나 "문제가 해결되었습니다"라고 말하면 C ++ 컴파일러 오류가 해결된다는 의미입니다 . C ++에서는 initializer 로 스칼라 선언을 건너 뛰는 것은 불법 이지만, initializer없이 스칼라 선언을 건너 뛰는 것이 좋습니다. 에서 case ANOTHER_VAL:포인트 변수 newVal하지만 불확정 값으로 보이는 것이다.
AnT

3
매혹적인. §A9.3: Compound StatementK & R C (제 2 판)를 읽은 후이 질문을 찾았습니다 . 항목은의 기술적 정의에 언급 한 화합물 문 이다 {declaration-list[opt] statement-list[opt]}. 나는 선언이 성명라고 생각했기 때문에, 혼란, 나는 그것을 보았다 바로이 질문에, 차이가 분명해집니다 실제로 말했다 예를 들어 발견 나누기 프로그램을. C에 대한 또 다른 해결책 은 선언문 앞에 다른 문장 (아마도 널 문장입니까?)을 넣어서 표시된 문장 을 만족시키는 것이라고 생각합니다 .
Braden Best

방금 제안한 null 문 솔루션이 이미 귀하의 답변에 있음을 알았습니다. 그럼 신경 쓰지 마
Braden Best

3
빈 명령문을 추가하는 수정은 C99 이후에만 작동한다는 점에 주목할 가치가 있습니다. C89에서 변수는 둘러싸는 블록의 시작 부분에 선언되어야합니다.
Arthur Tacca '12

136

확인. 이것을 명확히하기 위해 선언과는 전혀 관련이 없습니다. "초기화를 통한 점프"(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에서는 초기화를 건너 뛰는 것이 오류가 아닙니다.

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


2
"초기화 점프 오류"??? 내 GCC가 아닙니다. j 아래 레이블을 사용할 때 "j는 단일화로 사용될 수 있습니다"경고를 표시 할 수 있지만 오류는 없습니다. 그러나 스위치의 경우 오류 (경고 오류가 아닌 약한 경고)가 있습니다.
Mecki

9
@Mecki : C ++에서는 불법입니다. ISO C ++ '03-6.7 / 3 : "... 자동 저장 기간이있는 지역 변수가 범위 내에 있지 않은 지점에서 변수에 POD 유형이없는 한 범위 내에 있지 않은 지점으로 이동하는 프로그램 (3.9) 및 이니셜 라이저 (8.5)없이 선언되었습니다. "
Richard Corden

1
예, 그러나 C에서는 불법이 아닙니다 (적어도 gcc는 그렇지 않다고 말합니다). j는 초기화되지 않지만 (임의의 숫자가 있음) 컴파일러가 컴파일합니다. 그러나 switch 문의 경우 컴파일러는 컴파일조차하지 않으며 goto / label case와 switch case의 차이점을 알 수 없습니다.
Mecki

8
@Mecki : 일반적으로 단일 컴파일러 동작이 언어에서 실제로 허용되는 whtat을 반드시 반영하지는 않습니다. C'90과 C'99를 모두 확인했으며 두 표준 모두 switch 문에서 초기화를 통한 점프 예제가 포함되어 있습니다.
Richard Corden

38

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

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

    case ANOTHER_VAL:
      ...
    break;
}

괄호에 유의 하십시오.


30

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

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을 참조 하십시오 .


2
C 문제를 올바르게 설명했습니다. 그러나 C ++로 레이블이 지정된 초기화가 허용되지 않는다는 주장은 완전히 사실이 아닙니다. C ++에서 레이블이 지정된 초기화에는 아무런 문제가 없습니다. C ++에서 허용하지 않는 것은 변수 초기화를 변수 범위로 건너 뛰는 것 입니다. 따라서 C 관점에서 문제는 레이블에 관한 것이며 올바르게 설명했습니다. 그러나 C ++ 관점에서 볼 때 문제는 레이블입니다. aacase VAL:case ANOTHER_VAL:
AnT

C ++에서 C와 달리 선언은 명령문의 서브 세트입니다.
Keith Thompson

20

당신은 이것을 할 수 없기 때문에 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; 
    }
    ...
 }

1
이 Duff의 장치 구현은 버그를 매우 느리게 만듭니다. count는 int 유형이므로 %는 실제 나누기 / 모듈로 연산을 수행해야합니다. count를 부호없는 것으로 만들거나 더 나은 수는 항상 count / t를 count / t로 사용하면 문제가 사라집니다.
R .. GitHub 중지 지원 얼음

1
@R .. : 뭐?! 2의 보수 시스템에서 부호는 2의 거듭 제곱 (모듈의 AND에 불과 함)에 의해 모듈로에 영향을 미치지 않으며 프로세서 아키텍처에 산술 오른쪽 시프트 연산이있는 한 2의 거듭 제곱에 영향을 미치지 않습니다. ( SARx86에서, SHR부호없는 시프트에 대한 것입니다).
Chris Jester-Young

@Chris : 컴파일러가 "하단 비트의 AND 만"이 유지되지 않는 음의 값을 허용해야 할 때를 의미한다고 생각합니다. 예를 들어, -1 % 8은 g ++를 사용하여이 2의 보수 시스템에서 -1을 제공합니다 (이 경우 부호는 5.6 / 4에 따라 구현이 정의 됨).

3
@Chris : R이 그 영향을 과장하고 있음에 동의합니다. 나는 당신의 의견 만 보았고 간단하고 충분하지 않다는 것을 알았습니다.

1
또한 원래 Wikipedia 코드는 메모리 매핑 출력으로 데이터를 전송하기위한 것입니다. 여기에는 언급되지 않았으며 모든 바이트가 동일한 "to"위치로 복사되기 때문에 이상하게 보입니다. postfix ++를 to에 추가하거나 유스 케이스를 언급하면 ​​메모리 매핑 IO에 대한 문제를 해결할 수 있습니다. 원래 질문의 전연 :-).
Peter

16

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

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

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


1
Mr. 32 오류가 무엇인지 오해했습니다 : 예, 컴파일하지는 않지만 스위치 내부에 변수를 선언했기 때문이 아닙니다. 오류는 명령문에서 변수를 선언하려고하기 때문에 발생합니다. C에서는 불법입니다.
MrZebra

1
C90와 C의 최신 버전에서 법적으로 지금 일
Jeegar 파텔

12

내가 가장 좋아하는 사악한 스위치 트릭은 if (0)을 사용하여 원하지 않는 케이스 레이블을 건너 뛰는 것입니다.

switch(val)
{
case 0:
// Do something
if (0) {
case 1:
// Do something else
}
case 2:
// Do something in all cases
}

그러나 매우 악합니다.


아주 좋아요 이유의 예 : 사례 0과 사례 1은 예를 들어 변수를 다르게 초기화 한 후 사례 2에서 사용됩니다.
hlovdal

1
사례 0과 사례 1을 모두 사례 2를 거치게하려면 (사례 0은 사례 1을 거치지 않음). 그것이 정말로 유용한 지 모르지만 확실하게 작동합니다.
Petruza

1
goto코드를 난독 화하지 않고 필요한 레이블로 바로 이동할 수 있습니다.
SomeWittyUsername

10

이 시도:

switch (val)
{
    case VAL:
    {
        int newVal = 42;
    }
    break;
}

7

새 블록을 시작 하면 switch 문 내에서 변수를 선언 할 수 있습니다 .

switch (thing)
{ 
  case A:
  {
    int i = 0;  // Completely legal
  }
  break;
}

그 이유는 로컬 변수를 저장하기 위해 스택에 공간을 할당하고 회수하는 것과 관련이 있습니다.


1
변수를 선언 할 수는 있지만 초기화 할 수는 없습니다. 또한 문제가 어쨌든 스택 및 로컬 변수와 관련이 없음을 확신합니다.
Richard Corden

6

치다:

switch(val)
{
case VAL:
   int newVal = 42;
default:
   int newVal = 23;
}

break 문이 없으면 newVal이 두 번 선언되고 런타임까지 수행되는지 알 수 없습니다. 제 생각 엔 한계는 이런 종류의 혼란 때문입니다. newVal의 범위는 무엇입니까? 컨벤션은 스위치 블록 전체 (브레이스 사이)가 될 것을 지시합니다.

저는 C ++ 프로그래머는 아니지만 C에서는 :

switch(val) {
    int x;
    case VAL:
        x=1;
}

잘 작동합니다. 스위치 블록 내부에 변수를 선언하는 것이 좋습니다. 케이스 가드 후에 선언하는 것은 아닙니다.


3
@ Mr.32 : 실제로 귀하의 예는 printf가 실행되지 않는다는 것을 보여 주지만이 경우 int x는 명령문이 아니라 선언이며 x가 선언되며 함수 환경이 스택 될 때마다 공간이 예약됩니다. 참조 : codepad.org/4E9Zuz1e
Petruza

질문의 제목을 읽을 때 이것을 찾을 것으로 예상되었습니다. 질문은 "case :"레이블 내에서 변수를 선언하는 것이 아니라 switch 문에서 변수를 선언하기 때문입니다. 그리고 오직 당신 (그리고 VictorH, 답을 강조)은 실제로 switch 문에서 변수에 대해 이야기했습니다.
중단

4

스위치의 전체 섹션은 단일 선언 컨텍스트입니다. 그런 case 문에서 변수를 선언 할 수 없습니다. 대신 이것을 시도하십시오 :

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

변수를 선언 할 수는 있지만 초기화 할 수는 없습니다.
Richard Corden

@Richard Corden 초기화가 작동한다고 확신합니다. 여전히 초기화 할 수 없다고 주장합니까?
chux-복원 모니카

3

코드에 "int newVal = 42"라고 표시되면 newVal이 초기화되지 않은 것으로 예상됩니다. 그러나이 문장 (당신이하고있는 일)을 넘어 가면 정확히 일어난 일입니다-newVal은 범위 내에 있지만 할당되지 않았습니다.

그것이 당신이 정말로 의도 한 것이라면, 언어는 "int newVal; newVal = 42;"라고 말함으로써 그것을 명시 적으로 만들어야합니다. 그렇지 않으면 newVal의 범위를 단일 사례로 제한 할 수 있습니다.

동일한 예제를 고려하지만 "const int newVal = 42;"


3

난 그냥 슬림요점 을 강조하고 싶었다 . 스위치 구성은 전체 일등 시민 범위를 만듭니다. 따라서 추가 대괄호 쌍 없이 첫 번째 사례 레이블 앞에 switch 문에서 변수를 선언하고 초기화 할 수 있습니다 .

switch (val) {  
  /* This *will* work, even in C89 */
  int newVal = 42;  
case VAL:
  newVal = 1984; 
  break;
case ANOTHER_VAL:  
  newVal = 2001;
  break;
}

-1 여기에서 int newVal = 42; 절대로 처형되지 않습니다. 이 codepad.org/PA1quYX3
Jeegar Patel

4
선언 int newVal 실행되지만 = 42할당 실행되지 않습니다 .
Petruza

3

지금까지 C ++에 대한 답변이있었습니다.

C ++의 경우 초기화를 건너 뛸 수 없습니다. C에서는 가능하지만 C에서는 선언이 진술이 아니므로 대소 문자 레이블 뒤에 문장이 와야합니다.

따라서 유효하지만 추악한 C, 유효하지 않은 C ++

switch (something)
{
  case 1:; // Ugly hack empty statement
    int i = 6;
    do_stuff_with_i(i);
    break;
  case 2:
    do_something();
    break;
  default:
    get_a_life();
}

반대로 C ++에서 선언은 명령문이므로 다음은 유효한 C ++, 유효하지 않은 C입니다.

switch (something)
{
  case 1:
    do_something();
    break;
  case 2:
    int i = 12;
    do_something_else();
}

1
두 번째 예제는 유효하지 않은 C ++입니다 (vc2010 및 gcc 4.6.1에서 테스트 C6.1 초기화 부분을 건너 뛸 수 없습니다. gcc 오류 메시지는 'int i'의 교차 초기화
zhaorufei

3

이것이 괜찮다는 것에 흥미가 있습니다.

switch (i)  
{  
case 0:  
    int j;  
    j = 7;  
    break;  

case 1:  
    break;
}

...하지만 이것은 아닙니다 :

switch (i)  
{  
case 0:  
    int j = 7;  
    break;  

case 1:  
    break;
}

나는 수정이 충분히 간단하다는 것을 알지만, 첫 번째 예제가 컴파일러를 방해하지 않는 이유를 아직 이해하지 못한다. 앞서 언급했듯이 (2 년 전 hehe), 논리에도 불구하고 선언 이 오류를 유발하는 것은 아닙니다. 초기화가 문제입니다. 변수가 초기화되어 다른 행에서 선언되면 컴파일됩니다.


1
gcc 4.2에서는 "잘못 : 'int'앞의 예상 된 표현"에 대해서는 처음에는 좋지 않다. Peter와 Mr.32가 말했듯이 "case 0 :; int j; ..."와 "case 0 :; int j = 7; ..."은 모두 작동합니다. C의 문제는 "case <label> : declaration"이 유효한 C 구문이 아니라는 것입니다.
dubiousjim

3

나는 이 질문에 대한 원래 답변을 썼습니다 . 그러나 내가 끝내면 대답이 닫 혔음을 알았습니다. 그래서 여기에 게시했습니다. 표준에 대한 참조를 좋아하는 사람이 도움이 될 것입니다.

문제의 원본 코드 :

int i;
i = 2;
switch(i)
{
    case 1: 
        int k;
        break;
    case 2:
        k = 1;
        cout<<k<<endl;
        break;
}

실제로 두 가지 질문이 있습니다.

1. 왜 case레이블 뒤에 변수를 선언 할 수 있습니까?

C ++ 레이블의 형식은 다음과 같아야합니다.

N3337 6.1 / 1

표시 문 :

...

  • 속성 지정자 -seqopt case constant-expression :statement

...

그리고 C++ 선언문 에서 진술 문 으로도 간주됩니다 . (반대 C) :

N3337 6/1 :

성명서 :

...

선언문

...

2. 왜 변수 선언을 건너 뛰어 사용할 수 있습니까?

이유 : N3337 6.7 / 3

초기화로 선언을 우회하는 방식으로 블록으로 전송할 수는 없습니다 . 점프 프로그램 ( 에서 전송 a의 조건 경우 레이블 스위치 문은 점프 간주된다 이 점에서).

변수가 스칼라 유형 , 사소한 기본 생성자를 가진 클래스 유형 및 사소한 소멸자, cv 규정 버전을 갖지 않는 한 자동 저장 기간이있는 변수가 범위에 있지 않는 범위에서 범위에 속하지 않는 지점까지 이 타입들 중 하나의 타입, 또는 앞의 타입들 중 하나의 배열이고 초기화 기 (8.5)없이 선언된다.

보낸 사람 k이다 스칼라 유형 , 그리고 그것의 선언이 가능 이상 선언 점프의 시점에서 초기화되지 않았습니다. 이것은 의미 상 동일합니다.

goto label;

int x;

label:
cout << x << endl;

그러나 x선언 시점에서 초기화 된 경우 불가능합니다 .

 goto label;

    int x = 58; //error, jumping over declaration with initialization

    label:
    cout << x << endl;

1

새로운 변수는 블록 범위에서만 데칼 될 수 있습니다. 다음과 같이 작성해야합니다.

case VAL:  
  // This will work
  {
  int newVal = 42;  
  }
  break;

물론 newVal은 중괄호 안에 만 범위가 있습니다 ...

건배, 랄프


1

switch블록 들의 연속과 동일하지 if/else if블록.나는 다른 대답이 그것을 명확하게 설명하지 않는다는 것에 놀랐습니다.

switch진술을 고려하십시오 :

switch (value) {
    case 1:
        int a = 10;
        break;
    case 2:
        int a = 20;
        break;
}

놀랍지 만 컴파일러는 간단한 것으로 보지 않을 것 if/else if입니다. 다음 코드를 생성합니다.

if (value == 1)
    goto label_1;
else if (value == 2)
    goto label_2;
else
    goto label_end;

{
label_1:
    int a = 10;
    goto label_end;
label_2:
    int a = 20; // Already declared !
    goto label_end;
}

label_end:
    // The code after the switch block

case문은 레이블로 변환 한 후 호출된다 goto. 대괄호는 새로운 범위를 생성하고 왜 같은 이름으로 두 개의 변수를 선언 할 수 없는지 쉽게 알 수 있습니다.switch 블록 .

이상하게 보일지 모르지만 실패 를 지원해야합니다 (즉, break실행을 다음으로 계속하도록 사용하지 않음 case).


0

나는 당면한 문제는 진술을 건너 뛰고 var를 다른 곳에서 사용하려고 시도했지만 선언되지 않았다는 것입니다.


0

newVal은 스위치의 전체 범위에 존재하지만 VAL 사지에 도달 한 경우에만 초기화됩니다. VAL에서 코드 주위에 블록을 만들면 괜찮습니다.


0

C ++ 표준의 특징 : 블록으로 전송할 수는 있지만 초기화로 선언을 우회하는 방식으로는 불가능합니다. 변수가 POD 유형 (3.9)을 갖고 이니셜 라이저 (8.5)없이 선언되지 않으면 자동 저장 기간이있는 지역 변수가 범위 내에 있지 않은 지점에서 범위 내에있는 지점으로 이동하는 프로그램입니다.

이 규칙을 설명하는 코드는 다음과 같습니다.

#include <iostream>

using namespace std;

class X {
  public:
    X() 
    {
     cout << "constructor" << endl;
    }
    ~X() 
    {
     cout << "destructor" << endl;
    }
};

template <class type>
void ill_formed()
{
  goto lx;
ly:
  type a;
lx:
  goto ly;
}

template <class type>
void ok()
{
ly:
  type a;
lx:
  goto ly;
}

void test_class()
{
  ok<X>();
  // compile error
  ill_formed<X>();
}

void test_scalar() 
{
  ok<int>();
  ill_formed<int>();
}

int main(int argc, const char *argv[]) 
{
  return 0;
}

이니셜 라이저 효과를 표시하는 코드 :

#include <iostream>

using namespace std;

int test1()
{
  int i = 0;
  // There jumps fo "case 1" and "case 2"
  switch(i) {
    case 1:
      // Compile error because of the initializer
      int r = 1; 
      break;
    case 2:
      break;
  };
}

void test2()
{
  int i = 2;
  switch(i) {
    case 1:
      int r;
      r= 1; 
      break;
    case 2:
      cout << "r: " << r << endl;
      break;
  };
}

int main(int argc, const char *argv[]) 
{
  test1();
  test2();
  return 0;
}

0

익명의 객체 참조 할 수없고 다음 사례로 넘어 가지 않기 때문에 스위치 케이스 문에서 선언하거나 만들 있는 것으로 보입니다 . 이 예제는 GCC 4.5.3 및 Visual Studio 2008에서 컴파일되는 것을 고려하십시오 (전문가가 계량해야하는 규정 준수 문제 일 수 있음)

#include <cstdlib>

struct Foo{};

int main()
{
    int i = 42;

    switch( i )
    {
    case 42:
        Foo();  // Apparently valid
        break;

    default:
        break;
    }
    return EXIT_SUCCESS;
}

투표에 참여할 이유를 설명하십시오. 익명 개체를 만드는 것이 면제되는 이유가 궁금합니다.
Olumide

1
DV가 아니라 : 전체 질문은 명명 된 변수의 선언 / 범위에 관한 것입니다. 임시 ( "익명 개체"는 용어가 아님)는 명명 된 변수가 아니며 선언도 아니고 범위도 적용되지 않습니다 ( const자체 범위 가있는 참조에 바인딩되지 않은 경우 ). 그것은 성명서 내에서 살고 죽는 표현입니다. 따라서 전적으로 관련이 없습니다.
underscore_d

Foo();선언이 아닙니다. 문제는 선언에 관한 것입니다.
MM
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.