使用switch语句没有大括号是否有用?

在H&S5中,我遇到了不使用大括号的“最离奇的”switch语句(8.7.1,p.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语句在switchexpression式后面有一个语句和右括号。

但是在我的编程实践中,我再也没有遇到过这样一个奇怪的switch语句(我不想看到任何我需要负责的代码),但是我开始想:

你们中的任何一个人是否会知道这样一个转换expression式,一个没有使用大括号,但仍然有意义? 不只是switch (i); (这是合法的,但一个NOP),但至less使用两个具有某种有用的目的的案例标签?

如果你在macros中使用控制结构,一个switch而不是if方便,因为它没有else悬而未决的问题。

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

有了这个,如果这个macros的用户把它放在一个附加条件下,你就没有惊喜

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

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

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

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

这是丹尼斯·里奇(Dennis Ritchie)1972年在第一个C编译器上编写的一个例子。 链接到我刚刚链接的页面脚下的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年的代码,很明显丹尼斯是switch语句的粉丝 – 他用了很多。 由于缺乏其他数据types的可能性,几乎所有的东西都被编码为int,所以并不奇怪。 他的编译器实现在这个阶段没有使用任何结构,因为他只是将它们添加到语言中。 dynamic调度,vtable和多态性还有很长的路要走。 我已经试过了,但没有find一个参考,但如果我记得丹尼斯“发明”转换语句正确或至less贡献的想法,导致他们在C中的forms,并认为他是他最好的或最自豪的语言补充之一

省略大括号的能力使得开关语句在forms上类似于ifwhilewhile语句,有助于简化和统一语法。 在C语法中查看select语句和迭代语句生成(例如Kernighan和Ritchie的附录A13,我的副本中的236-237页),在这些东西被定义的地方。

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

我想过另一个案子。

假设我有一个unsigned chartypes的计数器,表示一个循环的迭代次数,但是如果计数器等于零,则需要循环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位处理器上,我甚至可以! 🙂

第6.8.4.2节switch语句说:

switch语句使得控制跳转到,进入或通过作为开关主体的语句,具体取决于控制expression式的值,以及在交换机上或交换机上是否存在默认标签和任何情况标签的值身体。 案例或默认标签只能在最近的封闭的switch语句中访问。

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

在实践中,出于可读性原因,开关与支架一起使用(甚至在Duff的装置中)。 加大括号不会有什么坏处。