Bit Twiddling - 对此程序的输出感到困惑

时间:2012-03-29 15:12:12

标签: c integer bit-shift bit-manipulation

所以我在C语言中乱搞Bit-Twiddling,我遇到了一个有趣的输出:

int main()
{
    int a = 0x00FF00FF;
    int b = 0xFFFF0000;

    int res = (~b & a);

    printf("%.8X\n", (res << 8) | (b >> 24));
}

此声明的输出是:

  

FFFFFFFF

我预计输出为

  

0000FFFF

但为什么不呢?我在这里错过了一些比特移位的东西吗?

4 个答案:

答案 0 :(得分:5)

TLDR:你的整数b是负数,所以当你向右移动时,最高位(即1)的值保持不变。因此,当你将b向右移动24个位置时,你最终会得到0xFFFFFFFF。

更长的解释:

假设您的平台上的整数为32位或更长,并且有符号整数由2的补码表示,则分配给有符号整数变量的0xFFFF0000为负数。如果int超过32位,则0xFFFF0000将首先进行符号扩展,但仍为负数。

向右移动负数是标准所定义的实施(C99 / N1256,第6.5.7.5节):

  

E1&gt;的结果&gt; E2是E1右移E2位位置。 [...]如果E1   有一个有符号的类型和一个负值,结果值是   实施定义。

这意味着特定的编译器可以选择在特定情况下发生的情况,但应在编译器手册中注明效果是什么。

在许多处理器中往往有两组移位指令,即逻辑移位和算术移位。逻辑右移将移位,并用零填充暴露的位。算术右移(假设再次为2的补码)将使用最高有效位的相同位值填充暴露的位,以使其结果与使用移位除以2的结果一致。(例如,-4 &gt;&gt; 1 == 0xFFFFFFFC&gt;&gt; 1 == 0xFFFFFFFE == -2。)

在您的情况下,似乎编译器实现者在应用于有符号整数时选择使用算术移位,因此将负值向右移动的结果仍为负值。在位模式方面0xFFFF0000&gt;&gt; 24给出0xFFFFFFFF。

除非你完全确定你在做什么,否则最好只对无符号类型执行按位运算,因为它们的内部表示可以安全地被视为一组位。您可能还希望确保在这种情况下使用的任何数值都是无符号的,方法是将无符号后缀附加到您的数字上。

答案 1 :(得分:2)

右移负值(如b)可以用两种不同的方式定义:逻辑移位,用左边的零填充值(当产生正数时产生正数)移动非零量)和算术移位,它将值填充为1(总是产生负数)。 C中使用的定义是实现定义的,您的编译器显然使用算术移位,因此b >> 240xFFFFFFFF

答案 2 :(得分:1)

b&gt;&gt; 24给出0xFFFFFFFF 带负数的右键垫

List = (res << 8) | (b >> 24)
a        = 0x00FF00FF =  0000 0000 1111 1111 0000 0000 1111 1111
b        = 0xFFFF0000 =  1111 1111 1111 1111 0000 0000 0000 0000
~b       = 0x0000FFFF =  0000 0000 0000 0000 1111 1111 1111 1111
~b & a   = 0x000000FF =  0000 0000 0000 0000 0000 0000 1111 1111, = res
res << 8 = 0x0000FF00 =  0000 0000 0000 0000 1111 1111 0000 0000
 b >> 24 = 0xFFFFFFFF =  1111 1111 1111 1111 1111 1111 1111 1111
List     = 0xFFFFFFFF =  1111 1111 1111 1111 1111 1111 1111 1111

答案 3 :(得分:0)

黄金法则:永远不要将带符号的运算符与有符号运算符混合。

将所有整数更改为无符号整数。正如预防措施一样,将所有文字也改为无符号。

#include <stdint.h>

uint32_t a = 0x00FF00FFu;
uint32_t b = 0xFFFF0000u;

uint32_t res = (~b & a);