为什么 `(char)~0` 和 `(unsigned char)~0` return 值的宽度不同?

Why do `(char)~0` and `(unsigned char)~0` return values of different widths?

我在编写试图打印 UTF-8 字符的组成字节值的程序时遇到了这个问题。

这是我为测试各种 ~0 操作而编写的程序:

#include <stdio.h>

int main()
{
    printf("%x\n", (char)~0); // ffffffff
    printf("%x\n", (unsigned char)~0); // ff
    printf("%d\n", sizeof(char) == sizeof(unsigned char)); // 1
    printf("%d\n", sizeof(char) == sizeof(unsigned int)); // 0
    printf("%d\n", (char)~0 == (unsigned int)~0); // 1
}

我很难理解为什么 char 会产生 int 大小的值,而 unsigned char 会产生 char 大小的值。

将小于 int 的类型传递给 printf 等可变参数函数时,它会 提升 以键入 int

在第一种情况下,您传递的 char 值为 -1,其表示(假设 2 的补码)为 0xff。这被提升为具有值 -1 和表示 0xffffffff 的 int,因此这就是打印的内容。

在第二种情况下,您传递的 unsigned char 值为 255,表示为 0xff。这被提升为具有值 255 和表示 0x000000ff 的 int,因此这就是打印的内容(没有前导零)。

在这两个调用中

printf("%x\n", (char)~0); // ffffffff
printf("%x\n", (unsigned char)~0); // ff

由于整数提升,表达式 (char)~0)(unsigned char)~0) 被转换为类型 int

在使用的系统中,类型 char 的行为与类型 signed char 相同。因此,当表达式提升为类型 int.

时,会传播此表达式中的符号位

另一方面,在整数提升之前,这个表达式 (unsigned char)~0 由于转换为无符号类型而具有类型 unsigned char。因此,当表达式提升为类型 int.

时,两个符号位都不会传播

注意转换说明符x应用于unsigned int类型的对象。所以第一次调用printf应该这样写

printf("%x\n", ( unsigned int )(char)~0);

它们不会产生不同宽度的值。它们产生的值中包含不同数量的设置位。

在您的 C 实现中,int 似乎是 32 位并且 char 是有符号的。我将在这个答案中使用这些,但读者应该注意 C 标准允许其他选择。

我将使用十六进制来表示代表值的位。

(char)~0中,0是一个int~0 然后有位 FFFFFFFF。在 32 位二进制补码 int 中,这表示 -1。 (char) 将其转换为 char.

此时,我们有一个值为 −1 的 char,用位 FF 表示。当它作为参数传递给 printf 时,它会自动转换为 int。由于它的值为 −1,因此它被转换为值为 −1 的 int。表示 int 的位是 FFFFFFFF。您要求 printf 将其格式化为 %x。从技术上讲,这是一个错误; %x 用于 unsigned int,但您的 printf 实现将位 FFFFFFFF 格式化为 unsigned int,生成“ffffffff”的输出。

(unsigned char)~0) 中,~0 再次具有值 −1,用位 FFFFFFFF 表示,但现在转换为 unsigned char。转换为无符号整数类型换行模 M,其中 M 比类型的最大值大 1,因此 [=63 为 256 =] unsigned char。从数学上讲,转换为 −1 + 1•256 = 255,即起始值加上使值进入 unsigned char 范围所需的 256 的倍数。结果是255,实际是取低八位实现的,所以FFFFFFFF变成FF。然而,在 unsigned char 中,位 FF 表示 255 而不是 −1。

现在我们有一个值为 255 的 unsigned char,用位 FF 表示。将其传递给 printf 会导致自动转换为 int。由于它的unsigned char值为255,所以转换为int的结果是255。当你要求printf格式化为%x时(如上错误), printf 将其格式化为 unsigned int,产生“ff”的输出。