我写了一个程序,其中涉及到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标准草案附件一“常见警告”规定:

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