在可变函数中,会发生默认的参数提升。
6.5..2.6如果表示被调用函数的表达式的类型不包括原型,则对每个参数执行整数提升,并且将类型为
float
的参数提升为double
。这些被称为默认参数提升。[…]
6.5..2.7[…]函数原型声明符中的省略号表示法会导致参数类型转换在最后一个声明的参数之后停止。默认的参数提升是对尾随参数执行的。
因此,
signed char c = 123;
int i = 123;
float f = 123;
double d = 123;
printf("%dn", i); // ok
printf("%dn", c); // ok, even though %d expects int.
printf("%fn", d); // ok
printf("%fn", f); // ok, even though %f expects double.
那么为什么char
(hh
(和short
(h
(有printf
长度修饰符呢?
章节编号参考N2176。
考虑这个例子:
#include <stdio.h>
int main(void)
{
unsigned short x = 32770;
printf("%dn", x) ; // (1)
printf("%un", x) ; // (2)
}
在典型的16位实现中,默认参数提升采用unsigned short
到unsigned int
,而在典型的32位实施中,unsigned short
变为int
。
因此,在16位系统上(1(是UB,(2(是正确的,但在32位系统上,(1(正确,(2。
使用%hu
打印x
适用于所有系统,您不必考虑这些问题。
可以在具有CCD_ 15的系统上为CCD_ 14构造类似的示例。
这是为了向后兼容。
在C89标准的草案版本中,使用%x
格式说明符打印签名的int
、short
或char
不是未定义的行为:
d,i,o,u,x,x int参数转换为有符号十进制(d或i(、无符号八进制(o(、无签名十进制(u(或无符号十六进制表示法(x或x(;字母abcdef用于x转换,字母abcdef用于x转换。精度指定要显示的最小位数;如果被转换的值可以用更少的数字表示,它将用前导零展开。默认精度为1。转换显式精度为零的零值的结果是没有字符。
这似乎记录了预先标准化的C,使用格式说明符(如用于签名值的%x
(是一种现有的做法,因此可能存在使用h
和hh
长度修饰符的预先存在的代码库。
在没有h
和hh
长度修饰符的情况下,如果使用简单的%X
格式说明符打印,则具有位模式0xFF
的signed char
值将在32位int
系统上打印为0xFFFFFFFF
。
关于hh
说明符,它是在C99中显式添加的,目的是利用打印stdint.h
/inttypes.h
中的所有默认固定大小类型。C99使类型int_leastn_t
从8到64是强制性的,因此需要相应的格式说明符。
根据C99原理5.10,§7.19.6.1(fprintf
(:
在C99中添加了
%hh
和%ll
长度修饰符(见§7.19.6.2(
§7.19.6.2(fscanf
(:
C99的一个新特性:在C99中添加了
hh
和ll
长度修饰符。ll
支持新的long-long-int类型。CCD_ 41增加了与所有其他整数类型一样对待字符类型的能力;这在实现诸如<inttypes.h>
中的SCNd8
之类的宏时是有用的(参见7.18(
C99之前,只有用于打印整数类型的d
、h
和l
。在C99中,例如,传统的实现可以将inttypes.h
说明符定义为:
#define SCNi8 hh
#define SCNi16 h
#define SCNi32 d
#define SCNi64 ll
现在,默认的参数promotions变成了printf
/scanf
实现的头痛,而不是inttypes.h
实现。
它们不是用于printf()
的,而是用于scanf()
,以便能够使用对short
整数和char
整数的引用。为了一致性和完整性,它们被printf()
函数所接受,但它们是不可区分的,因为对于short
和char
整数值类型的所有参数,printf()
的vaarg参数都被提升为int
。因此,它们在printf()
中是等价的,而在scanf()
和朋友中不是等价的。