【问题标题】:Switch case weird scoping切换案例奇怪的范围
【发布时间】:2013-06-19 13:16:07
【问题描述】:

查看一些第 3 方 C 代码时,我遇到了类似的情况:

switch (state) {
case 0: 
    if (c=='A') { // open brace
        // code...
    break; // brace not closed!
case 1:
    // code...
    break;
    } // close brace!
case 2:
    // code...
    break;
}

我正在查看的代码中似乎只是一个错字,但令我惊讶的是它编译时没有出现错误。

为什么这是有效的 C?
与在预期位置关闭大括号相比,此代码的执行有什么影响?
有什么情况可以使用吗?

编辑:在示例中,我查看了所有中断(如上) - 但答案也可能包括在情况 0 或 1 中不存在中断时的行为。

【问题讨论】:

  • 嗯,这是因为 switch-case 的奇怪 goto label 实现。虽然您的特定案例可能有点奇怪并且难以提出用例(尽管请查看 Duff's Device),但失败案例的一般概念(当您离开 break 时)确实可以很有用。
  • 如果您现在想了解 Duff 的设备 的工作原理,here 是一些。
  • 在这种情况下,case 1: 被编译器视为单独的标签。语法完全有效,但几乎可以肯定(从上下文中)在这种情况下存在逻辑错误。通过发送state ==1 进行测试,您将看到不正确的结果。

标签: c syntax switch-statement duffs-device


【解决方案1】:

它不仅有效,在实际代码中也使用了类似的结构,例如Duff's Device,这是一个用于复制缓冲区的展开循环:

send(to, from, count)
register short *to, *from;
register count;
{
        register n = (count + 7) / 8;
        switch(count % 8) {
        case 0: do {    *to = *from++;
        case 7:         *to = *from++;
        case 6:         *to = *from++;
        case 5:         *to = *from++;
        case 4:         *to = *from++;
        case 3:         *to = *from++;
        case 2:         *to = *from++;
        case 1:         *to = *from++;
                } while(--n > 0);
        }
}

由于switch 语句实际上只是计算一个地址并跳转到它,很容易看出它为什么会与其他控制结构重叠;其他控制结构中的行也有可以作为跳转目标的地址!

在您介绍的情况下,想象一下您的代码中是否没有switchbreaks。当您执行完if 语句的then 部分后,您只需继续,这样您就会陷入case 2:。现在,既然你有switchbreak,那么break 可以突破什么很重要。根据MSDN page, “The C break statement”

break 语句终止最近的封闭 doforswitch 的执行>while 语句。控制权传递给终止语句之后的语句。

因为最近的封闭 doforswitchwhile 语句是您的 switch (注意 if 不包含在该列表中),那么如果您在 then 块内,则转移到 switch 语句的外部。不过,更有趣的是,如果您输入 case 0 会发生什么,但 c == 'A' 是假的。然后if 将控制权转移到then 块的右大括号之后,然后您开始执行case 2 中的代码。

【讨论】:

  • c!='A' 的情况按照您的解释方式是有道理的 - 但这与快速浏览代码的直觉相去甚远!
  • @Ricibob 我承认确实需要一定程度的将 C 视为“高级程序集”。如果您花了一些时间研究如何将 C 代码编译为汇编/机器代码,它会变得更容易一些。例如,在if condition then 语句中,只有一次跳转;如果条件为 false,则跳转到 then 部分之后。如果你最终进入then 部分,继续执行也会让你到达那里。不需要break。然而,在迭代形式中,例如while condition block,该块被编译为跳回开始以测试…
  • @Ricibob ...再次条件,要么再次进入块,要么跳转到块之后。所以迭代构造需要break,因为主体会将它们带回开始。 switch 有点奇怪,因为它不进行迭代。它只需要break,因为程序员希望只执行一个案例而不执行其余案例是很常见的,并且在没有中断的情况下,他们必须在 switch 语句之后编写一个新标签,并从每个案例中编写一个 goto .无论如何,除非你已经……
  • @Ricibob ...有一个很好的理由,所以你不太可能在野外看到它,你应该更不可能写它。
  • 感谢您的解释。 Switch 显然仍然水深火热——它在表面上看起来很直,但在引擎盖下可能会发生很多事情。让我欣赏为简化 C# 中的切换所做的选择。我下定决心永远不要使用这个特殊的开关结构......
【解决方案2】:

在 C 和 C++ 中,只要不跳过任何变量声明,就可以跳入循环和 if 块。您可以查看this answer 以查看使用goto 的示例,但我不明白为什么相同的想法不适用于switch 块。

语义与 } 高于 case 1 时的语义不同。
这段代码实际上说如果state == 0c != 'A' 然后转到case 2,因为那是if 语句的右大括号所在的位置。然后它处理该代码并点击case 2 代码末尾的break 语句。

【讨论】:

  • 我认为你可以跳转到包含变量的块,只要你不从外部跳转到具有可变修改类型的标识符范围的内部(C11 6.8 .4.2p1 和 6.8.6.1p1)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-03-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多