为什么switch语句需要休息?

时间:2008-10-31 02:56:42

标签: c language-design

给出一个简单的switch语句

switch (int)
{
    case 1 :
    {
        printf("1\n");
        break;
    }

    case 2 : 
    {
        printf("2\n");
    }

    case 3 : 
    {
        printf("3\n");
    }
}

在案例2中缺少break语句意味着在案例3的代码中将继续执行。 这不是意外;它是这样设计的。为什么做出这个决定?这提供了什么好处与块的自动中断语义?理由是什么?

9 个答案:

答案 0 :(得分:145)

许多答案似乎都集中在原因要求break声明的能力上。

我认为这只是一个错误,主要是因为在设计C时,对于如何使用这些结构的经验并不多。

Peter Van der Linden在他的“专家C编程”一书中提到了这个案例:

  

我们分析了Sun C编译器源代码   看看默认下降的频率   通过使用。太阳ANSI C   编译器前端有244个开关   声明,每个都有一个   平均7例。堕落   仅发生在所有这些案件中的3%。

     

换句话说,正常开关   行为是错误 97%的时间。   它不只是在编译器中 - 在...上   相反,在使用跌倒的地方   在这个分析中,它经常是   更频繁发生的情况   在编译器中比在其他软件中,   例如,在编译运算符时   可以有一个或两个   操作数:

switch (operator->num_of_operands) {
    case 2: process_operand( operator->operand_2);
              /* FALLTHRU */

    case 1: process_operand( operator->operand_1);
    break;
}
     

案件失败是如此广泛   被认为是有缺陷的   甚至是特别评论大会,   如上所示,告诉lint“这是   真的是其中3%的情况之一   希望失败。“

我认为C#在每个case块的末尾需要一个显式的跳转语句是一个好主意(同时仍然允许堆叠多个case标签 - 只要只有一个语句块)。在C#中你仍然可以让一个案例落到另一个案例中 - 你只需要通过使用goto跳转到下一个案例来明确表达。

糟糕的是,Java并没有抓住机会摆脱C语义。

答案 1 :(得分:30)

在很多方面,c只是标准装配惯用语的干净界面。在编写跳转表驱动的流控制时,程序员可以选择通过或跳出“控制结构”,跳出需要一个明确的指令。

所以,c做同样的事情......

答案 2 :(得分:21)

显然要实现Duff的设备:

dsend(to, from, count)
char *to, *from;
int count;
{
    int 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);
    }
}

答案 3 :(得分:18)

如果案件被设计为隐含破坏,那么你就无法实现这一目标。

case 0:
case 1:
case 2:
    // all do the same thing.
    break;
case 3:
case 4:
    // do something different.
    break;
default:
    // something else entirely.

如果开关设计为在每个案例后隐式突破,您将无法选择它。开关盒结构的设计方式更加灵活。

答案 4 :(得分:15)

switch语句中的case语句只是标签。

当你打开一个值时,switch语句基本上会对带有匹配值的标签执行 goto

这意味着中断是必要的,以避免传递到下一个标签下的代码。

至于为什么以这种方式实现的原因 - 在某些情况下,switch语句的性能可能很有用。例如:

case optionA:
    // optionA needs to do its own thing, and also B's thing.
    // Fall-through to optionB afterwards.
    // Its behaviour is a superset of B's.
case optionB:
    // optionB needs to do its own thing
    // Its behaviour is a subset of A's.
    break;
case optionC:
    // optionC is quite independent so it does its own thing.
    break;

答案 5 :(得分:8)

允许以下内容:

switch(foo) {
case 1:
    /* stuff for case 1 only */
    if (0) {
case 2:
    /* stuff for case 2 only */
    }
    /* stuff for cases 1 and 2 */
case 3:
    /* stuff for cases 1, 2, and 3 */
}

case关键字视为goto标签,它更自然地来了。

答案 6 :(得分:3)

当几个案例需要执行相同的代码(或顺序相同的代码)时,它消除了代码重复。

由于在汇编语言级别上,它并不关心你是否在每一个之间断开,因此无论如何都会出现零开销,所以为什么不允许它们,因为它们在某些情况下具有显着优势。

答案 7 :(得分:2)

我碰巧遇到了将向量中的值分配给结构的情况:必须以这种方式完成:如果数据向量短于结构中数据成员的数量,则其余成员将保留其默认值。在那种情况下,省略break是非常有用的。

switch (nShorts)
{
case 4: frame.leadV1    = shortArray[3];
case 3: frame.leadIII   = shortArray[2];
case 2: frame.leadII    = shortArray[1];
case 1: frame.leadI     = shortArray[0]; break;
default: TS_ASSERT(false);
}

答案 8 :(得分:0)

正如许多人在此指定的那样,允许单个代码块适用于多种情况。对于您的switch语句,应该更常见,而不是您在示例中指定的“每个案例的代码块”。

如果每个案例都有一个代码块而没有漏掉,也许您应该考虑使用if-elseif-else块,因为这似乎更合适。