我一直想知道-为什么您不能在switch语句中的case标签之后声明变量? 在C ++中,您几乎可以在任何地方声明变量(并且声明它们接近首次使用显然是一件好事),但以下操作仍然无效:
switch (val)
{
case VAL:
// This won't work
int newVal = 42;
break;
case ANOTHER_VAL:
...
break;
}
上面给了我以下错误(MSC):
“ newVal”的初始化被“ case”标签跳过
这似乎也是其他语言的限制。 为什么会有这样的问题?
#1楼
C ++ Standard具有:可以传输到块中,但不能以初始化绕过声明的方式进行。 除非变量具有POD类型(3.9)且声明时没有初始化程序(8.5),否则该程序将从具有自动存储持续时间的局部变量不在范围内的点跳转到其处于范围内的点的格式不正确。
用来说明此规则的代码:
#include
using namespace std;
class X {
public:
X()
{
cout << "constructor" << endl;
}
~X()
{
cout << "destructor" << endl;
}
};
template
void ill_formed()
{
goto lx;
ly:
type a;
lx:
goto ly;
}
template
void ok()
{
ly:
type a;
lx:
goto ly;
}
void test_class()
{
ok();
// compile error
ill_formed();
}
void test_scalar()
{
ok();
ill_formed();
}
int main(int argc, const char *argv[])
{
return 0;
}
显示初始化效果的代码:
#include
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;
}
#2楼
到目前为止,答案一直是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();
}
#3楼
似乎可以在switch case语句中声明或创建匿名对象,原因是它们无法被引用,因此不能陷入下一种情况。 考虑此示例在GCC 4.5.3和Visual Studio 2008上编译(可能是合规性问题,因此请专家们权衡一下)
#include
struct Foo{};
int main()
{
int i = 42;
switch( i )
{
case 42:
Foo(); // Apparently valid
break;
default:
break;
}
return EXIT_SUCCESS;
}
#4楼
这个问题
是最初标记为[C]和[C ++]在同一时间。 原始代码确实在C和C ++中均无效,但是出于完全不同的不相关原因。
在C ++中,此代码无效,因为case ANOTHER_VAL:标签跳过了其初始化而跳入了变量newVal的范围。 在C ++中,绕过自动对象初始化的跳转是非法的。 大多数回答正确地解决了问题的这一方面。
但是,在C语言中,绕过变量初始化不是错误。 在C语言中,进入变量的初始化范围是合法的。这仅表示该变量未初始化。 出于完全不同的原因,原始代码无法在C中编译。 标签case VAL:在原始代码中附加到变量newVal的声明中。 在C语言中,声明不是语句。 它们不能被标记。 这就是当将此代码解释为C代码时导致错误的原因。switch (val) { case VAL: /*
添加一个额外的{}块可以解决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角度来看它仍然无效。
#5楼
我只是想强调苗条的观点 。 开关构造可创建一个完整的一流公民范围。 因此,可以在第一个case标签之前的switch语句中声明(并初始化)变量, 而无需附加的括号对:
switch (val) {
/* This *will* work, even in C89 */
int newVal = 42;
case VAL:
newVal = 1984;
break;
case ANOTHER_VAL:
newVal = 2001;
break;
}