为什么 `(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”的输出。
我在编写试图打印 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”的输出。