小编典典

使用gcc的按位移位的意外行为

linux

我有一个这样的测试程序:

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

怎么了?


阅读 275

收藏
2020-06-07

共1个答案

小编典典

根据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
2020-06-07