我可以将代码放在案例之外的交换机中吗?

假设:

switch ( test ) { // Set some variables, call some functions ? int x = 1 ; int y = function(x) ; // case 1 : // Process for test = 1 ... break; case 5 : // Process for test = 5 ... break; default : // Process for all other cases. ... } 

执行我在第一个case之前添加的额外代码是“合法的”吗? 我在例子中从未见过这个。

首先介绍一下switch (真的)如何工作:

switch通常被认为是一个构造,它根据某个表达式的值选择要执行的代码片段,如

 switch (x) { case 1: foo(); break; case 2: bar(); break; } 

但是,将switch视为计算goto语句的一种forms更为准确。 例如,以下内容完全合法:

 switch (x) { puts("I can't be reached"); case 1: if (cond) { case 2: puts("Either x == 1 && cond, or x == 2"); } } 

根据x的值,程序将跳转到case 1case 2 (如果x既不是1也不是2,则跳过switch )。


您的程序将编译为C(在switch内部包含xy垃圾值,因为跳过了初始化),但不是C ++。 原因是C ++不允许跳转到case标签来跨越变量的初始化。 对于像int这样的简单类型,跳过int x; 允许(因为不涉及初始化),但不跳过int x = 1;

这种差异的主要动机可能是,当涉及构造函数时,跳转到C ++中的初始化的case标签是不安全的。 例如,如果C ++允许在某个范围内定义My_class my_object之后发生case标签,那么跳转到该case标签将跳过my_object的构造函数,但在退出范围时仍然运行其析构函数。

同样的限制适用于C ++中的goto 。 您无法使用它跳转到块并通过变量初始化。


作为旁注, switch遵循与ifwhile相同的一般语法。 if如C11标准(ISO / IEC 9899:2011,第6.8.4节)中给出的语法是

if (表达式)语句

,而switch的语法是

switch (表达式)语句

关于语句的唯一区别(在C-C ++中增加了一些如上所述的限制)是允许它包含一个switch case标签(和break )但不包含if (除非if发生在if中)一个switch )。

就像使用if ,您甚至可以省去大括号并编写如下代码。 (这是否会造成不必要的混乱是另一种讨论。)

 switch (x) case 1: case 2: puts("x is 1 or 2"); 

在语法上, casedefault标签与goto标签属于同一类别。 C11标准的第6.8.1节有以下定义:

标记的语句:
identifier 声明
case constant-expression statement
默认 声明

您可以通过简单的测试了解会发生什么:

 int w = 1; switch (w) { int i = 3; int y = foo(i); case 1: printf("here %d\n", y); printf("here %d\n", i); break; case 2: printf("not here\n"); break; } 

这段代码将使用gcc在函数内部gcc 。 C编译器看到iy在由大括号分隔的块内声明,因此将接受它。 但是, printf语句将为iy打印垃圾,因为从不执行赋值。 这是因为switch语句形成了跳转到与switch头部表达式相对应的case 。 因此无法到达左大括号和第一个case之间的可执行代码。 请参阅为什么不能在switch语句中声明变量? ,它没有解释完全相同的情况,但确实有一些关于switch语句的相关讨论。

如果你在打开警告( gcc -Wall )的情况下编译它,你会得到:

 foo.c: In function 'main': foo.c:19:15: warning: 'y' may be used uninitialized in this function [-Wuninitialized] foo.c:20:15: warning: 'i' may be used uninitialized in this function [-Wuninitialized] 

有趣的是,以下代码将在没有警告和工作的情况下编译:

 int w = 1; switch (w) { int i; int y; case 1: i = 2; y = 3 * i; printf("here %d\n", y); printf("here %d\n", i); break; case 2: i = 1; y = 2; printf("here %d\n", y); printf("here %d\n", i); break; } 

变量按照您的预期打印,因为它们是在switch块的范围内声明的,并且在执行发生的case部分中设置了值。 在这种情况下它起作用的事实并不是说它是推荐的做法。 🙂

控制可以到达switch头和第一个case表达式之间的语句 – 但是其他一些控制结构必须将它发送到那里。 例如,没有什么能阻止你交换开关和循环 :

 #include  int main(void) { int x = 1; switch (x) { do { printf("got here with x=%d\n", x); case 2: puts("case two or loop"); case 1: puts("case one or loop"); } while (++x < 3); } return 0; } 

编译时没有任何来自gcc 4.9或clang 3.5 in -std=c11模式的投诉,并且警告会随着它们的高速而上升,并且在运行时打印:

 case one or loop got here with x=2 case two or loop case one or loop 

有趣的事实:启用优化后,两个编译器实际上都会生成100%的直线代码:

 main: subq $8, %rsp movl $.LC0, %edi call puts movl $2, %esi movl $.LC1, %edi xorl %eax, %eax call printf movl $.LC2, %edi call puts movl $.LC0, %edi call puts xorl %eax, %eax addq $8, %rsp ret 

这是不合法的,当你编译时,你会得到一堆“十字架初始化…”错误。 对这里发生的事情有一个非常好的解释: 获得一堆十字架初始化错误

本质上,switch case通过跳过case语句之间的任何代码来工作,跳过变量的初始化是违法的。 它在技术上是合法的,如果你只是在两者之间放一个函数调用,它将编译,虽然函数永远不会被实际调用。

 int test = 1; int x = 1 ; switch ( test ) { //Technically legal, although will never be called. std::cout< 

我尝试编译类似于你问的代码,它至少出现一个错误:

错误C2360:’case’标签跳过’x’的初始化

您的额外代码从未被执行过,因为在切换检查语句’test’之后,它将转到相应的标签,您的额外代码不在任何标签下,因此它永远被跳过。

为什么你不能在switch语句之前添加额外的代码? 任何原因?