右移 signed int 是否为 8 * sizeof(int) 或更多未定义?

Is rightshifting signed int by 8 * sizeof(int) or more undefined?

我知道这是未定义的:

uint32_t u = 1;
u << 32;

但我对哪些班次未定义感到有点困惑。

将有符号整数向右移动其大小(以位为单位)或更多是未定义的吗?

更新:正如答案中所指出的,这是关于位的大小,而不是字节的大小。

标准中的一切。 Section 6.5.7p3

3) The integer promotions are performed on each of the operands. The type of the result is that of the promoted left operand. If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined.

这适用于左移和右移,以及运算符左侧的有符号和无符号操作数。有符号操作数的移位还有其他限制。

sizeof (int)int 字节的大小 ,所以它不相关。相关的不是大小,而是 宽度 ,它是表示中值位数(加上符号位的符号位)。

如果 <<>> 运算符的右操作数大于或等于提升后的左操作数的宽度,则行为未定义。 (例如,如果左操作数的类型为 short,则在应用操作之前将其提升为 int)。

对于 << 左移运算符,仅当左操作数为非负且结果可表示时才定义行为。

对于 >> 右移运算符,如果左操作数为负,则结果由实现定义。

这些都在 C standard 的第 6.5.7 节中定义(link 是针对 N1570,最新公开的 C11 草案)。

下面是语义的完整描述:

The integer promotions are performed on each of the operands. The type of the result is that of the promoted left operand. If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined.

The result of E1 << E2 is E1 left-shifted E2 bit positions; vacated bits are filled with zeros. If E1 has an unsigned type, the value of the result is E1 × 2E2, reduced modulo one more than the maximum value representable in the result type. If E1 has a signed type and nonnegative value, and E1 × 2E2 is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined.

The result of E1 >> E2 is E1 right-shifted E2 bit positions. If E1 has an unsigned type or if E1 has a signed type and a nonnegative value, the value of the result is the integral part of the quotient of E1 / 2E2. If E1 has a signed type and a negative value, the resulting value is implementation-defined.

C 语言在编写 C89 标准之前被广泛使用,C89 标准的作者不想强加任何可能与现有实现已经在做的事情相反的极端情况语义的要求.

某些实现在给定非常大或负的值时会表现不佳 偏移量(例如,我认为在 Transputer 上偏移 -1 大约需要 执行 4294967295 个时钟周期,在此期间将产生中断 禁用),以及一些在被要求按字长移位时的实现 根本不会改变。标准的作者认为没有理由区分这些行为,只是将字数视为阈值,超过阈值就不会施加任何要求。

尽管使用格式时负值的正确含义不清楚 除了二进制补码之外,二进制补码没有真正的歧义 价值超出了一些现有的实现使用逻辑权利的事实 - 移位而不是算术右移,即使有符号类型和 委员会不想强制任何现有的编译器改变行为 该代码可能依赖于它。在格式中使用负值时 除了二进制补码之外,尚不清楚是什么向任一方向移动 应该意味着,但委员会认为更有可能存在 左移一个负数时会做一些奇怪的事情的机器 而不是机器在右移负数时可能会做一些奇怪的事情 数.

请注意,高质量编译器给出的概念类似于:

unsigned long rotate_left(unsigned long dat, int amount)
{ return (dat << amount) | (dat >> (32-amount)); }

当 amount==0 时除了 yield "dat" 之外应该做任何事情 [注意这两个 评估右移的常用方法会产生相同的结果]是 比较新的。我认为 C89 的作者打算如果质量 平台 X 的编译器可能会在之前以某种方式运行 C89 已发布,标准允许这种行为, 该平台的高质量编译器应该继续以这种方式运行。 尽管如此,已经出现了一种态度,即不应允许程序员 依赖标准未规定的任何行为,即使在平台上也是如此 它们在哪里有用又便宜。