#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
但根据编译器它不是真的,所以请解释逻辑....
#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
但根据编译器它不是真的,所以请解释逻辑....
没有逻辑,这是未定义的行为,因为
++b * ++b * ++b;
修改和读取b
3 次,没有交错序列点。
奖励:如果你尝试,你会看到另一个奇怪的行为CUBE(1+2)
。
除了 Luchian Grigore 所说的(这解释了为什么你会观察到这种奇怪的行为),你应该注意到这个宏是可怕的:它可能会导致微妙且非常难以追踪的错误,尤其是当使用具有副作用的语句调用时(like ++b
) 因为这将导致语句执行多次。
你应该从中学到三件事:
切勿在宏中多次引用宏参数。虽然这条规则有例外,但您应该更愿意将其视为绝对规则。
如果可能,尽量避免使用包含副作用的语句调用宏。
尽可能避免使用类似函数的宏。请改用内联函数。
在序列中多次更改相同变量的未定义行为。并且因为这个原因,您将使用不同的编译器为您的代码获得不同的结果。
碰巧我a = 150 and b = 6
的编译器也得到了相同的结果。
gcc version 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5)
您的宏表达式 a = CUBE(++b);
扩展为
a = ++b * ++b * ++b;
并且在完整表达b
结束之前改变不止一次。.
但是我的编译器如何在低级别转换这个表达式(可能你的编译器做类似的事情,你可以尝试使用相同的技术)。为此,我使用-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
cab 只能评估这个序列b=3
和你的表达式5*5*6