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