最近,我遇到过这样一段代码(不是真正的代码,而是一个基于它的简短示例):
#include <stdio.h>
int main()
{
int n;
printf ("n? ");
scanf ("%d", &n);
n%2 && printf ("N is odd\n"); /* <-- this is it */
return 0;
}
如果有人没有得到它,这段代码相当于:
int main()
{
int n;
printf ("n? ");
scanf ("%d", &n);
if (n%2)
printf ("N is odd\n");
return 0;
}
使用GCC 4.4.5-8针对x86-64位编译的此代码的反汇编为n
进行评估且有条件地调用printf()
的部分提供了此参数:
andl $1, %eax
testb %al, %al
je .L3
movl $.LC2, %eax
movq %rax, %rdi
movl $0, %eax
call printf
testl %eax, %eax
.L3:
movl $0, %eax
leave
ret
听起来像if
语句会产生的代码。 “标准”方式给出了这个:
andl $1, %eax
testb %al, %al
je .L2
movl $.LC2, %edi
call puts
.L2:
movl $0, %eax
leave
ret
更短,也更快一点,因为编译器可以使用puts()
代替printf()
,因为它已检测到printf()
正用于打印单个字符串,并且它的返回值没有使用。前一个示例必须在&&
之后评估第二个表达式,因为第一个表达式被评估为true,因此必须使用printf()
来获取要评估的值。
所以我的观点是:这种快捷方式评估技巧可以被认为是良好的编码吗?它有效,但除了帮助赢得单线C比赛之外,它还能做得更好吗?从我提供的例子来看,我会说不,但可能存在一个证明不然的例子吗?
注意:在尝试编译原始代码时,编译器甚至生成了一个ICE(MingW 2.95.2),文本错误说明了“执行do_jump时的错误”或类似内容。
答案 0 :(得分:5)
这种技术一般没用,因为它与普遍接受的惯例相反,导致代码不可读。 (请注意,这种技术在其他语言中是可以接受的,例如perl,它是一种常用的习语。)
您可能被迫使用此技术的一个地方是类似函数的宏。
#define foo(x, y) (((x) % 2) && (y))
您无法将宏编写为
#define foo(x, y) if ((x) % 2) (y)
因为这会弄乱像
这样的东西if (a) foo(x, y); else bar();
的常用解决方法
#define foo(x, y) do { if ((x) % 2) (y); } while (0)
不适用于逗号运算符。
for (i = 0, foo(x, y); i < 10; i++) ...
也就是说,任何对这种习语的使用都应该得到很好的评论。