带符号 int 的位移位重置过多

问题描述

请看下面的代码片段,它基本上只是向左移动 1 个字节 24 位。

uint64_t i = 0xFFFFFFFF00000000;
printf("before: %016llX \n",i);
i += 0xFF << 24;
printf("after : %016llX \n",i);

// gives:
// before: FFFFFFFF00000000
// after : FFFFFFFEFF000000

最高的 32 位是 FFFFFFFE(注意最后的 E)。这并不像我预期的那样。我不明白为什么左移 1 个字节 24 位会触及位 #32(位 #31 应该是最后一个修改的)它将最后一个 F (1111) 更改为 E (1110)).

为了使其正常工作,我使用了 0xFF unsigned (0xFFU)。

uint64_t i = 0xFFFFFFFF00000000;
printf("before: %016llX \n",i);
i += 0xFFU << 24;
printf("after : %016llX \n",i);

// gives:
// before: FFFFFFFF00000000
// after : FFFFFFFFFF000000

为什么带符号整数 (0xFF) 的位移位会过多地触摸/重置一位?

解决方法

您左移到符号位。

整数常量 0xFF 的类型为 int。假设 int 是 32 位,表达式 0xFF << 24 将设置为 1 的位移入有符号整数触发 undefined behavior 的高位,在您的情况下,它表现为意外值。

这在 C standard 的第 6.5.7p4 节中有详细说明:

E1 << E2 的结果是 E1 左移 E2 位位置;空出的位用零填充。如果 E1 具有无符号类型,则结果的值为 E1×2E2,比结果类型中可表示的最大值减少模 1。如果 E1 有符号类型和非负值,并且 E1×2E2 在结果类型中是可表示的,那么就是结果值;否则,行为未定义。

通过使用 U 后缀,这使得常量具有 unsigned int 类型,并且将设置为 1 的位移入高位是有效的,因为没有符号位。