我最近遇到了如下所示的代码片段,我期望它是一个语法错误,但令我惊讶的是,该代码产生了有效的输出。
#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
任何人都可以解释
答案 0 :(得分:5)
case X: some_statement;
是labeled statement (6.8.1) ,就像goto_label: some_statement;
一样,唯一的警告是case
/ default
标签只能出现在{{1 }}(可能在任意嵌套的复合语句中)。从语法上讲,这使得switch
语句仅与case
es非常松散耦合。
在语义上,switch
可以像计算的switch
一样实现,并且像常规的goto
一样,它们可能会在任何地方跳跃很多(在C11中,您不能跳过{{ 3}}声明),包括在循环内部(有关其他说明,请参见VLA)。
在您的示例中,goto
由于case 3:
而被跳过,但是break
的确遵循,因为case 4:
之后的break
是一个循环中断{ {1}},而不是断断续续的case 3:
({{1} / break
始终适用于它们可以适用的最近的构造)。
答案 1 :(得分:0)
switch
语句的正式C语法是:
开关 ( 表达式 ) 声明
任何声明可以是带标签的声明,其语法包括:
案例 常量表达式 : 声明
这意味着您可以在switch
主体中放入几乎所有内容:它可以是包含多个语句的复合语句以及包含以下内容的do
-while
语句多个语句和case
标签可以添加到任何这些语句的前缀。
编译器仅使用跳转指令(在其抽象机内部;在优化后最终可能会与其他指令一起使用)来实现切换。诸如do
-while
之类的循环语句是通过测试控制表达式并有条件地跳转的代码实现的。因此,尽管您在结构化语言中想到的是一个不错的结构,但它会归结为跳转指令,并且可以按需将它们交织在一起。
跳跃并不是最令人不安的部分。对象初始化和生存期更加令人关注。如果不小心,switch语句可能会无意间跳过对象的初始化。
永远不会执行“ 3”的printf
,因为控制权从switch
跳到case 2
,然后中断,从而退出了do
-while
。这将使代码保留在case 4
语句中,该语句显示“ 4”,然后脱离switch
语句。