这个问题在这里已有答案:
当我编译这个C代码并在我的机器上运行十几次时,我每次都得到一个不同的9位负数。另一台机器上的另一个clang编译器产生10位不同的正整数。我期待一个奇怪的值,因为右移由负计数未定义,但我很惊讶该值不是一个唯一的数字。相反,我使用相同的输入获得多个值。为什么这不是数学函数?
#include <stdio.h>
int main(void) {
printf("%d", 1 >> -1);
return 0;
}
因为没有定义行为,所以编译器(在Clang的情况下,至少见下文)选择不在寄存器中放置任何参数将传递给printf
。这导致寄存器具有准备C环境并调用main
的启动代码留下的任何值。这恰好是在启动期间使用的一些地址,并且它被用于阻止对软件的攻击的Address Space Layout Randomization随机化。
对clang生成的程序集的检查确认没有值放在将用于此参数的寄存器%esi中(使用带有clang-1000.11.45.5的Apple LLVM 10.0.0,为macOS 10.14.3中的默认目标构建)只有开关-O3
)。
当然,其他编译器可能表现不同,因为行为不是由C标准定义的;这仅仅解释了OP在有限的情况下报告的观察结果。
当您调用未定义的行为时,允许编译器执行它想要的任何操作。它允许段错误,或每次打印零,或者让你的CPU着火。没有什么能真正要求它确定性或每次都给出相同的结果。
在这种情况下,Clang知道未定义的行为发生,所以它只是停止关怀。编译右移指令并不困难 - 因为没有任何义务。它甚至懒得在寄存器中放置任何值,并且预先留下任何在那里发生的值,这是不可预测的并且是有效随机的。