看板 C_and_CPP 關於我們 聯絡資訊
短的答案: 當 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); // -- ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 36.229.104.16 ※ 文章網址: http://www.ptt.cc/bbs/C_and_CPP/M.1409052644.A.379.html
michael47: 謝謝認真的解釋 08/26 22:14
witchang: 非常精闢的講解,GJ! 08/26 23:50
BlazarArc: 推 08/27 09:15
KAOKAOKAO: 推 08/27 14:27