短的答案:
当 sizeof(unsigned int) = 4 的时候, 左移 32 位已经超出他的大小了,
规定上是 undefined behavior, 换句话说编译器产生任何指令都没问题,
算出任意结果都不奇怪
假如你编译时指定 -O2 的话, 有可能你会发现输出都变成 0 了! 这也是 "对" 的
长的答案:
1) 为何 test_bits_1 << 32 可能是 1?
x86(_64) 的 SAL 指令在 32-bit 及 64-bit without REX.W 时只看低 5 个位元,
因此左移 32 位事实上会变成左移 0 位(因为只看低 5 个位元). 结果就是不变.
也就是说
(i) mov eax, 1
mov ecx, 32
sal eax, cl ; 只看低 5 位
(ii) mov eax, 1
sal eax, 32 ; 还是只看低 5 位
两个出来的结果都是 eax := 1; 只看低五个位元话就是 shift 0 bits
而 test_bits_1 << 32 有可能被翻译成 (i), 出来会是 1. 但 -O2 也有可能被
最佳化掉, compiler 在编译时期就先算掉, 变成 0.
2) 为何 1u << 32 可能是 0?
标准定的基本就让编译器可以直接假设任何 signed 运算都不会 overflow, 而
signed/unsigned 运算遇到移位也不会爆出去. 当编译器做了这个假设后说不定
直接 1u<<32 算出来是 2^32 (纯粹数学上, 不溢位的状况), 然后 unsigned 系列
的计算都是 mod (Uxxx_MAX + 1), 就变成 0 了.
这种东西变成 undefined behavior 当然有可能动机是上面 x86 系列的复杂定义,
但是这也让 compiler 的最佳化变得更简单. 你可能已经看过如下的例子:
int i = 1;
while (i >= 0) {
i = i * 2;
}
这个程式会停下来吗? 答案是 "不知道", 因为 signed arithmetic overflow 是
undefined behavior. 可能我们开了最佳化后 compiler 可以推导出
(i) i 本来就是正的
(ii) 正数*2 还是正的 (因为 "不会" overflow!)
=> i >= 0 永远成立, 这个可以被无穷循环替换掉
但是若换成 i = i + 1, 结果 -O2 后出来的程式是整个循环被拿掉了! 怎么回事
我也不知道. 但是 undefined behavior 发生什么事都有可能.
p.s. REX http://wiki.osdev.org/X86-64_Instruction_Encoding#REX_prefix
shift operator in C++:
1. [...] The operands shall be of integral or unscoped
enumeration type and integral promotions are performed.
The type of the result is that of the promoted left
operand. The behavior is undefined if the right operand
is negative, or greater than or equal to the length in
bits of the promoted left operand.
http://stackoverflow.com/questions/18918256/
shift operator in C:
3. The integer promotions are performed on each of
the operands. The type of the result is that of the
promoted left operand. If the value of the right
operand is negative or is greater than or equal to
the width of the promoted left operand, the behavior
is undefined.
※ 引述《michael47 (hitman)》之铭言:
: Platform: Linux GCC
: Question:
: unsigned int test_bits_1 = 0x00000001;
: printf("test_bits_1 = 0x00000001, test_bits_1 << 32 = %X\n",
: test_bits_1 << 32);
: printf("0x01 << 32 = %X\n", 0x01 << 32);
: 输出结果
: test_bits_1 = 0x00000001, test_bits_1 << 32 = 1
: 0x01 << 32 = 0
: 请问问题出在哪里?
: 我以为结果test_bits_1 << 32 = 0
: 程式码(Code):
: unsigned int test_bits_1 = 0x00000001;
: printf("test_bits_1 = 0x00000001, test_bits_1 << 32 = %X\n",
: test_bits_1 << 32);
: printf("0x01 << 32 = %X\n", 0x01 << 32);
//