类似于以下内容:
uint32_t foo = 1;
foo = foo << 33;
C中的未定义行为?
实际上,移位是一种未定义的行为。请参阅标准(C11,最终草案 N1570,6.5.7p3):
原因:如果移位计数大于或等于参数的宽度,则移位操作在不同的 CPU 架构上可能会有非常不同的行为。这样,标准允许编译器生成最快的代码,而不必考虑边界效应。
请注意:如果int比33位(例如64位)更宽,则情况会有所不同。原因是整数提升首先将uint32_t转换为int,因此移位使用的是(然后更大的)int值。这使得赋值的回转到uint32_t,参见6.3.1.3第1、2段。但是,在大多数现代系统上,int不大于32位。(1u<<(w-1))<<1
的原因是明确的,只是因为这两个表达式不相同。就像 11 / 2 * 2
不同于 11 / (2 / 2)
一样。但对于移位操作,如果架构恰好如此,编译器可以自由地将两个移位表达式折叠成相同的机器代码。请注意,许多 CPU 不支持机器指令中 w
或更高的移位计数。如果它们使用循环进行多次移位(仅使用单比特移位指令),则循环可以忽略计数以节省代码(周期)。 - too honest for this site2^E2
不能在(提升后的)类型中表示。这也解释了为什么对于有符号的情况,它明确表示E1 x 2^E2
必须是可表示的,否则它就是未定义的。有符号类型没有溢出,因此它们允许实现将移位完全实现为E1 2^E2
。 - MicroVirus
uint32_t
是无符号的。添加unsigned
是无意义的。而且移位是未定义的行为! - too honest for this siteuint32_t
了。 - too honest for this siteuint32_t foo
,但会变成64位的int
,然后进行移位操作,最后再赋值给uint32_t
。 - chux - Reinstate Monica