printf()中变量的提升和转换

Promotions and conversions of variables in printf()

在这种情况下,

#include <stdio.h>

int main()
{
    unsigned char a = 1;
    printf("%hhu", -a);

    return 0;
}

printf中的参数-a通过一元减运算符的整数提升提升为int,随后通过默认参数提升提升为[=14] =] 通过格式说明符。
所以 -a => -(int)a(by ~) => 函数调用没有转换 => (unsigned char)-(int)a(by %hhu)。我的想法对吗?

printf 是可变参数函数。 ... 参数传递的参数类型在函数内部是未知的。因此,任何可变参数函数都必须依赖其他机制来解释 va_args 参数的类型。 printf 和家人使用 const char* format 字符串“告诉他们”传递了什么样的参数。传递与其格式说明符指定的预期类型不同的类型会导致未定义的行为。

例如:

printf("%f", 24)

是未定义的行为。任何地方都没有从 intfloat 的转换,因为参数按原样传递(在提升后)并且在 printf 函数内部错误地将其第一个参数视为 float . printf不知道也不可能知道参数的真实类型是int.

可变参数有自己的一些提升。对你的问题感兴趣 unsigned char 被提升为 intunsigned int (我不确定 tbo)。因此,可变参数实际上是 unsigned char 类型是不可能的。所以 hhu 虽然确实是 unsigned char 的说明符,但它实际上会期望一个 unsigned int (int),这就是你传递给它的内容。

所以 afaik 代码是安全的,因为一元减号和传递可变参数引起的两个整数提升。不过我不是 100% 确定。整数提升很奇怪也很复杂。

a-a 中被提升为 int 并且 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的规范:

The unsigned int argument is converted to unsigned octal (o), unsigned decimal (u),…

7.21.6.1 中的 hh 7:

Specifies that a following d, i, o, u, x, or X conversion specifier applies to a signed char or unsigned char argument (the argument will have been promoted according to the integer promotions, but its value shall be converted to signed char or unsigned char before printing);…

首先要解决“signed char或者unsigned char”这个问题。这是否表示我们可以为 %hhu 传递 signed charunsigned char?我想不是;我认为作者刚刚将 %hhd(旨在转换 signed char)和 %hhu(旨在转换 unsigned char)的语言放在一起。所以我相信目的是应该为 %hhu 转换规范传递一个提升的 unsigned char

Apple Clang 11.0.0 似乎同意,当传递 -a(但不是 a)时,它会警告:“warning: format specified type 'unsigned char' but the argument has type 'int' [-W格式]”

如上所述,传递 -a 可能会传递一个无法通过传递提升的 unsigned char 产生的值。它甚至可能传递一个无法通过传递提升的 signed charunsigned char 产生的值。在这种情况下,可以说我们违反了传递 unsigned char 的要求,因此 C 标准没有指定结果行为。尽管它说传递的值应转换为 unsigned char,但我认为这是一种概念性转换,而不是对库实现的特定要求,并且也属于“好像”规则:它确实如果程序的结果定义行为相同,则实际上不必执行。但是,由于可能未定义传递不正确的值,因此我们没有定义的行为。

这可能是对规则的严格解读,但如果 printfa 为 1 时打印“4294967295”而不是“255”,我不会感到非常惊讶。