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

时间:2015-03-13 01:54:44

标签: c++ c

假设:

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之前添加的额外代码是“合法的”吗?我在例子中从未见过这个。

5 个答案:

答案 0 :(得分:12)

首先介绍switch(真的)的工作原理:

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

switch (x) {
case 1:
    foo();
    break;

case 2:
    bar();
    break;
}

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

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

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


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

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

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


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

  

if (表达式)声明

,而case的语法是

  

切换(表达式)声明

关于语句的唯一区别(在C-C ++中添加了一些如上所述的限制)是允许它包含break标签(和{{ 1}})对于switch而不是if(除非if出现在switch内)。

if一样,您甚至可以不用括号来编写如下代码。 (这是不必要的混淆是另一种讨论。)

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

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

  

标记的语句:
  标识符 声明
  案例 常量表达式 声明
  默认 声明

答案 1 :(得分:7)

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

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在函数内部编译。 C编译器看到iy在由大括号分隔的块内声明,因此将接受它。但是,printf语句将打印iy的垃圾,因为从不执行任务。这是因为switch语句形成了case的跳转,该跳转对应于switch头部的表达式。因此无法到达左大括号和第一个case之间的可执行代码。请参阅Why can't variables be declared in a switch statement?,它没有完全解释相同的情况,但确实有关于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部分中设置的值。在这种情况下它起作用的事实并不是说它是推荐的做法。 :)

答案 2 :(得分:3)

控件可以到达switch头部和第一个case表达式之间的语句 - 但其他一些控件结构必须发送它那里。例如,没有什么可以阻止你interlacing a switch and a loop

#include <stdio.h>

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;
}

-std=c11模式下没有任何来自gcc 4.9或clang 3.5的投诉进行编译,并且警告随着时间的推移而上升,并且在运行时打印:

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

答案 3 :(得分:1)

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

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

int test = 1;
int x = 1 ; 
switch ( test ) { 

    //Technically legal, although will never be called.
    std::cout<<somefunc(x)<<std::endl;;  

    //jumps straight here, over any code above it
    case 1 : 
        std::cout<<"1"<<std::endl;
        break;

    case 5 : 
        std::cout<<"5"<<std::endl;
        break;

    default : 
        std::cout<<"Default."<<std::endl;
}  

答案 4 :(得分:0)

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

  

错误C2360:初始化&#39; x&#39;被&#39; case&#39;跳过标签

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

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