错误:在switch语句中跳转到大小写标签

我写了一个程序,其中涉及到switch语句的使用,但是在编译时它显示:

错误:跳转到大小写标签。

为什么会这样?

#include <iostream>
int main()
{
int choice;
std::cin >> choice;
switch(choice)
{
case 1:
int i=0;
break;
case 2: // error here
}
}
413455 次浏览

问题是在一个case中声明的变量在随后的case中仍然可见,除非显式地使用了{ }块,但是它们不会被初始化,因为初始化代码属于另一个case

在下面的代码中,如果foo等于1,一切正常,但如果它等于2,我们将意外地使用i变量,该变量确实存在,但可能包含垃圾。

switch(foo) {
case 1:
int i = 42; // i exists all the way to the end of the switch
dostuff(i);
break;
case 2:
dostuff(i*2); // i is *also* in scope here, but is not initialized!
}

将case包装在显式块中可以解决问题:

switch(foo) {
case 1:
{
int i = 42; // i only exists within the { }
dostuff(i);
break;
}
case 2:
dostuff(123); // Now you cannot use i accidentally
}

编辑

进一步说明,switch语句只是一种特别花哨的goto。下面是一段类似的代码,显示了同样的问题,但使用了goto而不是switch:

int main() {
if(rand() % 2) // Toss a coin
goto end;


int i = 42;


end:
// We either skipped the declaration of i or not,
// but either way the variable i exists here, because
// variable scopes are resolved at compile time.
// Whether the *initialization* code was run, though,
// depends on whether rand returned 0 or 1.
std::cout << i;
}

在case语句中声明新变量是导致问题的原因。在{}中包含所有case语句将把新声明变量的范围限制在当前正在执行的情况下,从而解决了问题。

switch(choice)
{
case 1: {
// .......
}break;
case 2: {
// .......
}break;
case 3: {
// .......
}break;
}

c++ 11标准中跳过一些初始化

JohannesD给出了一个解释,现在为标准。

c++ 11 N3337标准草案 6.7“声明声明”说:

可以转移到块中,但不能绕过带有初始化的声明。一个 从具有自动存储持续时间的变量不在作用域中的点(87)跳转到 它在作用域中的点是病态的,除非变量具有标量类型,类类型具有简单的默认值 构造函数和普通析构函数、这些类型之一的cv限定版本或类型之一的数组 前面的类型,并且声明时没有初始化式(8.5)

87)从switch语句的条件到case标签的转换在这方面被认为是一个跳跃。

(例子:

void f() {
// ...
goto lx;    // ill-formed: jump into scope of a
// ...
ly:
X a = 1;
// ...
lx:
goto ly;    // OK, jump implies destructor
// call for a followed by construction
// again immediately following label ly
}

- end示例]

从GCC 5.2开始,错误消息现在显示:

交叉初始化

C

C允许:C99去过去的初始化

c99n1256标准草案附录I“常见警告”说:

一个对象的初始化块具有自动存储持续时间

JohannesD的回答是正确的,但我觉得它不是完全清楚的问题的一个方面。

他给出的例子在情况1中声明并初始化变量i,然后在情况2中尝试使用它。他的论点是,如果切换到情况2,i将在没有初始化的情况下被使用,这就是为什么会有编译错误。在这一点上,人们可能会认为,如果在一个案例中声明的变量从未在其他案例中使用,那么就没有问题。例如:

switch(choice) {
case 1:
int i = 10; // i is never used outside of this case
printf("i = %d\n", i);
break;
case 2:
int j = 20; // j is never used outside of this case
printf("j = %d\n", j);
break;
}

你可以期望这个程序可以编译,因为ij都只在声明它们的case中使用。不幸的是,在c++中它不能编译:作为,我们根本不能跳转到case 2:,因为这会跳过i初始化的声明,而且即使case 2根本不使用i,这在c++中仍然是禁止的。

有趣的是,经过一些调整(从#ifdef#include是适当的头文件,并且在标签后面有一个分号,因为标签后面只能跟语句,而声明在C语言中不能算作语句),这个程序编译为C:

// Disable warning issued by MSVC about scanf being deprecated
#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS
#endif


#ifdef __cplusplus
#include <cstdio>
#else
#include <stdio.h>
#endif


int main() {


int choice;
printf("Please enter 1 or 2: ");
scanf("%d", &choice);


switch(choice) {
case 1:
;
int i = 10; // i is never used outside of this case
printf("i = %d\n", i);
break;
case 2:
;
int j = 20; // j is never used outside of this case
printf("j = %d\n", j);
break;
}
}

多亏了像http://rextester.com这样的在线编译器,你可以使用MSVC、GCC或Clang将其快速编译为C或c++。与C语言一样,它总是有效的(只是记得设置STDIN!),因为c++没有编译器接受它。