我一直在想这个问题——为什么不能在switch语句的case标签后面声明变量呢?在c++中,你可以在任何地方声明变量(并且在第一次使用时声明它们显然是一件好事),但以下仍然不能工作:

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

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

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

这在其他语言中似乎也是一个限制。为什么这是一个问题?


当前回答

到目前为止,答案都是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();
}

其他回答

匿名对象似乎可以在switch case语句中声明或创建,因为它们不能被引用,因此不能进入下一个case。考虑这个例子是在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;
}

一个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调用。括号创建了一个新的作用域,现在很容易看出为什么不能在一个开关块中声明两个具有相同名称的变量。

它可能看起来很奇怪,但是支持fallthrough是必要的(也就是说,不使用break让执行继续到下一个case)。

整个switch语句在同一个作用域中。要解决这个问题,可以这样做:

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

    case ANOTHER_VAL:
      ...
    break;
}

注意括号。

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

达夫的装置最清楚地说明了这一点。以下是一些来自维基百科的代码:

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标签与使用goto相同,因此不允许跳过带有构造函数的局部变量。

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

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

我只是想强调斯利姆的观点。转换构造创建了一个完整的、一等公民的范围。因此,可以在switch语句中在第一个case标签之前声明(并初始化)一个变量,而不需要额外的括号对:

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