在switch语句中第一个'case'之前的代码

18
在C语言中,可以在第一个case标签之前编写代码。是否有任何情况下这样做是有用的,还是只是“死代码块”?例如:
switch (...)    {
  {
    int a = 0x2a;
    printf("%d\n", a);
  }
  case 0:
    ...
}

1
我尝试了许多不同的示例,上面的示例只是为了澄清。但仍有可能我错过了(GNU) C的某些特性,这个领域还有待探索。 - tur1ng
4个回答

21

我认为这不是一个特性,而是C语言对于switch/case处理方式的一个副作用——仅仅是一系列没有语法限制的跳转目标。这就是为什么Duff's设备能够起作用,也是为什么第一个case之前的代码永远不会运行。

如果你查看生成的汇编代码,你会发现代码将直接被跳过:

    mov ecx, DWORD PTR _x$[ebp]
    mov DWORD PTR tv64[ebp], ecx
    cmp DWORD PTR tv64[ebp], 0                  ; here begins the switch
    je  SHORT $LN1@main                         ; jump to case 0
    jmp SHORT $LN4@main                         ; jump out of the switch
; Line 8
    mov DWORD PTR _a$752[ebp], 42
; Line 9
    mov edx, DWORD PTR _a$752[ebp]              ; here we have the dead code
    push    edx
    push    OFFSET $SG754
    call    _printf
    add esp, 8
$LN1@main:                                      ; and here case 0
; Line 12
    push    OFFSET $SG756
    call    _printf
    add esp, 4
$LN4@main:
; Line 15
    xor eax, eax
    mov esp, ebp
    pop ebp
    ret 0

16

C标准文档提供了一个例子,解释了这种类型的结构的行为(6.8.4.2/7 "switch语句"):

例如,在下面的伪代码中

switch (expr)
{
    int i = 4;
    f(i);
case 0:
    i  =  17;
    /*  falls through into default code  */
default:
    printf("%d\n", i);
}
当控制表达式非零值时,具有自动存储周期 (在块中) 的标识符为 i 的对象存在但从未初始化,因此调用 printf 函数将访问一个不确定的值。同样,无法到达函数f的调用。尽管这是允许的,但这很容易成为“你可以做就不一定应该这么做”的情况之一。这种结构很容易令人困惑,并且可能导致使用未初始化的变量,因为很难确定初始化是否发生以及何时发生。

1
C标准文档可在以下网址找到:http://www.open-std.org/JTC1/SC22/wg14/www/docs/n1336.pdf - tur1ng

14

声明变量,其作用域仅限于switch块,可能是有用的(但请注意,这些变量的任何初始化程序都将被跳过):

switch (...)
{
    int n;

    case 0:
    ...
}

理论上,您也可以在那里放置使用 goto 调用的代码。


如果你想在这个作用域中使用一个变量,在 switch 语句之前开始一个块,并在其中声明变量,即将整个 switch 语句包含在另一组括号中。在 switch 中声明它但不在 case 中声明是行不通的。 - Martin
2
@Martin:变量声明确实有效,因为它们不会产生任何输出代码;它们只是为了让编译器注意到“啊,我知道这个变量,我在那里看到过它”。 - Joey
这些变量的任何初始化器都可以被跳过,这是否真的那么简单(初始化器要么被跳过,要么不被跳过),还是跳过初始化器会导致未定义行为/不合法程序?我记不清了。 - Steve Jessop
你真的建议使用 goto 吗?我认为那是极其不好的做法。 - Arc676
6
@Arc676 这是一个常见的误解,即使在有经验的程序员中也是如此。它始于Dijkstra的一篇好文章,但正如Steele在“揭穿‘昂贵的过程调用’神话”中所指出的那样,goto语句本身并不是坏的,而是对其滥用才是坏的。Steele说:“[我们]试图通过禁止某个结构来消除不需要的概念和编程风格。” Goto语句可以被很好地使用,但最好非常谨慎地使用它。正如他还指出的,如果滥用其他控制结构,可能会编写混淆的代码。重要的是要避免混淆的代码(可以轻松使用goto编写),而不仅仅是goto语句本身。 - calavicci

1

我不明白你想要什么。为什么不直接在case之前放置代码?

int a = 0x2a;
printf("%d\n", a);
switch (...)    {
case 0:
...
}

这不是你的本意吗?(除了在你的示例中,如果编译器没有抱怨,代码将永远不会运行。)


1
我的示例中选择了随机的printf。 - tur1ng
5
有必要吗?所有的事情都应该被问到。 - Matt Joiner

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