switch (val)
{
case VAL:
// This won't work
int newVal = 42;
break;
case ANOTHER_VAL:
...
break;
}
上述代码出现了以下错误(MSC):
初始化“newVal”的操作被“case”标签跳过。
这似乎在其他语言中也存在限制。为什么会成为问题呢?
switch (val)
{
case VAL:
// This won't work
int newVal = 42;
break;
case ANOTHER_VAL:
...
break;
}
Case
语句仅仅是标签。这意味着编译器将其解释为直接跳转到标签。在C++中,问题在于作用域。花括号定义了switch
语句内的全部作用域。这意味着你留下了一个作用域,在其中会执行进一步跳转以跳过初始化。case
语句定义一个特定的作用域,并在其中定义变量:switch (val)
{
case VAL:
{
// This will work
int newVal = 42;
break;
}
case ANOTHER_VAL:
...
break;
}
_alloca()
时,编译器无法在进入时知道需要多少空间,因此必须进行逐步调整。 - Ben Voigtbreak;
、return
等的期望都被违反了。 - Ben Voigt这个问题最初被同时标记为c和c++。原始代码在C和C++中都是无效的,但原因完全不相关。
In C++ this code is invalid because the case ANOTHER_VAL:
label jumps into the scope of variable newVal
bypassing 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;
}
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.
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.
;
的修复方法也将不再必要。case ANOTHER_VAL:
点,变量newVal
是可见的,但具有不确定的值。 - AnT stands with Russia§A9.3:Compound Statement
后找到了这个问题。该条目提到了compound-statement的技术定义,即{declaration-list[opt] statement-list[opt]}
。 由于我曾认为声明就是语句,所以感到困惑,但是我查找后立刻发现了这个问题,其中展示了这种差异变得明显并实际上破坏了程序的示例。 我认为另一个解决方案(针对C语言)是在声明之前放置另一个语句(可能是空语句?),以便满足labeled-statement。 - Braden Best好的。仅仅为了澄清,这与声明严格没有任何关系。它只涉及到“跳过初始化”(ISO C++ '03 6.7/3)。
这里有很多帖子提到跳过声明可能会导致变量“未被声明”。这是不正确的。POD对象可以在没有初始化器的情况下声明,但它将具有不确定的值。例如:
switch (i)
{
case 0:
int j; // 'j' has indeterminate value
j = 0; // 'j' set (not 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或聚合类型,编译器会隐式添加一个初始化程序,因此不可能跳过这样的声明:
如果对象是非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中,跳过初始化不会导致错误。
正如其他人提到的,解决方法是添加一个嵌套块,以便变量的生命周期仅限于个别的case标签。
整个 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语言中,没有任何允许“标记声明”的子句。这不是该语言的一部分。
因此
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++规范,
允许使用标记声明(labeled-declaration),但不允许使用标记初始化(labeled-initialization)。
请参见http://codepad.org/ZmQ0IyDG。
解决此问题有两种方法:
可以使用新的作用域来解决这个问题,即使用 {}。
case 1:
{
int x=10;
printf(" x is %d", x);
}
break;
或者使用带标签的虚拟语句。case 1: ;
int x=10;
printf(" x is %d",x);
break;
在 switch() 语句之前声明变量,并在 case 语句中使用不同的值进行初始化,如果这符合您的要求。
main()
{
int x; // Declare before
switch(a)
{
case 1: x=10;
break;
case 2: x=20;
break;
}
}
使用 switch 语句的一些注意事项
不要在 switch 语句中编写任何不属于任何标签的语句,因为它们永远不会被执行:
switch(a)
{
printf("This will never print"); // This will never executed
case 1:
printf(" 1");
break;
default:
break;
}
a
的初始化进入变量a
的作用域。因此,从C的角度来看,问题出在case VAL:
标签上,你正确地描述了它。但从C++的角度来看,问题出在case ANOTHER_VAL:
标签上。 - AnT stands with Russia由于 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;
}
...
}
DIV
指令。它执行的相当于:num < 0 ? ((num + 7) & 7) - 7 : num & 7
(但完全没有分支、乘法或除法)。因此,它绝不是“非常慢”的。 - C. K. Young到目前为止,大部分回答在一个方面都是错误的:你可以在case语句之后声明变量,但你不能初始化它们:
case 1:
int x; // Works
int y = 0; // Error, initialization is skipped by case
break;
case 2:
...
正如之前提到的,一个不错的解决方法是使用花括号为您的案例创建一个作用域。
我最喜欢的邪恶开关技巧是使用if(0)来跳过不需要执行的case标签。
switch(val)
{
case 0:
// Do something
if (0) {
case 1:
// Do something else
}
case 2:
// Do something in all cases
}
但非常邪恶。
goto
直接跳转到所需的标签,而不会使代码混乱。 - SomeWittyUsername试试这个:
switch (val)
{
case VAL:
{
int newVal = 42;
}
break;
}
if
。switch (thing)
{
case A:
{
int i = 0; // Completely legal
}
break;
}