考虑以下程序,
#include <stdio.h>
int main()
{
char a = 130;
unsigned char b = 130;
printf("a = %d\nb = %d\n",a,b);
return 0;
}
该程序将显示以下输出。
a = -126
b = 130
我的问题是printf()
函数如何知道a
的类型是签名的,b
的类型是无符号的,以显示上面的结果?
printf()
不知道类型,这就是为什么你必须提供正确的格式字符串。 printf()
的原型如下:
int printf(const char * restrict format, ...);
因此,具有已知类型的唯一参数是第一个,即格式字符串。
这也意味着之后传递的任何参数都受默认参数提升的影响 - 强烈简化,读取它,因为任何整数都将被转换为至少int
- 或者请谷歌了解该术语以了解每一个细节;)
在您的示例中,您具有实现定义的行为:
char a = 130;
如果你的char
可以代表130
,那就是你在printf()
的输出中看到的。提升int
的价值不会改变价值。你得到一个负数,这意味着130
溢出你的char
。在C中转换期间溢出有符号整数类型的结果是实现定义的,您获得的值可能意味着在您的机器上,char
有8位(因此最大值为127
)并且有符号整数溢出导致环绕到负值范围。你不能依赖那种行为!
简而言之,负数在此行中创建 - 130
的类型为int
,将其分配给char
转换它并且此转换溢出。
一旦你的char
具有值-126
,将其传递给printf()
只需将其转换为int
,而不是更改值。
printf()
的附加参数根据类型说明符进行格式化。有关C格式说明符的列表,请参见此处。
https://fr.cppreference.com/w/c/io/fprintf
确实,人们不会期望b
在你的例子中被打印为130
,因为你使用的是%d
说明符而不是%u
。这种惊人的行为似乎在这里解释。
Format specifier for unsigned char
我希望我能很好地回答你的问题。
编辑:我不能评论Felix Palmen在我的低声誉上的回答。默认参数提升确实似乎是关键在这里,但对我来说,除了a
溢出之外,真正的问题是为什么b
仍然打印为130
尽管使用了签名说明符。它也可以用默认参数提升来解释,但应该更精确。
您需要查看printf
中stdio.h
语句的定义。你已经在评论中得到了答案printf
只是将格式指向的字符串写入stdout。
它是可变函数,它使用vargas来获取可变长度参数列表中的所有参数。
你这是来自glibc from the GNU version。
int __printf (const char *format, ...)
{
va_list arg;
int done;
va_start (arg, format);
done = vfprintf (stdout, format, arg);
va_end (arg);
return done;
}
什么是vfprintf呢?
它只是将格式指向的字符串写入流中,以与printf相同的方式替换任何格式说明符,但使用由arg标识的变量参数列表中的元素而不是其他函数参数。
有关vfprintf的更多信息
printf()
不知道参数的数据类型。它适用于您传递的格式说明符。您使用的数据类型是char
(范围从-128到+127)和unsigned char
(范围从0到255)。 a
的输出在127之后溢出。因此输出到-126。