没有break的switch-case语句

36
根据我正在阅读的这本书:
问: 如果我在 switch-case 语句中省略了 break 会发生什么?
答: break 语句使程序执行退出 switch 结构。如果没有它,执行将继续评估以下 case 语句。
假设我有类似下面的代码:
switch (option}{
    case 1:
    do A;
    case 2:
    do B;
    default:
    do C;
    break;
}

如果我选择方案1,那么A和C就会完成。如果我选择方案2,则会完成B和C。如果我两个都不选,则只有C会完成。

如果这样做,在执行完C后省略break语句会发生什么?

我认为这些是不好的编程实践,但我很好奇省略break语句会发生什么,以便更深入地了解它的工作原理。谢谢


15
如果我选择方案1,那么A和C就完成了:没有A,但B和C已经完成。 - 101010
12
只需编译一些测试代码,你就会发现我猜测的东西了 :p - user5166622
8
您在选定的情况下执行所有内容,直到看到breakswitch语句结束。因此,可能只执行C,或者先执行B再执行C,或者先执行A和B再执行C,但从不执行A和C。 - Jon
6
顺便说一下,这不是糟糕的编程实践。 - 101010
7
必须提到Duff's Device :) - maddouri
显示剩余4条评论
8个回答

51

您执行从所选案例开始直到看到 break 或者 switch 语句结束的所有内容。因此,可能仅执行 C,或先执行 B 再执行 C,或先执行 A 和 B 然后执行 C,但永远不会执行 A 和 C。


为什么行为会是这样呢?如果匹配了一次,为什么它会简单地忽略后续条件呢? - Abhishek Choudhary

20
  • 如果您在任何一个case中不包含break,那么下面的所有case都将被执行,直到遇到break。

  • 如果您在default中不包含break,它将没有影响,因为在“默认”case下面没有任何case。

  • 通常不使用break被认为是一种不好的做法,但有时由于其穿透性质可能会派上用场。例如:

    case optionA:

//optionA needs to do its own thing, and also B's thing.
//Fall-through to optionB afterwards.
//Its behaviour is a superset of B's.

选项B:

// optionB needs to do its own thing
// Its behaviour is a subset of A's.
break;

选项C:

// optionC is quite independent so it does its own thing.
break;

9
break就像一个goto命令。或者更好的例子,它就像在void函数中使用return一样。由于它处于结尾位置,加不加它没有区别。虽然我喜欢包含它。

1
在问题下面留下的评论之后,我怀疑了这个。这是一个很好的解释,谢谢。 - Lost1
1
嗯,我认为强调break类似于GOTO的性质很重要。 - Kyle Strand
@craigmosey 嗯,我想我所说的是要认识到 switch 本身就像一个 GOTO,因为各个 case 不是 单独的作用域(除非你显式地添加一个作用域)。所以与函数不同,在进入/退出 case没有 自动设置或清理。 - Kyle Strand
@ Kyle Strand 啊,我不知道没有清理,因为整个 switch case 有自己的作用域,所以我认为它会清理。所以基本上,整个 switch case 在退出时不会清理并且就像一堆 goto 一样。虽然它作为一个整体确实有自己的作用域。 - Craig Mosey
3
最好采纳的答案应该涵盖提问者所提及的所有方面。特别要注意编辑以指出在情况1中,A、B和C都将被执行,而不仅仅是A和C。 - fpierrat
显示剩余2条评论

8
switch (option}{
    case 1:
    do A;
    case 2:
    do B;
    case 2:
    do C;
    break;  
    default:
    do C;
}

如果您的选项是1,它会执行所有操作,直到找到break关键字...这意味着switch-->case的执行会被中断 输出:A,然后B,然后C 因此建议在每个case之后加上break
switch (option}{
        case 1:
        do A;
        break;
        case 2:
        do B;
        break;
        do C;
        break;        
        default:
        do D;
    }

如果您选择的是1,输出结果将为:仅为 A ...

注意:default 不需要 break


4
在许多评论和答案中,我看到省略break语句是一种不好的做法。但在某些情况下,我个人认为它非常有用。
让我们举一个非常简单的例子。这可能不是最好的例子,只是拿来说明:
- 登录失败时,您需要记录失败的尝试。
- 对于第三次错误的尝试,您想要记录并执行一些其他操作(警报管理员、阻止账户等)。
由于在第一次和第二次尝试时执行的操作相同,因此在这两者之间不需要break,再次重写相同的命令。现在,在第三次尝试中,您想要先执行其他操作,然后再对日志进行记录。不需要break,因为它会自动运行先前的日志操作。
switch (badCount) {
    case 3: //only for 3
        alertAdmin();
        blockAccount();
    case 2: //for 2 AND 3
    case 1: //for 1 AND 2 and 3
        errorLog();
        badCount++;
}

在我看来,如果将公共代码用于不同情况是非常糟糕的做法,那么 C 语言结构体就根本不会允许这样做。


你应该使用if语句,而不是switch。Switch语句用于堆叠表达式值的不相关情况,但没有人会发现你的代码垂直方向对应其逻辑方向。 - user4396006
1
那么你的意思是没有人能够阅读和理解微软在线文档中的基本示例?请参见https://learn.microsoft.com/en/cpp/c-language/switch-statement-c?view=vs-2019,查看示例代码块2和4。 - fpierrat

2
关键在于执行控制被转移到匹配 case 的语句。例如:
1. switch(x) {
2.   case 1:
3.      do_step1;
4.   case 2:
5.      do_step2;
6.   default:
7.      do_default;
8.   }

将第2、4、6行视为“标签”,用于goto调用。当x = 1时,控制权将转移到第3行,并执行第3、5和7行。


"goto calls" 这个说法很有启发性,我学到了新东西。谢谢。 - user12211554

0
没有break语句,每次在switch中匹配成功后,该case和其后续的case语句都会执行,直到遇到break语句或者switch结束。

0

请自行尝试 - 使用可在此处找到的ideone运行代码。

#include <stdio.h>

void doA(int *i){
    printf("doA i = %d\n", *i);
    *i = 3;
}

void doB(int *i){
    printf("doB i = %d\n", *i);
    *i = 4;
}

void doC(int *i){
    printf("doC i = %d\n", *i);
    *i = 5;
}

int main(void) {
    int i = 1;
    switch(i){
        case 1:
            doA(&i);
        case 2:
            doB(&i);
        default:
            doC(&i);
            break;
    }
    return 0;
}

输出:

doA i = 1
doB i = 3
doC i = 4

注意:

  • 它将执行所选案例中的所有选项,直到看到breakswitch语句结束。因此,可能只执行C,或者先执行B和C,或者A、B和C,但永远不会是A和C。
  • 如果您在处理函数(例如doA)内更改在switch中分析的变量的值,则不会影响上述流程。

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