为什么 ~0 >> 1 不移动位?

Why ~0 >> 1 doesn't shift the bit?

我刚从 K&R c 书第 2 章学习,假设我有这段代码:

#include <stdio.h>
int
main(void)
{
    printf("0x%016llx\n", ~0); //0x00000000ffffffff

    printf("0x%016llx\n", ~0 >> 1); //0x00000000ffffffff
    printf("0x%016llx\n", 0x00000000ffffffff >> 1); //0x000000007fffffff
    return 0;
}

我希望 ~0 >> 1 会像 0x00000000ffffffff >> 1 那样给出 0x000000007fffffff~0 的值是 0x00000000ffffffff

为什么 ~0 >> 1 不移动位?

llx 格式说明符需要一个 unsigned long long 参数,但您传递的是 int.

转换没有给您预期的效果,因为 ~0 导致 int 具有负值。所以右移保留符号位,即 1 位向左移。

ULL 后缀放在整数常量上以强制它们成为正确的类型:

printf("0x%016llx\n", ~0ULL);
printf("0x%016llx\n", ~0ULL >> 1);
printf("0x%016llx\n", 0x00000000ffffffffULL >> 1);

然后你会得到预期的输出:

0xffffffffffffffff
0x7fffffffffffffff
0x000000007fffffff

因为值现在是无符号的,所以 0 位总是移到左边。

此代码...

printf("0x%016llx\n", ~0)

...和您的其他类似示例表现出未定义的行为,因为 ~0 的类型 (int) 与相应的字段描述符 %llx 不对应,这需要unsigned long long int.

评估表达式 ~0 >> 1 表现出实现定义的(不是 un 定义的)行为,因为标准明确表示

The result of E1 >> E2 is E1 right-shifted E2 bit positions [, but] If E1 has a signed type and a negative value, the resulting value is implementation-defined.

(C2011, 6.5.7/5)

出现这种情况至少部分是因为对负数右移有两种不相容的解释:

  • 一个算术移位,其中结果与左操作数的符号相同,并且

  • 一个逻辑移位,移位空出的位置总是用零填充。

实现可以选择(自由地,而不仅仅是在这些备选方案之间)。实际上,这意味着可移植代码不应依赖此类操作。