【问题标题】:While loop around switch cases in CWhile循环在C中的switch case
【发布时间】:2018-07-29 19:37:33
【问题描述】:

我最近遇到了如下所示的代码 sn-p,我原以为这是一个语法错误,但令我惊讶的是,该代码产生了有效的输出。

#include <stdio.h>

int main(void) {
    int x = 2;

    switch(x) {
    case 1: printf("1"); break;

        do {
            case 2: printf("2 "); break;
            case 3: printf("3 "); break;
        } while(++x < 4);

        case 4: printf("4"); break;
    }
    return 0;
}

output: 2 4

编译器:GCC 6.3

我发现了一个类似的问题,但它不能完全证明上述条件, Mixed 'switch' and 'while' in C

谁能解释一下,

  1. 这里到底发生了什么?
  2. 为什么不是语法错误?
  3. 为什么会跳过案例“3”?

【问题讨论】:

  • 您提问的 Stack Overflow 中的一个 cmets 指向另一个关于 Duff’s Device 的问题。它是有效的 C,因为 case 标签的语法允许它们作为任何语句的前缀。编译器只是将它们实现为跳转指令(在其抽象机器中)。

标签: c while-loop switch-statement


【解决方案1】:

case X: some_statement;labeled statement (6.8.1) 就像 goto_label: some_statement; 一样,唯一需要注意的是 case/default 标签只能出现在 switch 的主体内(可能在任意嵌套的复合语句中)。这使得case 语句与switches 在语法上的耦合非常松散。

从语义上讲,switches 可以实现为计算出的gotos 和普通的gotos,它们几乎可以跳转到任何地方(在 C11 中,你不能跳过 VLA 声明),包括内部一个循环(参见https://en.wikipedia.org/wiki/Duff%27s_device#Mechanism 以获得其他描述)。

在您的示例中,case 3:break 而被跳过,但case 4: 确实紧随其后,因为case 3: 之后的break 是循环中断break,而不是开关中断@987654340 @(break/continue 总是适用于他们可以应用的最近的构造)。

【讨论】:

  • 如果我理解正确,break inside case '2' 会导致循环终止,并且在 case '4' 和 switch 退出时执行失败。
  • @prasadM 是的。如果您想轻松查看 Duff 设备的代码,请忘记与 case 和 fallthrough 紧密耦合的开关,只需将 case 标签视为一种只能在 switch 中使用的特殊 goto 标签。有了这个,跌倒是很自然的。 breaks 在do{/*...*/}while(/*...*/); 内直接跳到while(/*...*/); 之后的分号之后。一个case 4:-labeled 声明如下。它被执行是因为它只是一个带标签的语句,所以代码没有理由跳转到其他地方。
【解决方案2】:

switch 语句的官方 C 语法是:

switch ( 表达式 ) 语句

任何语句都可以是labeled-statement,其语法包括:

case 常量表达式 : 语句

这意味着您可以在 switch 的主体中添加几乎任何您想要的内容:它可以是包含多个语句的复合语句和包含多个语句的 do-while 语句,以及 @987654325 @ 标签可以作为任何这些语句的前缀。

编译器仅使用跳转指令实现切换(在其抽象机器内;优化后它们可能最终成为其他指令)。诸如do-while 之类的循环语句是用测试控制表达式并有条件地跳转的代码实现的。因此,尽管您认为结构化语言的结构很好,但它归结为跳转指令,并且可以根据需要交织。

跳跃并不是其中最令人不安的部分。对象初始化和生命周期更受关注。如果不注意,Switch 语句可能会无意中跳过对象的初始化。

“3”的printf 永远不会执行,因为控制从switch 跳转到case 2,然后中断,退出do-while。这会在 case 4 语句中留下代码,该语句会打印“4”,然后跳出 @9​​87654334@ 语句。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-19
    • 2020-11-07
    • 1970-01-01
    • 2014-06-13
    • 2021-04-09
    • 1970-01-01
    相关资源
    最近更新 更多