2011-08-27 126 views
3

我有一個這樣的測試程序:意外行爲使用gcc

int main() 
{ 
    unsigned n = 32; 

    printf("ans << 32 = 0x%X\n", (~0x0U) << 32); 
    printf("ans >> 32 = 0x%X\n", (~0x0U) >> 32); 

    printf("ans << n(32) = 0x%X\n", (~0x0U) << n); 
    printf("ans >> n(32) = 0x%X\n", (~0x0U) >> n); 

    return 0; 
} 

它產生以下輸出:

ans << 32 = 0x0 ... (1) 
ans >> 32 = 0x0 ... (2) 
ans << n(32) = 0xFFFFFFFF ... (3) 
ans >> n(32) = 0xFFFFFFFF ... (4) 

我期待(1)和(3)是相同,以及(2)和(4)是相同的。

使用gcc版本:gcc.real(Ubuntu的4.4.1-4ubuntu9)4.4.1

這是怎麼回事?

+2

FWIW:http://ideone.com/RqWE9 –

回答

8

由類型的大小移是未定義的行爲,根據C standard,§6.5.7.3:

6.5.7按位的移位運算符
(...)如果值 右操作數爲負數或者大於至推廣左操作數的寬度 ,則行爲未定義。

你的編譯器應該提醒你一下:

$ gcc shift.c -o shift -Wall 
shift.c: In function ‘main’: 
shift.c:5:5: warning: left shift count >= width of type [enabled by default] 
shift.c:6:5: warning: right shift count >= width of type [enabled by default] 

如果你看一下assembler code的gcc產生,你會看到它實際上是計算在編譯時前兩次效果。簡化:

main: 
    movl $0, %esi 
    call printf 

    movl $0, %esi 
    call printf 

    movl -4(%rbp), %ecx ; -4(%rbp) is n 
    movl $-1, %esi 
    sall %cl, %esi  ; This ignores all but the 5 lowest bits of %cl/%ecx 
    call printf 

    movl -4(%rbp), %ecx 
    movl $-1, %esi 
    shrl %cl, %esi 
    call printf 
+0

我不是類型的多個大小移...我移等於類型 – puffadder

+0

@R的大小。 Martinho Fernandes哎呀,我的意思是大於*或等於*。更新並引用了標準。 – phihag

+0

現在更好+1。 @puffadder我希望這會教你啓用並且不會忽略你的編譯器警告;) –