int c = 0xffffffff;
printf("%x\n", (c) == (0xffffffff));
printf("%x\n", (c >> 1) == (0xffffffff >> 1));
Run Code Online (Sandbox Code Playgroud)
第一个输出是1但第二个输出是0。并c >> 1输出0xffffffff。但为什么?
整数常量0xffffffff的类型为unsigned int。因此,当您对该值执行右移时,值为 0 的位将从左移入。
相反,cis anint并且包含负值。对负值执行右移是实现定义的,尽管大多数实现将通过从左侧移入值为 1 的位来保留符号。
至于为什么在比较正值和负值时会打印 1:
printf("%x\n", (c) == (0xffffffff));
Run Code Online (Sandbox Code Playgroud)
它与整数转换有关。
当 0xffffffff(无符号)分配给 时c,它会经历实现定义的转换,因为该值超出范围。在二进制补码系统中,这通常意味着表示将直接应用于目的地。所以现在c的值是 -1,表示为 0xffffffff。
现在进行比较。常量0xffffffff是无符号的,值为 2 32 -1。将有符号值与相同大小的无符号值进行比较时,有符号值将转换为无符号值。这是通过将unsigned int+ 1的最大值添加到有符号值以将其置于范围内来实现的(根据标准)。所以有符号值 -1 被转换为无符号值 2 32 -1。这与 0xffffffff 的值相同,因此比较为真。
因为0xffffffff在你的平台上是一个 type 的值unsigned int。因此0xffffffff >> 1等于0xffffffffU >> 1是0x7fffffffU。
将类型值分配给类型unsigned int变量时,int该值将转换为目标类型。0xffffffff即 4294967295 超出了 32 位有符号整数的范围,因此自然无法按原样存储。但是因为您的计算机使用二进制补码系统,所以位将保持不变,即所有位都将被设置,但该值被解释为-1而不是4294967295。
负整数右移的行为是在 C 标准中实现定义的。GCC 将其定义为算术右移,即使用符号扩展。所以位表示 ffffffff右移 1 将导致ffffffff位表示;其中 signed int 表示 value -1。
最后,当您使用运算符将 anint与 anunsigned int进行比较时==,操作数将进行“通常的算术转换” - int-1 将转换为 an unsigned int- 在二进制补码系统上保留位模式 ie ffffffff,但现在该值将被解释为 4294967295而不是 -1。但该值比较不等于 0x7fffffff (2147483647),它具有值
什么是更令人困惑的是,价值c不等于0xffffffff即使平等运算符返回true!值c将是-1,和值0xffffffff是0xffffffffu即4294967295和明确的区别-1,并4294967295为4294967296!但是,正是因为的signed/unsigned比较是不同的值可以比较平等的-和例如GCC将警告这跟-Wextra:
warning: comparison of integer expressions of different signedness: ‘int’ and ‘unsigned int’ [-Wsign-compare]
5 | printf("%x\n", (c) == (0xffffffff));
| ^~
[venv][vrcappsdev][master?]
Run Code Online (Sandbox Code Playgroud)
要查看值确实不同,您需要将双方强制转换为足够大以包含它们的类型 -long long int是可移植的:
printf("%d\n", (long long int)c == (long long int)0xffffffff);
Run Code Online (Sandbox Code Playgroud)
不会打印 1。