比特移位时的奇怪行为

时间:2016-10-25 12:17:43

标签: c bit-manipulation

为什么下面用gcc编译的代码打印“ffffffff 0”而不是“0 0”?两个指令中的位向右移动了32个位置。它没有多大意义,因为x == 32,但仍然会发生这种奇怪的结果......

#include <stdio.h>

int main(void)
{
    int x = 32;
    printf("%x\n", 0xffffffff >> x);
    printf("%x\n", 0xffffffff >> 32);
    return 0;
}

编辑: enter image description here

EDIT2:    是的,编译器警告我。但这不是重点。我使用0xffffffff作为掩码,我用变量进行bithift。例如,当我使用8进行bithift时,我想要0xffffff(并且它会这样做)。当我用31进行bithift时,我希望得到0x1(并且它会这样做)。当我用32进行bithift时,它给了我0xffffffff(而不是0,这是当我有32作为文字,而不是变量时的行为)。这很奇怪,为了我的目的,为32创建一个特殊情况真的不方便,因为它应该给0(它确实如此,但只有当32是文字时)

5 个答案:

答案 0 :(得分:6)

假设int和unsigned int是32位宽,则整数常量0xffffffff的类型为unsigned int。

右移一个值大于或等于整数宽度的整数将导致未定义的行为 1

在您的示例中,两种情况都会发生这种情况。

更新

未定义的行为意味着任何事情都可能发生。获取值0xffffffff而不是0符合此行为。

您不能按整数类型的宽度移动,因为它在标准中这样说。您必须检查该值是否大于或等于您使用的类型的宽度。

1 (引自:ISO / IEC 9899:201x 6.5.7按位移位操作符3)
如果右操作数的值为负或大于或等于提升的左操作数的宽度,则行为未定义。

答案 1 :(得分:4)

  

Edit2:是的,编译器警告我。但这不是重点。我是   使用0xffffffff作为掩码,我用变量进行bithift。对于   例如,当我使用8进行bithift时,我想要0xffffff(并且它会这样做)。   当我用31进行bithift时,我希望得到0x1(并且它会这样做)。和   当我用32位移位时它给了我0xffffffff(而不是0,这是   当我有32作为文字,而不是变量时,beahaviour)。它是   奇怪的是,为了我的目的,制作一个特例非常奇怪   为32,因为它应该给0(它确实如此,但只有当32是a时   文字)

是的,非常重要。你尝试将它移动32的那一刻,正如你的编译器所说,>= width你调用未定义的行为。此时所有投注都已关闭,不再对程序将执行的操作做出任何假设。可以免费打印任何内容或格式化您的驱动器。所以它并不“奇怪”。

答案 2 :(得分:2)

除了其他答案所讨论的未定义行为之外,结果的差异来自GCC优化第二个分位移位。

如果使用-S标志反汇编代码,您会注意到只使用了一条shrl指令:

subq    $16, %rsp
movl    $32, -4(%rbp)
movl    -4(%rbp), %eax
movl    $-1, %edx
movl    %eax, %ecx
shrl    %cl, %edx
movl    %edx, %eax
movl    %eax, %esi
movl    $.LC0, %edi
movl    $0, %eax
call    printf
movl    $0, %esi
movl    $.LC0, %edi
movl    $0, %eax
call    printf
movl    $0, %eax

编译器看到它可以在编译时计算第二个bitshift,因为它永远不会改变,并用结果替换计算,这里0

答案 3 :(得分:0)

如果您的目标是使用32位int的环境,那么移位32位或更多位是Undefined Behaviour

  

如果右操作数的值为负或大于或等于提升的左操作数中的位数,则行为未定义。

作为程序员,您有责任避免调用UB。

答案 4 :(得分:-1)

这取决于编译器。但在您的示例中,在第一种情况下,x已签名,因此移位是算术运算(从左侧重新插入最左侧的位)。在第二种情况下,文字32被视为无符号,因此移位变为逻辑(从左侧插入0)。这解释了ffffffff 0结果。