c++ - 切换语句中被声明为什么不能变量?

  显示原文与译文双语对照的内容

我一直在想这一点- 为什么不能在 switch 语句中的案例标签之后声明变量? 在 C++ 中,你可以在任意( 在第一次使用时声明它们显然是一件好事) 处声明变量,但以下仍将不起作用:


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

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

'newval'的初始化被'大小写'标签跳过

这似乎是其他语言的限制。 为什么这样一个问题?

时间:

案例语句仅'标签'。 这意味着编译器将把它解释为直接跳转到标签。 在 C++ 中,问题是作用域之一。 你的花括号定义了'switch'语句内的范围。 这意味着你剩下的作用域将进一步执行跳过初始化的代码。 处理这个问题的正确方法是定义一个特定于该事例语句的作用域,并在其中定义你的变量。


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

好的,只是为了澄清这与声明无关。 它只与"跳过初始化"( 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;
}

其中,对象是non-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 LABEL;//Error jumping over initialization
int j = 0; 
LABEL:
 ;

一些琐事是 C++ 和之间的区别。 在C 中,跳过初始化不是一个错误。

就像其他人所提到的,解决方案是添加一个嵌套块,以便变量的生命周期限制在单个事例标签中。

整个 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 标签如何完全忽略块边界。 是的,这是邪恶的,但是这就是为什么你的代码示例不工作的原因。 跳转到 case 标签与使用 goto 相同,所以你不允许通过构造函数跳过局部变量。

就像其他一些海报所示,你需要放置一个自己的块:


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

这里问题同时标记为 [C] 和 [C++] 。 原始代码在 C++ 和中确实是无效的,但对于完全不同的原因却是完全不同的。 我相信这个重要的细节被现存的答案忽略了( 或者模糊) 。

  • 在 C++ 中,这里代码无效,因为 case ANOTHER_VAL: 标签跳转到变量 newVal的作用域,绕过它的初始化。 这样的跳转在 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;
    }
    
    

添加一个额外的{} 块可以修复 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 角度来看它仍然无效。

到目前为止,大多数答复都是错误的: 你案后 可以声明变量语句,但是你不能初始化它们:


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

就像前面提到的,一个不错的方法是使用大括号为你的案例创建一个作用域。

我最喜欢的switch 伎俩是使用 if(0) 跳过一个不需要的标签标签。


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

但非常邪恶。

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


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

原因是在堆栈上分配( 并回收) 空间来存储本地 variable(s) 。

...