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

发布于 2024-11-01 14:21:18 字数 322 浏览 0 评论 0原文

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

错误:跳转到案例标签。

为什么要这样做?

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

I wrote a program which involves use of switch statements, however on compilation it shows:

Error: Jump to case label.

Why does it do that?

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

神也荒唐 2024-11-08 14:21:18

问题是,除非使用显式的 { } 块,否则在一个 case 中声明的变量在后续 case 中仍然可见,< em>但是它们不会被初始化,因为初始化代码属于另一个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;
}

The problem is that variables declared in one case are still visible in the subsequent cases unless an explicit { } block is used, but they will not be initialized because the initialization code belongs to another case.

In the following code, if foo equals 1, everything is ok, but if it equals 2, we'll accidentally use the i variable which does exist but probably contains garbage.

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

Wrapping the case in an explicit block solves the problem:

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

To further elaborate, switch statements are just a particularly fancy kind of a goto. Here's an analoguous piece of code exhibiting the same issue but using a goto instead of a 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;
}
方觉久 2024-11-08 14:21:18

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

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

Declaration of new variables in case statements is what causing problems. Enclosing all case statements in {} will limit the scope of newly declared variables to the currently executing case which solves the problem.

switch(choice)
{
    case 1: {
       // .......
    }break;
    case 2: {
       // .......
    }break;
    case 3: {
       // .......
    }break;
}    
櫻之舞 2024-11-08 14:21:18

跳过某些初始化的 C++11 标准

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

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

3 可以转移到块中,但不能以绕过初始化声明的方式。一个
程序从具有自动存储持续时间的变量不在范围内的点跳转(87)
除非变量具有标量类型、具有简单默认值的类类型,否则它在范围内的点格式不正确
构造函数和一个简单的析构函数,这些类型之一的 cv 限定版本,或者其中之一的数组
前面的类型,并且在没有初始化器的情况下声明(8.5)。

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

[示例:

void f() {
   // ...
  转到lx; // 格式错误:跳转到 a 的范围
  // ...
ly:
  X a = 1;
  // ...
勒克斯:
  转到ly; // OK,跳转意味着析构函数
              // 调用一个随后的构造
              // 再次紧跟在标签 ly 之后
}

—结束示例]

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

交叉初始化

C

的初始化C允许这样做:c99转到过去的初始化

C99 N1256 标准草案 附件一“常见警告” ” 说:

2 跳转到具有自动存储持续时间的对象初始化的块

C++11 standard on jumping over some initializations

JohannesD gave an explanation, now for the standards.

The C++11 N3337 standard draft 6.7 "Declaration statement" says:

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 ]

As of GCC 5.2, the error message now says:

crosses initialization of

C

C allows it: c99 goto past initialization

The C99 N1256 standard draft Annex I "Common warnings" says:

2 A block with initialization of an object that has automatic storage duration is jumped into

離殇 2024-11-08 14:21:18

JohannesD的答案是正确的,但我觉得问题的某个方面并不完全清楚。

他给出的示例在情况 1 中声明并初始化变量 i,然后尝试在情况 2 中使用它。他的论点是,如果直接切换到情况 2,i code> 将在未初始化的情况下使用,这就是出现编译错误的原因。此时,人们可能会认为,如果在一种情况下声明的变量在其他情况下从未使用过,那就没有问题。例如:

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 都只在声明它们的情况下使用。不幸的是,在 C++ 中它无法编译:正如 Ciro Santilli 包子露宪六四事件法轮功所解释的,我们根本就可以' t 跳转到 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++,没有编译器接受它。

JohannesD's answer is correct, but I feel it isn't entirely clear on an aspect of the problem.

The example he gives declares and initializes the variable i in case 1, and then tries to use it in case 2. His argument is that if the switch went straight to case 2, i would be used without being initialized, and this is why there's a compilation error. At this point, one could think that there would be no problem if variables declared in a case were never used in other cases. For example:

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

One could expect this program to compile, since both i and j are used only inside the cases that declare them. Unfortunately, in C++ it doesn't compile: as Ciro Santilli 包子露宪 六四事件 法轮功 explained, we simply can't jump to case 2:, because this would skip the declaration with initialization of i, and even though case 2 doesn't use i at all, this is still forbidden in C++.

Interestingly, with some adjustments (an #ifdef to #include the appropriate header, and a semicolon after the labels, because labels can only be followed by statements, and declarations do not count as statements in C), this program does compile as 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;
    }
}

Thanks to an online compiler like http://rextester.com you can quickly try to compile it either as C or C++, using MSVC, GCC or Clang. As C it always works (just remember to set STDIN!), as C++ no compiler accepts it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文