C 按负数右移不仅是未定义的,而且在叮当声中从一个映射到多个

C 按负数右移不仅是未定义的,而且在叮当声中从一个映射到多个,c,one-to-many,undefined-behavior,C,One To Many,Undefined Behavior,当我编译这个C代码并在我的机器上运行十几次时,每次都会得到一个不同的9位负数。另一台机器上的另一个铿锵编译器会产生不同的10位正整数。我期待一个奇怪的值,因为由负数右移是未定义的,但我很惊讶,该值不是一个单一的,唯一的数字。相反,我用相同的输入获取多个值。为什么这不是一个数学函数 #include <stdio.h> int main(void) { printf("%d", 1 >> -1); return 0; } #包括 内部主(空){ printf(“

当我编译这个C代码并在我的机器上运行十几次时,每次都会得到一个不同的9位负数。另一台机器上的另一个铿锵编译器会产生不同的10位正整数。我期待一个奇怪的值,因为由负数右移是未定义的,但我很惊讶,该值不是一个单一的,唯一的数字。相反,我用相同的输入获取多个值。为什么这不是一个数学函数

#include <stdio.h>
int main(void) {
  printf("%d", 1 >>  -1);
  return 0;
}
#包括
内部主(空){
printf(“%d”,1>>-1);
返回0;
}

由于行为未定义,编译器(在叮当声的情况下,至少请参见下文)选择不将任何内容放入将参数传递给
printf
的寄存器中。这将导致寄存器具有准备C环境并调用
main
的启动代码留下的任何值。这恰好是启动过程中使用的某个地址,它被用于阻止对软件的攻击

通过检查clang生成的程序集,确认寄存器%esi中没有用于此参数的值(使用clang-1000.11.45.5中的Apple LLVM 10.0.0,在macOS 10.14.3中构建默认目标,仅使用开关
-O3


当然,其他编译器的行为可能会有所不同,因为C标准没有定义这种行为;这仅仅解释了OP在有限情况下报告的观察结果。

当您调用未定义的行为时,编译器完全可以做任何它想做的事情。它允许SEGFULT,或每次打印零,或启动CPU。没有什么真正要求它是确定性的,或者每次都给出相同的结果


在这种情况下,Clang知道会发生未定义的行为,所以它只是停止关心。它不需要费心去编译一条右移指令,因为它没有义务这样做。它甚至不需要在寄存器中输入任何值,只留下事先存在的值,这是不可预测的,实际上是随机的。

Argh检查完程序集输出,回来后发现您的答案是第一个得到的+不管怎样,因为这是完全正确的。@Draconis不好地竖起大拇指,兄弟,当这种情况发生在我身上时,讨厌,埃里克也竖起大拇指。