我们以下面两个1字节变量为例:
uint8_t x1 = 0x00;
uint8_t x2 = 0xFF;
打印按位补码时,结果是一个4字节的变量:
printf("%02X -> %02X; %02X -> %02X\n", x1, ~x1, x2, ~x2);
00 -> FFFFFFFF; FF -> FFFFFF00
我知道这可以通过使用强制转换或屏蔽来“解决”:
printf("%02X -> %02X; %02X -> %02X\n", x1, (uint8_t) ~x1, x2, (uint8_t) ~x2);
00 -> FF; FF -> 00
printf("%02X -> %02X; %02X -> %02X\n", x1, ~x1&0xFF, x2, ~x2&0xFF);
00 -> FF; FF -> 00
但为什么非直觉行为首先出现?
许多计算机处理器的大多数操作都具有“字”大小。例如,在32位机器上,可能存在加载32位的指令,存储32位的指令,将一个32位数加到另一位的指令,等等。
在这些处理器上,使用其他尺寸可能会令人讨厌。可能没有将16位数乘以另一个16位数的指令。 C在这些机器上长大。它被设计成int
(或unsigned int
)是“无论大小对你运行的机器有什么好处”而且char
或short
适合存储在内存中,但是,一旦它们从内存加载到处理器寄存器中,C就可以了和他们一样,他们是int
。
这简化了早期C编译器的开发。编译器不必通过执行32位补码指令,然后执行AND指令来删除不需要的高位来实现补码。它只做了一个简单的32位补码。
我们今天可以以不同的方式开发语言,但C承担着这种遗产。
当您将~
运算符应用于x1
和x2
时,值首先受整数提升,因为uint8_t
小于int
。然后将运算符应用于提升的值。
所以~x1
真的是~0x00000000
(即0xFFFFFFFF
)和~x2
真的是~0x000000FF
(即FFFFFF00
)。这就是为什么你得到你正在获得的价值。
此外,%x
格式说明符期望它打印的unsigned int
。
您需要使用%hhx
作为格式说明符。这表示unsigned char
论点。
printf("%02hhX -> %02hhX; %02hhX -> %02hhX\n", x1, ~x1, x2, ~x2);