2013-10-28 40 views
30

我必须在这里绝对疯狂,但我的机器上的gcc 4.7.3给出了最荒谬的结果。下面是我测试的确切代码:算术右移给假结果?

#include <iostream> 

using namespace std; 

int main(){ 
    unsigned int b = 100000; 
    cout << (b>>b) << endl; 
    b = b >> b; 
    cout << b << endl; 
    b >>= b; 
    cout << b << endl; 
    return 0; 
} 

现在,应该产生这是正确的本身转移任意数量的(n/(2^n) == 0整数除法n>1正/无符号) ,但不知何故这里是我的输出:

100000 
100000 
100000 

我疯了吗?可能会发生什么?

+0

@ShafikYaghmour:假设编译器甚至不愿意提出指示。完全拒绝这个计划是正确的。 – MSalters

+0

@ MSalters确实,我们正在进入编译器/平台/版本特定在这一点上,但对于目前和最近的版本,这是这种情况,因为我已经表明它是未定义的,所以你是明确的你自己,'gcc'似乎只当使用'-O0'时产生'shr'。 –

+0

@ShafikYaghmour:英特尔只是gcc支持的众多平台之一,它们有不同的优化阶段。优化中的一个常见技巧是说“这个值只能在0到31之间,因为它用于移位,如果我遵循代码路径X来到这里,值不会介于0和31之间,所以代码路径X是不可能的我甚至不需要为它生成指令“。 GCC着名的做空指针检查。 – MSalters

回答

44

与C中一样,C++中的移位被限制为移位值的大小(以位为单位)。例如,如果unsigned int是32位,那么大于31的位移是未定义的。

实际上,一个共同的结果是使用移位量的5个最低有效位并忽略高位位;这是由于编译器生成的机器指令确实如此(例如,x86上的SHR)。

在这种情况下,移位值是100000(十进制),它恰好是二进制的11000011010100000 - 低5位是零。所以,你实际上得到了0的转变。然而,你不应该依赖这个;从技术上讲,你看到的是未定义的行为

参考文献:

对于C,N1570节6.5.7:

如果右操作数的值是负的或大于或 等于推动左操作数的宽度,行为是 未定义。

对于C++,N3690 5.8节“[expr.shift]”:

如果将右操作数是负的,或大于或 等于长度中的比特,则行为未定义提升了左操作数。

N1570是一个草案,几乎与公布的ISO C11标准相同;自从1989年的ANSI C标准以来,这个条款几乎一样。

N3690是C++标准的最新草案;我不确定它是否是最好的,但是这个条款没有改变。

+1

你是否观察到这种行为与不同的编译器或从哪里读取? –

+3

@GrijeshChauhan,这是在C和C++标准规范中记录的。关于生成SHR指令,我观察到了这一点。 – davmac

+0

编码的圣母......我刚刚测试过这个,100000的二进制是'0b110000110101_00000'。我试着移动100001,它确实移位了1. – Suedocode

31

要调用undefined behavior如果你转移比左边的操作数位长度越大,draft C++ standard部分5.8移位运算款说(重点煤矿):

的操作数应是完整或无法列举的枚举类型和积分促销。结果的类型是提升的左操作数的类型。 如果右操作数为负数,或者大于或等于升级的左操作数的位长度,则行为未定义。

有趣地注意到,gccclang可以产生警告此代码,如果移位量如果字面

cout << (b>> 100000) ; 

,或者如果b常量gcc的警告如下:

warning: right shift count >= width of type [enabled by default] 

为MSalters在评论的问题指出,我们可能无法甚至靠这个警告,因为这是不确定的行为,这是符合标准的方面对未定义行为注意和定义部分它说:

注:[...]允许的未定义行为从不可预知的结果完全无视的情况下,在环境中的记录方式的特性翻译或程序执行期间表现范围(有或没有发布诊断消息),终止翻译重刑或执行(通过发布诊断信息)。 [...]

平台的具体细节

为明显缺乏在示例代码的移位的可能的解释可能是因为在某些平台上移位计数将是掩蔽5 bits例如上x86架构我们可以看到Intel® 64 and IA-32 Architectures Software Developer’s ManualSAL/SAR/SHL/SHR移在IA-32体系结构兼容性说:

8086不掩盖移位计数。但是,所有其他IA-32处理器(以Intel 286处理器开始)都会将移位计数屏蔽为5位,导致最大计数为31. [...]

+0

80286故意允许的移位量达到字的大小,包括在内。我想知道为什么80386没有这样做(当操作数大小为32位时,使用6位CL)? – supercat

+0

另请参阅[本博客文章](https://david.wragg.org/blog/2012/11/shift-instructions.html)了解x86的更多背景信息。 –