以下程序:
#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
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是不可移植的。
转载注明原文:c++ 右移操作符的奇怪行为(1 >> 32)