【问题标题】:unexpected behavior of bitwise shifting using gcc使用 gcc 的按位移位的意外行为
【发布时间】:2011-08-27 11:34:35
【问题描述】:

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

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

发生了什么?

【问题讨论】:

标签: c linux gcc bit-shift


【解决方案1】:

根据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

【讨论】:

  • 我移动的幅度不超过类型的大小...我移动的幅度等于类型的大小
  • @R. Martinho Fernandes 哎呀,我的意思是大于或等于。更新并引用了标准。
  • 现在更好+1。 @puffadder 我希望这能教你启用而不是忽略你的编译器警告;)
  • 但是 32 位等于类型的大小。 C 标准说“大于或等于”
  • 再次阅读引用的规范。它说“大于或等于”。根据规范未定义 32 位操作数上的 32 位移位。处理器通常会忽略移位量的低 5 位以外的所有位,这会导致这些处理器(包括 x86)上出现x &lt;&lt; 32 == x。在其他处理器上,零将被移入。C 标准很灵活,允许实现在所有处理器上做快速的事情。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-09
  • 2011-03-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多