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

问题描述 投票:0回答:3

在这种情况下,

#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
)。我的想法对吗?

c implicit-conversion integer-promotion
3个回答
3
投票

您是正确的,

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
的规范:

unsigned int
参数被转换为无符号八进制(o)、无符号十进制(u),…

以及 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”,我不会感到太惊讶。


2
投票

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% 确定。整数促销既奇怪又复杂。


0
投票

我很确定你的 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)

© www.soinside.com 2019 - 2024. All rights reserved.