我写了一个程序,其中涉及到switch语句的使用,但是在编译时它显示:
错误:跳转到大小写标签。
为什么会这样?
#include <iostream>
int main()
{
int choice;
std::cin >> choice;
switch(choice)
{
case 1:
int i=0;
break;
case 2: // error here
}
}
我写了一个程序,其中涉及到switch语句的使用,但是在编译时它显示:
错误:跳转到大小写标签。
为什么会这样?
#include <iostream>
int main()
{
int choice;
std::cin >> choice;
switch(choice)
{
case 1:
int i=0;
break;
case 2: // error here
}
}
当前回答
约翰:他的回答是正确的,但我觉得在问题的某一方面还不完全清楚。
他给出的例子在情况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;
}
人们可以期望这个程序能够编译,因为i和j都只在声明它们的case中使用。
有趣的是,经过一些调整(#ifdef到#包含了适当的头文件,并且标签后面有一个分号,因为标签后面只能跟语句,而声明在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++没有编译器接受它。
其他回答
问题是,在一种情况下声明的变量在随后的情况下仍然可见,除非使用显式{}块,但它们不会被初始化,因为初始化代码属于另一种情况。
在下面的代码中,如果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
}
Edit
为了进一步说明,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语句将把新声明的变量的范围限制在当前正在执行的case中,从而解决了这个问题。
switch(choice)
{
case 1: {
// .......
}break;
case 2: {
// .......
}break;
case 3: {
// .......
}break;
}
约翰:他的回答是正确的,但我觉得在问题的某一方面还不完全清楚。
他给出的例子在情况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;
}
人们可以期望这个程序能够编译,因为i和j都只在声明它们的case中使用。
有趣的是,经过一些调整(#ifdef到#包含了适当的头文件,并且标签后面有一个分号,因为标签后面只能跟语句,而声明在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++没有编译器接受它。
c++ 11标准中跳过一些初始化
约翰:现在我们来解释一下标准。
c++ 11 N3337标准草案6.7“声明声明”说:
3 It is possible to transfer into a block, but not in a way that bypasses declarations with initialization. A program that jumps (87) from a point where a variable with automatic storage duration is not in scope to a point where it is in scope is ill-formed unless the variable has scalar type, class type with a trivial default constructor and a trivial destructor, a cv-qualified version of one of these types, or an array of one of the preceding types and is declared without an initializer (8.5). 87) The transfer from the condition of a switch statement to a case label is considered a jump in this respect. [ Example: 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 example ]
从GCC 5.2开始,错误消息现在显示:
交叉初始化
C
C允许:c99去过去的初始化
C99 N1256标准草案附件一“常见警告”规定:
一个对象的初始化块具有自动存储持续时间