错误:在switch语句中跳转到case标签

341

我写了一个使用了 switch 语句的程序,但是编译时出现以下错误:

Error: Jump to case label.

为什么会这样?

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

1
你正在尝试编译哪段代码?你使用的是哪个编译器?你是否已经在每个case块中加入了大括号? - Cody Gray
10
这是一个非常绕的错误提示。 - jozxyqk
确实,你完全有可能从这个得出错误的结论! - undefined
3个回答

636
问题在于在一个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!
}

将该语句用一个明确的代码块包裹起来即可解决问题:

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语句:

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;
}

4
请查看此修复后的LLVM错误报告,以获取其他说明:http://llvm.org/bugs/show_bug.cgi?id=7789。 - Francesco
在这种情况下,使用goto是好的编码实践吗? - undefined

108

case语句中声明新变量是导致问题的原因。将所有case语句都包含在{}中,可以将新声明变量的作用范围限制在当前执行的case中,从而解决问题。

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

1
更清晰的修复指令 - yc_yuy
如果我把break语句放在花括号内,会有任何问题吗? - Vishal Sharma
1
我总是加大括号,如果不这样做,就会出现各种奇怪的作用域问题。@VishalSharma在大括号内使用break语句是可以的。 - Marius
我从未想过会有这么简单的解决方案,谢谢!+1 - FLAK-ZOSO

19

C++11标准关于跳过某些初始化的规定

JohannesD 给出了解释,现在来看看标准文件。

C++11 N3337标准草案 第6.7节 "声明语句" 如下:

3 可以进入一个块,但不能绕过带有初始化的声明。从一个自动存储期变量不在作用域的点跳转(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
}

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

跨初始化

C

C语言允许: c99 goto past initialization

C99 N1256标准草案的附录I“常见警告”中说:

2 跳转到一个包含具有自动存储期的对象初始化的块内

— 结束示例 ]


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接