当 b 大于 a 中的位数时右移 (a >> b) 的未定义行为?

Undefined behaviour of right shift (a >> b) when b is greater than the number of bits in a?

显然,右移操作的行为:

a >> b
b >= sizeof(a)*CHAR_BIT 时,

在 C 和 C++ 中未定义(而在正常情况下,由于右移而从左侧引入的 "new bits" 等于零)。

为什么在 b >= sizeof(a)*CHAR_BIT 时这种未定义的行为比将结果设置为零更好?

我们可以了解为什么语言从 Why Language Designers Tolerate Undefined Behavior 中选择未定义的行为,它说:

This answer came from two general design principles behind C:

  1. The language should impose no unnecessary overhead on the implementation.
  2. It should be as easy as possible to implement C on a wide variety of hardware.

在这种特定情况下,当我们使用大于位宽的移位计数时会发生什么将取决于架构,例如我在 answer here:

中解释的

在某些平台上,轮班计数将被屏蔽5 bits,例如在x86架构上我们可以看到Intel® 64 and IA-32 Architectures Software Developer’s Manual部分SAL/SAR/SHL/SHR—IA-32 架构兼容性 部分中的 Shift 说:

The 8086 does not mask the shift count. However, all other IA-32 processors (starting with the Intel 286 processor) do mask the shift count to 5 bits, resulting in a maximum count of 31. [...]

因此,在某些平台上实现任意计数的移位可能会很麻烦,因此最好保留未定义的行为。

为什么不是未指定的行为

如果我们查看 Rationale for International Standard—Programming Languages—C 它说:

Unspecified behavior gives the implementor some latitude in translating programs. This latitude does not extend as far as failing to translate the program, however, because all possible behaviors are “correct” in the sense that they don’t cause undefined behavior in any implementation.

所以一定存在或仍然存在行为不正确并且会出现不良问题的情况。

这个未定义的行为为什么比将结果设置为零更好的例子。

通常 CPU 有一条指令进行移位。如果该指令需要与上限进行比较,那将需要更多的电路并减慢移位速度。相反,许多 CPU 只是使用移位的最低有效位来确定要移位多少。

// Example - not specified behavior  (assume 32-bit int)
y = 1 << 34;
// same as 
y = 1 << 2;

第一台 PC 使用 8/16 位处理器,它使用最少的 8 位来确定移位,因此一旦移位计数大于 `int 宽度但小于 256,它确实会移入零。这样做的问题是每个班次需要 1 个时钟滴答。所以在最坏的情况下,简单的移位命令可能需要 255 个时钟周期才能执行。当然,在 16 个刻度之后,只有 0 被移动了。这个 long 最坏情况指令是不可中断的!因此,该处理器的最坏情况 Interrupt latency 远比竞争对手差。英特尔没有再犯这个错误。