C++ x86机器上的uint64_t整数移位是否超过32位未定义行为?

C++ x86机器上的uint64_t整数移位是否超过32位未定义行为?,c++,c,x86,bit-shift,uint64,C++,C,X86,Bit Shift,Uint64,经过艰苦的学习,我尝试在x86机器上将long和uint64\u t左移到32位以上,结果是0。我模模糊糊地记得读过32位机器上的某个地方,移位运算符只处理前32位,但记不起源代码。 我想知道的是,在x86机器上移动超过32位的uint64_t整数是否属于未定义行为?通过0和类型宽度的前一个数字进行移动不会导致未定义行为,但左移负数会导致未定义行为。你会那样做吗 另一方面,右移负数是实现定义的,大多数编译器在右移有符号类型时传播符号位。该标准规定(n1570中的6.5.7): 3对每个操作数执行

经过艰苦的学习,我尝试在x86机器上将
long
uint64\u t
左移到32位以上,结果是
0
。我模模糊糊地记得读过32位机器上的某个地方,移位运算符只处理前32位,但记不起源代码。
我想知道的是,在x86机器上移动超过32位的uint64_t整数是否属于未定义行为?

通过0和类型宽度的前一个数字进行移动不会导致未定义行为,但左移负数会导致未定义行为。你会那样做吗

另一方面,右移负数是实现定义的,大多数编译器在右移有符号类型时传播符号位。

该标准规定(n1570中的6.5.7):

3对每个操作数执行整数提升。结果的类型是 提升后的左操作数的值。如果右操作数的值为负数或大于或等于提升后的左操作数的宽度,则行为未定义

4 E1>E2的结果是E1右移位E2位位置。如果E1具有无符号类型 或者,如果E1具有有符号类型和非负值,则结果值为整数 E1/2E2商的一部分。如果E1具有有符号类型和负值,则 由此产生的价值已确定

uint64\t
移动小于64位的距离完全由标准定义

由于
long-long
必须至少为64位,因此如果结果没有溢出,则小于64位的移位
long-long
值由非负值标准定义


但是,请注意,如果您编写的文字适合32位,例如
uint64_t s=1,则C标准需要移位才能正常工作。特定的错误编译器可能有您描述的缺陷,但这就是错误行为

这是一个测试程序:

#include <stdio.h>
#include <inttypes.h>

int main(void)
{
    uint64_t x = 1;
    for (int i = 0; i < 64; i++)
        printf("%2d: 0x%.16" PRIX64 "\n", i, (x << i));
    return 0;
}
不,没关系

ISO 9899:2011 6.5.7位移位运算符

如果右操作数的值为负数或大于或等于提升后的左操作数的宽度,则行为未定义

这里的情况不是这样的,所以它很好,定义也很好。

回答了关于C语言规范的问题。至于在x86机器上,当您发出可变数量的移位时,实际会发生什么情况,请参阅第2B卷第页。4-506:

计数被屏蔽为5位(或6位 如果处于64位模式,则使用REX.W)。计数范围限制为0到31(如果需要,则为63 64位模式,使用REX.W)

因此,如果尝试移位量大于31或63(分别针对32位和64位值),硬件将只使用移位量的底部5或6位。所以这个代码:

uint32_t RightShift(uint32_t value, uint32_t count)
{
    return value >> count;
}

将导致x86和x86-64上的
RightShift(2,33)==1
。根据C标准,它仍然是未定义的行为,但在x86上,如果编译器将其编译为
sar
指令,它将在该体系结构上定义行为。但是您仍然应该避免编写这种依赖于特定于体系结构的怪癖的代码。

不应该这样。你在用什么编译器?请澄清你所说的“m/c”是什么意思?我以前从未听说过“m/c”。在我的分支中,MC最有可能代表微控制器,或者不太可能代表摩托罗拉/飞思卡尔集成电路。想象一下,如果没有人使用奇怪的首字母缩略词,作为程序员工作会变得多么容易!您可能使用了类似于
uint64\u t x=1的内容,如果您发布了行为不端的代码,您可以更清楚地了解此处实际发生的情况。+1。应该是这样的。兼容的编译器必须遵循C标准。@drhirsch指出了可能存在的问题:类似于
uint64\u t x=1,大多数编译器对
unsigned
进行逻辑(插入0)右移,对
有符号的
变量进行算术(插入符号位)右移。至少我使用过的任何编译器。左移负数不是未定义的行为;它是实现定义的。在实践中,如果处理器有一条指令,当向左移位时,它将符号扩展,我希望编译器使用它;“定义实现”是为了支持没有此类指令的处理器。@JamesKanze C99 6.5.7:4“否则,行为未定义”。如果您正在寻找一个静态分析器,如果您左移一个负数,它会(可选)警告您,请参阅我的简历中的链接。@drhirsch我已经澄清了我的意思是“传播符号位”仅适用于有符号类型。@JamesKanze我所指的静态分析器的
-val左移负报警
选项在默认情况下是打开的。尽管如此,我们还是需要将此警告设置为可选的,因为许多程序员认为负数的左移位是实现定义的,并且只要编译器同意,这样的警告对他们来说只是噪音。但是由于它是未定义的行为,编译器可能已经决定实际移位不会发生,因此,甚至可能不会发出汇编程序指令。因此,进一步研究汇编程序将做什么是没有意义的。编译器将使用
shr
进行无符号右移
sar
(算术右移)将复制MSB,违反非UB情况下的C语义。编译器知道移位将计数屏蔽为
&31
&63
,因此将实际优化
值>>(count&31)
到单个
shr
shrx
指令,因为它实现了
&31
以及移位。很好地说明了正在发生的事情。我个人不喜欢
P
uint32_t RightShift(uint32_t value, uint32_t count)
{
    return value >> count;
}