右移和有符号整数

Ann*_*inn 17 c++ bit-shift platform-specific

在我的编译器上,以下伪代码(用二进制替换的值):

sint32 word = (10000000 00000000 00000000 00000000);
word >>= 16;
Run Code Online (Sandbox Code Playgroud)

生成一个如下所示word的位域:

(11111111 11111111 10000000 00000000)
Run Code Online (Sandbox Code Playgroud)

我的问题是,我可以依赖所有平台和C++编译器的这种行为吗?

And*_*ark 23

从以下链接:
INT34-C.不要将表达式移位负数位或大于或等于操作数中存在的位数

不合规代码示例(右移)
结果E1 >> E2E1右移位E2位置.如果E1具有无符号类型或者E1具有有符号类型和非负值,则结果的值是E1/2 E2的商的整数部分.如果E1具有有符号类型和负值,则结果值是实现定义的,可以是算术(签名)移位:
算术(签名)移位
或逻辑(无符号)转变:
逻辑(无符号)转换
这个不合规的代码示例无法测试右操作数是否大于或等于提升的左操作数的宽度,从而允许未定义的行为.

unsigned int ui1;
unsigned int ui2;
unsigned int uresult;

/* Initialize ui1 and ui2 */

uresult = ui1 >> ui2;
Run Code Online (Sandbox Code Playgroud)

假设右移是实现为算术(带符号)移位还是逻辑(无符号)移位也可能导致漏洞.见建议INT13-C.仅在无符号操作数上使用按位运算符.

  • 是否没有建议真正关注这个问题?因为基于该规则的名称,它不适用于此...您只是引用了一些提供的背景信息. (2认同)
  • 如果要移位的量是编译时常量,则可以通过使用(有符号)除法来强制进行算术移位。因此,您可以编写“a / (1 << 16)”,而不是由实现定义的“a >> 16”,编译器几乎肯定会用算术移位来替换它。 (2认同)

小智 16

来自最新的 C++20 草案

有符号整数类型的右移是算术右移,它执行符号扩展。

  • @阿德里安是的。C++20 现在假定 2 补码。为什么?因为现在是 2022 年了,去他爷爷地下室里的那个东西吧。世界上每个可信的平台都是 2 的补码,我们应该停止支持晦涩难懂的硬件架构。 (6认同)
  • @Adrian [是](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1236r1.html)。 (4认同)
  • 等待!这是否意味着所有带符号的值都需要使用 2 补码? (2认同)

R. *_*des 13

不,你不能依赖这种行为.负数量的右移(我假设你的例子正在处理)是实现定义的.

  • 如果您为某个体系结构编译某些东西,那么该体系结构的所有实现都应该这样做.例如,x86对符号扩展和非符号扩展移位有不同的移位操作,编译器决定使用哪一个.在其他架构上,它可能根本不起作用(读取:*任何东西*,而不仅仅是这种行为). (5认同)

Kei*_*win 6

在C++中,没有.它取决于实现和/或平台.

在其他一些语言中,是的.例如,在Java中,>>运算符被精确定义为始终使用最左边的位填充(从而保留符号).>>>运算符使用0填充.因此,如果您需要可靠的行为,一种可能的选择是更改为其他语言.(虽然很明显,根据您的具体情况,这可能不是一种选择.)