неожиданное поведение побитового сдвига при использовании gcc - PullRequest
3 голосов
/ 27 августа 2011

У меня есть такая тестовая программа:

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

Что происходит?

1 Ответ

8 голосов
/ 27 августа 2011

Сдвиг на размер типа является неопределенным поведением, в соответствии со стандартом C , § 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]

Если вы посмотрите на код ассемблера, который генерирует 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
...