#include<stdio.h>
#define CUBE(x) (x*x*x)
int main()
{
int a, b=3;
a = CUBE(++b);
printf("%d, %d\n", a, b);
return 0;
}
此代码返回a=150
和b=6
的值。请解释一下。
我认为当它执行时a
的值将被计算为a=4*5*6=120
但根据编译器不是真的,所以请解释逻辑......
答案 0 :(得分:9)
没有逻辑,它是未定义的行为,因为
++b * ++b * ++b;
修改并读取b
3次,没有交错序列点。
奖励:如果您尝试CUBE(1+2)
,您会看到另一种奇怪的行为。
答案 1 :(得分:5)
除了Luchian Grigore所说的(这解释了为什么你会观察到这种奇怪的行为),你应该注意到这个宏可怕:它会导致微妙且非常难以追踪的bug,特别是当使用具有副作用的语句(如++b
)调用时,因为这会导致语句多次执行。
你应该从中学到三件事:
从不在宏中多次引用宏参数。虽然这条规则有例外,但您应该将其视为绝对的。
尝试避免使用包含副作用的语句调用宏。
尽可能避免类似函数的宏。请改用内联函数。
答案 2 :(得分:2)
它的undefined behavior更改一个序列中的同一个变量。因为这个原因,您将使用不同的代码编译器获得不同的结果。
我偶然得到了与我的编译器相同的结果a = 150 and b = 6
。
gcc version 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5)
您的宏表达式a = CUBE(++b);
扩展为
a = ++b * ++b * ++b;
b
在full expression.结束前更改一次。
但我的编译器如何在低级别转换此表达式(可能是您的编译器做的类似,您可以尝试使用相同的技术)。为此,我使用-S
选项编译了源代码C,并得到了汇编代码。
gcc x.c -S
您将获得x.s
个文件。
我正在展示部分有用的asm代码(读取评论)
因为您想知道150
如何输出,这就是为什么我要添加我的答案
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movl $3, -8(%rbp) // b = 3
addl $1, -8(%rbp) // b = 4
addl $1, -8(%rbp) // b = 5
movl -8(%rbp), %eax // eax = b
imull -8(%rbp), %eax // 5*5 = 25
addl $1, -8(%rbp) // 6 `b` become 6 and
imull -8(%rbp), %eax // 6 * 25 = 150
movl %eax, -4(%rbp) // 150 assign to `a` become 150
movl $.LC0, %eax // printf function stuff...
movl -8(%rbp), %edx
movl -4(%rbp), %ecx
movl %ecx, %esi
movq %rax, %rdi
在检查这个汇编代码时,我可以理解它评估表达式
a = 5 * 5 * 6
因此a
变为150
,并且在三个增量后b
变为6
。
虽然不同的编译器会产生不同的结果,但我认为150
仅对b=3
的{{1}}以及5*5*6