【问题标题】:Why is case statement allowed inside block for another case? [duplicate]为什么在另一种情况下允许在块内使用case语句? [复制]
【发布时间】:2017-02-07 07:04:02
【问题描述】:

我遇到过如下代码:

  switch(i) {
    case 2: {
      std::cout << "2";
      break;
    case 3:
      std::cout << "3";
      break;
    }
    case 4: {
      std::cout << "4";
      break;
    }    
  }

请注意case 2 打开一个带有花括号的块,该块仅在case 3 之后关闭。起初,这似乎是一个错字,会导致编译器错误,或者更糟糕的是,忽略case 3。但它在 c++ 中工作得很好,如果 i 为 3,则输出 3。我来自 java 背景,所以我可能缺乏对 c++ 中逻辑块的理解。所以我的问题是:这是故意的行为吗?

【问题讨论】:

  • C 和 C++ 中的 switch 语句并不像您想象的那样逻辑。请参阅Duff's device 获取极端示例。
  • @NeilButterworth,这是一个影响深远的声明,我不能同意。
  • @NeilButterworth,开关在合理使用时提供良好的可读性(与if...else if... 语句的长列表相比),并且在某些情况下提供优化好处。前者的好处大于后者。
  • @NeilButterworth 实际上,相信switch 块平均可以更快地编写代码并没有什么“魔法”,只是简单的逻辑。 switch 块进行一次检查和一两次跳转(跳转到 case,在 break 处跳出(如果存在)),而未优化的 if..else if..else 链进行一次检查和跳转(或者到 else ,或到链的末尾)每个 if 执行(这意味着对于 if..else if..else if..else 块,它会进行 1-3 次检查和跳转)。对于任何可以写成任何一个的块,switch 因此平均效率会稍微高一些。
  • switch 块是对优化编译器的强烈提示,它应该将其优化为跳转表。一系列 if 语句强烈暗示它不应该这样做。我见过编译器会将一系列 if 语句转换为跳转表的情况,@Justin,但绝大多数情况下,它不会。假设是你会写你的意思。如果情况多于一系列 if 语句的可读性,那么您将其编写为 switch-case,并且应将其编译为跳转表。否则,跳转表的开销就是性能损失。

标签: c++ switch-statement


【解决方案1】:

您可以但不应该在switch 中滥用案例标签,这比这要糟糕得多——而且比Duff's Device 还要糟糕得多。 Duff's Device 具有令人怀疑的特权,它几乎看起来很有用,但可以被视为滥用switch

并非所有对switch 的滥用都可以声称很有用。例如,这编译为 C 或 C++,即使设置了严格的警告:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(int argc, char **argv)
{
    unsigned seed;
    if (argc == 2)
        seed = atoi(argv[1]);
    else
        seed = time(0);
    printf("seed: %u\n", seed);
    srand(seed);

    int i = rand() % 10;
    int j = 21;
    int k = 37;
    printf("i: %d\n", i);
    switch (i)
    {
    case 1:
        for (j = 10; j > i; j--)
        {
        case 2:
            printf("case 2:\n");
            for (k = j - 1; k > 0; k--)
            {
            case 6:
                printf("case 6:\n");
            default:
                printf("%d-%d-%d\n", i, j, k);
            }
        case 5:
            printf("case 5:\n");
            printf("%d-%d\n", i, j);
            break;
        }
        break;
    case 3:
        printf("case 3:\n");
        break;
    }
    return 0;
}

参数处理允许您设置种子,因此您可以根据需要重现结果。我不会声称它有用。确实,它没有用。请注意,循环内的break 会中断循环,而不是switch

基本上,case 标签(和default)必须在switch 的范围内,并且与最内层的switch 相关联。他们几乎没有其他限制。您必须小心不要跳过变量初始化等(这就是为什么jkswitch() 之外定义的原因)。但除此之外,它们只是标签,控制权会在“适当”时流向它们。

【讨论】:

    【解决方案2】:

    C/C++ 中的switch 语句是美化的goto 语句(具有一些优化优势)。

    因此,您可以像使用 goto 标签一样使用 case 标签。特别是,只要不绕过任何变量初始化,就可以在块内跳转。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-10-05
      • 2020-03-31
      • 1970-01-01
      • 2019-12-12
      • 2011-02-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多