C++ 为什么不能在 switch 语句中声明变量?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/92396/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 12:47:22  来源:igfitidea点击:

Why can't variables be declared in a switch statement?

c++switch-statement

提问by Rob

I've always wondered this - why can't you declare variables after a case label in a switch statement? In C++ you can declare variables pretty much anywhere (and declaring them close to first use is obviously a good thing) but the following still won't work:

我一直想知道 - 为什么不能在 switch 语句中的 case 标签后声明变量?在 C++ 中,您几乎可以在任何地方声明变量(并且在第一次使用时声明它们显然是一件好事),但以下仍然不起作用:

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

The above gives me the following error (MSC):

以上给了我以下错误(MSC):

initialization of 'newVal' is skipped by 'case' label

'case' 标签跳过了 'newVal' 的初始化

This seems to be a limitation in other languages too. Why is this such a problem?

这似乎也是其他语言的限制。为什么会出现这样的问题?

回答by TJ Seabrooks

Casestatements are only labels. This means the compiler will interpret this as a jump directly to the label. In C++, the problem here is one of scope. Your curly brackets define the scope as everything inside the switchstatement. This means that you are left with a scope where a jump will be performed further into the code skipping the initialization.

Case语句只是标签。这意味着编译器会将其解释为直接跳转到标签。在 C++ 中,这里的问题是范围之一。您的大括号将范围定义为switch语句中的所有内容。这意味着您将留下一个范围,在该范围内将进一步跳转到跳过初始化的代码中。

The correct way to handle this is to define a scope specific to that casestatement and define your variable within it:

处理此问题的正确方法是定义特定于该case语句的范围并在其中定义变量:

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

回答by AnT

This question iswas originally tagged as [C] and [C++] at the same time. The original code is indeed invalid in both C and C++, but for completely different unrelated reasons.

这个问题最初标记为[C]和[C ++]在同一时间。原始代码在 C 和 C++ 中确实无效,但出于完全不同的无关原因。

  • In C++ this code is invalid because the case ANOTHER_VAL:label jumps into the scope of variable newValbypassing its initialization. Jumps that bypass initialization of automatic objects are illegal in C++. This side of the issue is correctly addressed by most answers.

  • However, in C language bypassing variable initialization is not an error. Jumping into the scope of a variable over its initialization is legal in C. It simply means that the variable is left uninitialized. The original code does not compile in C for a completely different reason. Label case VAL:in the original code is attached to the declaration of variable newVal. In C language declarations are not statements. They cannot be labeled. And this is what causes the error when this code is interpreted as C code.

    switch (val)  
    {  
    case VAL:             /* <- C error is here */
      int newVal = 42;  
      break;
    case ANOTHER_VAL:     /* <- C++ error is here */
      ...
      break;
    }
    
  • 在 C++ 中,此代码无效,因为case ANOTHER_VAL:标签newVal绕过其初始化跳转到变量的范围内。绕过自动对象初始化的跳转在 C++ 中是非法的。大多数答案都正确解决了问题的这一方面。

  • 但是,在 C 语言中,绕过变量初始化并不是错误。在变量初始化时跳转到变量的作用域在 C 中是合法的。它只是意味着变量未被初始化。由于完全不同的原因,原始代码不能在 C 中编译。case VAL:原始代码中的标签附加在变量的声明中newVal。在 C 语言中,声明不是语句。它们不能被标记。当这段代码被解释为 C 代码时,这就是导致错误的原因。

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

Adding an extra {}block fixes both C++ and C problems, even though these problems happen to be very different. On the C++ side it restricts the scope of newVal, making sure that case ANOTHER_VAL:no longer jumps into that scope, which eliminates the C++ issue. On the C side that extra {}introduces a compound statement, thus making the case VAL:label to apply to a statement, which eliminates the C issue.

添加额外的{}块可以修复 C++ 和 C 问题,即使这些问题碰巧非常不同。在 C++ 方面,它限制了 的范围newVal,确保case ANOTHER_VAL:不再跳入该范围,从而消除了 C++ 问题。在 C 方面,额外{}引入了复合语句,从而使case VAL:标签应用于语句,从而消除了 C 问题。

  • In C case the problem can be easily solved without the {}. Just add an empty statement after the case VAL:label and the code will become valid

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

    Note that even though it is now valid from C point of view, it remains invalid from C++ point of view.

  • Symmetrically, in C++ case the the problem can be easily solved without the {}. Just remove the initializer from variable declaration and the code will become valid

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

    Note that even though it is now valid from C++ point of view, it remains invalid from C point of view.

  • 在 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 的角度来看它仍然是无效的。

回答by Richard Corden

Ok. Just to clarify this strictly has nothing to do with the declaration. It relates only to "jumping over the initialization" (ISO C++ '03 6.7/3)

好的。只是为了澄清这一点与声明无关。它仅与“跳过初始化”有关(ISO C++ '03 6.7/3)

A lot of the posts here have mentioned that jumping over the declaration may result in the variable "not being declared". This is not true. An POD object can be declared without an initializer but it will have an indeterminate value. For example:

这里的很多帖子都提到跳过声明可能会导致变量“未声明”。这不是真的。可以在没有初始化程序的情况下声明 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;
}

Where the object is a non-POD or aggregate the compiler implicitly adds an initializer, and so it is not possible to jump over such a declaration:

如果对象是非 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;
}

This limitation is not limited to the switch statement. It is also an error to use 'goto' to jump over an initialization:

此限制不仅限于 switch 语句。使用 'goto' 跳过初始化也是一个错误:

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

A bit of trivia is that this is a difference between C++ and C. In C, it is not an error to jump over the initialization.

有点琐碎的是,这是 C++ 和 C 之间的区别。在 C 中,跳过初始化不是错误。

As others have mentioned, the solution is to add a nested block so that the lifetime of the variable is limited to the individual case label.

正如其他人所提到的,解决方案是添加一个嵌套块,以便变量的生命周期仅限于单个案例标签。

回答by Mark Ingram

The whole switch statement is in the same scope. To get around it, do this:

整个 switch 语句在同一范围内。要解决它,请执行以下操作:

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

    case ANOTHER_VAL:
      ...
    break;
}

Notethe brackets.

注意括号。

回答by Jeegar Patel

After reading all answers and some more research I get a few things.

在阅读了所有答案和更多研究之后,我得到了一些东西。

Case statements are only 'labels'

In C, according to the specification,

在 C 中,根据规范,

§6.8.1 Labeled Statements:

§6.8.1 标签声明:

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

In C there isn't any clause that allows for a "labeled declaration". It's just not part of the language.

在 C 中没有任何条款允许“标记声明”。它只是不是语言的一部分。

So

所以

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

This will not compile, see http://codepad.org/YiyLQTYw. GCC is giving an error:

将无法编译,请参阅http://codepad.org/YiyLQTYw。GCC 报错:

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

Even

甚至

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

this is also not compiling, see http://codepad.org/BXnRD3bu. Here I am also getting the same error.

也不是编译,请参阅http://codepad.org/BXnRD3bu。在这里,我也遇到了同样的错误。



In C++, according to the specification,

在 C++ 中,根据规范,

labeled-declaration is allowed but labeled -initialization is not allowed.

标签声明是允许的,但标签初始化是不允许的。

See http://codepad.org/ZmQ0IyDG.

请参阅http://codepad.org/ZmQ0IyDG



Solution to such condition is two

这种情况的解决方案是两个

  1. Either use new scope using {}

    case 1:
           {
               int x=10;
               printf(" x is %d", x);
           }
    break;
    
  2. Or use dummy statement with label

    case 1: ;
               int x=10;
               printf(" x is %d",x);
    break;
    
  3. Declare the variable before switch() and initialize it with different values in case statement if it fulfills your requirement

    main()
    {
        int x;   // Declare before
        switch(a)
        {
        case 1: x=10;
            break;
    
        case 2: x=20;
            break;
        }
    }
    
  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;
        }
    }
    


Some more things with switch statement

switch 语句的更多内容

Never write any statements in the switch which are not part of any label, because they will never executed:

永远不要在 switch 中写任何不属于任何标签的语句,因为它们永远不会执行:

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

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

    default:
        break;
}

See http://codepad.org/PA1quYX3.

请参阅http://codepad.org/PA1quYX3

回答by emk

You can't do this, because caselabels are actually just entry points into the containing block.

你不能这样做,因为case标签实际上只是包含块的入口点。

This is most clearly illustrated by Duff's device. Here's some code from 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);
    }
}

Notice how the caselabels totally ignore the block boundaries. Yes, this is evil. But this is why your code example doesn't work. Jumping to a caselabel is the same as using goto, so you aren't allowed to jump over a local variable with a constructor.

请注意case标签如何完全忽略块边界。是的,这是邪恶的。但这就是您的代码示例不起作用的原因。跳转到case标签与 using 相同goto,因此不允许使用构造函数跳过局部变量。

As several other posters have indicated, you need to put in a block of your own:

正如其他几张海报所指出的,您需要放入自己的块:

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

回答by MrZebra

Most of the replies so far are wrong in one respect: you candeclare variables after the case statement, but you can'tinitialize them:

到目前为止的大多数回复在一个方面是错误的:你可以在 case 语句之后声明变量,但你不能初始化它们:

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

As previously mentioned, a nice way around this is to use braces to create a scope for your case.

如前所述,解决此问题的一个好方法是使用大括号为您的案例创建范围。

回答by Jeremy

My favorite evil switch trick is to use an if(0) to skip over an unwanted case label.

我最喜欢的邪恶开关技巧是使用 if(0) 跳过不需要的案例标签。

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

But very evil.

但是非常邪恶。

回答by Dan Shield

Try this:

尝试这个:

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

回答by Seb Rose

You can declare variables within a switch statement ifyou start a new block:

如果您开始一个新块,您可以在 switch 语句中声明变量:

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

The reason is to do with allocating (and reclaiming) space on the stack for storage of the local variable(s).

原因是在堆栈上分配(和回收)空间以存储局部变量。