在这种情况下,
#include <stdio.h>
int main()
{
unsigned char a = 1;
printf("%hhu", -a);
return 0;
}
-a
中的参数printf
通过一元减运算符的整数提升提升为int
,随后通过默认参数提升提升,最后通过格式说明符转换为unsigned char
。-a
=> -(int)a
(by ~
) => 不通过函数调用进行转换 => (unsigned char)-(int)a
(by %hhu
)。我的想法对吗?
您是正确的,
a
在 int
中提升为 -a
,并且 printf("%hhu", -a);
将 int
传递到 printf
。使用 %hhu
执行的名义转换尚不清楚。
请注意,如果
a
不为零,则 -a
生成的值(在 int
中)不是 unsigned char
值。此外,对于八位二进制补码 signed char
,如果 a
大于 128,则 -a
生成的值不是 signed char
值。
为了理解
%hhu
,我们查看 C 2018 7.21.6.1 8 中 u
的规范:
参数被转换为无符号八进制(o)、无符号十进制(u),…unsigned int
以及 7.21.6.1 7 中的
hh
:
指定后面的 d、i、o、u、x 或 X 转换说明符应用于
或signed char
参数(参数将根据整数提升进行提升,但其值应转换为打印前unsigned char
或signed char
);…unsigned char
首先我们要解决这个“
signed char
or unsigned char
”的问题。这是否表示我们可以通过 signed char
或 unsigned char
来传递 %hhu
?我认为不是;我认为作者刚刚将 %hhd
(旨在转换 signed char
)和 %hhu
(旨在转换 unsigned char
)的语言放在一起。所以我相信其目的是应该为 unsigned char
转换规范传递升级的 %hhu
。
Apple Clang 11.0.0 似乎同意,当传递
-a
(但不是 a
)时,它警告:“警告:格式指定类型 'unsigned char' 但参数具有类型 'int' [-Wformat]”
如上所述,传递
-a
可能会传递无法通过传递提升的 unsigned char
得到的值。它甚至可能传递一个无法通过传递提升的 signed char
或 unsigned char
得到的值。在这种情况下,可以说我们违反了传递 unsigned char
的要求,因此 C 标准没有指定结果行为。尽管它说传递的值应转换为 unsigned char
,但我相信这是一种概念上的转换,而不是对库实现的具体要求,并且这也属于“好像”规则:它实际上并不如果程序的最终定义行为相同,则必须执行。但是,由于可能无法定义传递不正确的值,因此我们没有定义的行为。
这可能是对规则的严格解读,但如果当
printf
为 1 时 a
打印“4294967295”而不是“255”,我不会感到太惊讶。
printf
是一个可变参数函数。 ...
参数传递的参数类型在函数内部是未知的。因此,任何可变参数函数都必须依赖其他机制来解释 va_arg
参数的类型。 printf
和家人使用 const char* format
字符串来“告诉他们”通过了什么样的参数。传递与格式说明符指定的预期类型不同的类型会导致未定义的行为。
例如:
printf("%f", 24)
是未定义的行为。在任何地方都没有从
int
到 float
的转换,因为参数按原样传递(提升后),并且在 printf
内部,函数错误地将其第一个参数视为 float
。 printf
不知道也不可能知道参数的真实类型是int
。
可变参数本身也经历了一些提升。对您的问题感兴趣的
unsigned char
被提升为int
或unsigned int
(我不确定)。因此,可变参数实际上不可能是 unsigned char
类型。因此 hhu
while 确实是 unsigned char
的说明符,它实际上会期望一个 unsigned int
(int
),这就是您传递给它的内容。
所以据我所知,代码是安全的,因为一元减法和传递可变参数引起了两个整数提升。但我不是 100% 确定。整数促销既奇怪又复杂。
我很确定你的 unsigned char 会被提升为 unsigned int,而不是 int。 考虑以下示例:
char sc = 0xFE;
unsigned char uc = 0xFE;
printf("%.8x\n", sc);
printf("%.8x\n", uc);
结果:
fffffffe
000000fe
char 被提升为 int,所以 0xFE(十进制的 -2)变成 0xFFFFFFFE(十进制的 -2),而 unsigned char 被提升为 unsigned int,所以 0xFE(十进制的 254)变成 0x000000FE(十进制的 254)