4

我在编写一个试图打印 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-sized 值时unsigned char产生char-sized 值。

4

3 回答 3

8

当将小于的类型传递int给可变参数函数printf时,它会被提升为type int

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

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

于 2022-02-25T18:12:43.980 回答
2

它们不会产生不同宽度的值。它们产生的值具有不同数量的设置位。

在您的 C 实现中,它显示int为 32 位并char已签名。我将在此答案中使用这些,但读者应注意 C 标准允许其他选择。

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

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

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

(unsigned char)~0)中,~0再次具有值 -1 用位 FFFFFFFF 表示,但现在强制转换为unsigned char。转换为无符号整数类型需要模M,其中M比该类型的最大值大一,因此 8 位为 256 unsigned char。在数学上,转换为 -1 + 1•256 = 255,即起始值加上将值带入 范围所需的 256 的倍数unsigned char。结果是255。实际上是取低八位来实现的,所以FFFFFFFF就变成了FF。然而,在 中unsigned char,位 FF 表示 255 而不是 -1。

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

于 2022-02-25T18:23:45.810 回答
1

在这两个电话中

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

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

在使用的系统中,类型char表现为 type signed char。因此,当表达式提升为 type 时,会传播此表达式中的符号位int

另一方面,在整数提升之前,由于转换为无符号类型,此表达式(unsigned char)~0具有类型。unsigned char因此,当表达式提升为 type 时,不会传播任何符号位int

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

printf("%x\n", ( unsigned int )(char)~0);
于 2022-02-25T18:17:07.990 回答