c++ 右移操作符的奇怪行为(1 >> 32)

我最近使用右移运算符面临一个奇怪的行为。

以下程序:

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <stdint.h>

int foo(int a, int b)
{
   return a >> b;
}

int bar(uint64_t a, int b)
{
   return a >> b;
}

int main(int argc, char** argv)
{
    std::cout << "foo(1, 32): " << foo(1, 32) << std::endl;
    std::cout << "bar(1, 32): " << bar(1, 32) << std::endl;
    std::cout << "1 >> 32: " << (1 >> 32) << std::endl; //warning here
    std::cout << "(int)1 >> (int)32: " << ((int)1 >> (int)32) << std::endl; //warning here

    return EXIT_SUCCESS;
}

输出:

foo(1, 32): 1 // Should be 0 (but I guess I'm missing something)
bar(1, 32): 0
1 >> 32: 0
(int)1 >> (int)32: 0

foo()函数会发生什么?我明白,它和最后两行之间的唯一区别是最后两行在编译时被评估。如果我使用64位整数为什么它“工作”?

任何关于这个的灯将不胜感激!

当然相关,这是什么g给:

> g++ -o test test.cpp
test.cpp: In function 'int main(int, char**)':
test.cpp:20:36: warning: right shift count >= width of type
test.cpp:21:56: warning: right shift count >= width of type
CPU实际上可能是计算机

a >> (b % 32)

在foo同时,1>> 32是一个常量表达式,所以编译器将在编译时折叠常数,这样就可以得到0。

由于标准(C 98§5.8/ 1)规定

The behavior is undefined if the right operand is negative, or greater than or equal to the length in bits of the promoted left operand.

没有具有foo(1,32)和1>> 32的矛盾给出不同的结果。

另一方面,在bar中,您提供了64位无符号值,为64> 32保证结果必须是1/232 = 0。但是,如果你写的话

bar(1, 64);

你可能还会得到1。

编辑:逻辑右移(SHR)的行为类似于>> (b%32/64)x86 / x86-64(Intel#253667,第4-404页):

The destination operand can be a register or a memory location. The count operand can be an immediate value or the CL register. The count is masked to 5 bits (or 6 bits if in 64-bit mode and REX.W is used). The count range is limited to 0 to 31 (or 63 if 64-bit mode and REX.W is used). A special opcode encoding is provided for a count of 1.

然而,在ARM(至少armv6和7)上,逻辑右移(LSR)被实现为(ARMISA第A2-6页)

(bits(N), bit) LSR_C(bits(N) x, integer shift)
    assert shift > 0;
    extended_x = ZeroExtend(x, shift+N);
    result = extended_x<shift+N-1:shift>;
    carry_out = extended_x<shift-1>;
    return (result, carry_out);

其中(ARMISA页面AppxB-13)

ZeroExtend(x,i) = Replicate('0', i-Len(x)) : x

这保证了≥32的右移会产生零。例如,当这个代码在iPhone上运行时,foo(1,32)将给出0。

这些显示32位整数移位≥32是不可移植的。

http://stackoverflow.com/questions/3394259/weird-behavior-of-right-shift-operator-1-32

本站文章除注明转载外,均为本站原创或编译
转载请明显位置注明出处:c++ 右移操作符的奇怪行为(1 >> 32)