是否有一个使用没有大括号的switch语句的有用案例?

时间:2011-11-14 06:21:32

标签: c language-lawyer

在H& S5中,我遇到了“最离奇”的开关语句(8.7.1,第277页),没有使用大括号。
这是样本:

switch (x)
    default:
    if (prime(x))
        case 2: case 3: case 5: case 7:
            process_prime(x);
    else
        case 4: case 6: case 8: case 9: case 10:
            process_composite(x);

这个想法似乎是为了避免prime(x)对最常见的小数字的开销。

当我看到那句话时,我对丢失的大括号感到困惑,但检查官方语法(C1X pre-standard,6.8.4,p.147),语法是正确的:一个switch语句只是有一个语句在切换表达式和右括号之后。

但是在我的编程实践中,我再也没有遇到过这样一个奇怪的切换语句(我不想看到任何代码,我必须承担责任),但我开始疑惑:

你们中的任何人都知道这样的开关表达式,一个没有使用大括号,但仍然有意义吗?不只是switch (i);(这是合法的,而是NOP),但使用至少两个具有某种有用目的的案例标签?

5 个答案:

答案 0 :(得分:9)

如果您在宏中使用控制结构,switch而不是if会派上用场,因为它没有悬空else问题。

#define DEBUG_PRINT(...) switch (!debug_mode) case 0: fprintf(__VA_ARGS__)

如果该宏的用户将其置于附加条件中,您就不会感到意外

if (unclear) DEBUG_PRINT(stderr, "This is really %unclear\n", unclear);
else {
 // do something reasonable here
}

这样的调试宏具有始终编译(然后最终优化)的优点。因此,调试代码必须在程序的所有实时时间内保持有效。

此处还要注意,switch不使用{}非常重要,否则if/else示例也无效。所有这些都可以通过其他方式(if/else(void)0do/while技巧来实现),但这个是我所知道的最方便的。

不要误解我的意思,我不是说每个人都应该在宏中使用控制结构,你当然应该知道你在做什么。但有些情况是合理的。

答案 1 :(得分:2)

这是Dennis Ritchie在1972年his work on the first C compiler期间写的一个例子。在我刚刚链接到的页面底部链接的c02.c模块包括

easystmt()
{
    extern peeksym, peekc, cval;

    if((peeksym=symbol())==20)  /* name */
        return(peekc!=':');  /* not label */
    if (peeksym==19) {      /* keyword */
        switch(cval)
        case 10:    /* goto */
        case 11:    /* return */
        case 17:    /* break */
        case 18:    /* continue */
            return(1);
        return(0);
    }
    return(peeksym!=2);     /* { */
}

从阅读他的1972年代码可以清楚地看出丹尼斯是开关陈述的粉丝 - 他使用了很多。鉴于缺乏其他数据类型的可能性,几乎所有内容都被编码为int,因此并不令人惊讶。他的编译器实现在那个阶段没有使用任何结构,因为他只是在将它们添加到语言中。动态调度,vtable和多态性还有很长的路要走。我已经尝试过但未能找到这方面的参考但是如果我没记错,Dennis“发明了”转换语句或者至少提供了导致他们在C中采用的形式的想法,并且认为这是他最好或最自豪的补充之一语言

省略大括号的能力使得switch语句与iffordowhile语句形式上类似,有助于简化和统一语法。请参阅C语法中的selection-statement和iteration-statement产生(例如,在Kernighan和Ritchie的附录A13中,我的副本中的第236-237页),其中定义了这些内容。

显然,人们可以随时添加大括号,但对于像这样的简单例子来说,这似乎很重要。这个例子可以被编码为析取if语句,但我认为Dennis对切换的一个想法是,编译器更明显地提供了根据所涉及的特定常量优化分支逻辑的实现的机会。

答案 2 :(得分:1)

我想到了另一个案例。

假设我有一个unsigned char类型的计数器,表示循环的迭代次数,但如果计数器等于零,则需要经过256次循环。如果我的想法是正确的,你可以按如下方式编写代码:

uint8_t counter;
/* counter will get its value here somewhere */
switch (counter)
    default:
        while (0 < counter)
        {
            case 0:
                /* Perform action */
                counter--;
        }

这当然假设0x00的下溢导致无符号字符的0xFF。但它适用于我的所有环境,即使PC Lint会抱怨...... 是的,它包含大括号,但仅适用于while,而不适用于switch。如果你知道更好的事情,让我听听吧!

我会像这样编程吗?决不! ...好吧,在一个小型的8位处理器上我甚至可能! : - )

答案 3 :(得分:0)

实际上,出于可读性原因,开关与支架一起使用(即使在Duff的设备中)。添加牙箍没有任何害处。

答案 4 :(得分:0)

第6.8.4.2节switch语句说:

  

switch语句导致控制跳转到,进入或超过   声明是开关体,取决于a的值   控制表达式,以及是否存在默认标签和   开关体上或开关体内的任何案例标签的值。案例或默认情况   标签只能在最近的封闭开关内访问   言。

术语 switch-body 最接近的switch-statement 似乎不需要大括号。 所以你说得对,看起来很奇怪但是合法。 (之前从未见过)