为什么C语言printf函数存在hh、h两种长度修饰符?
可变参数默认提升规则与printf长度修饰符设计疑问解答
前提:可变参数的默认参数提升规则
在C语言的可变参数函数中,参数传递时会触发默认参数提升,相关规则出自C标准草案N2176:
6.5.2.2.6 若表示被调用函数的表达式类型不包含原型,则每个参数会执行整数提升,类型为
float的参数会被提升为double,这类操作被称为默认参数提升。[...]
6.5.2.2.7 [...] 函数原型声明符中的省略号表示会让参数类型转换在最后一个已声明参数处停止,默认参数提升会作用于后续的可变参数。
基于该规则,以下代码运行完全合法:
signed char c = 123; int i = 123; float f = 123; double d = 123; printf("%d\n", i); // 正常运行 printf("%d\n", c); // 正常运行,即便%d预期接收int类型参数 printf("%f\n", d); // 正常运行 printf("%f\n", f); // 正常运行,即便%f预期接收double类型参数
核心疑问
既然char、short类型作为可变参数传递时会被默认提升为int,那为什么printf还要专门设计用于char的hh长度修饰符、用于short的h长度修饰符呢?
解答
默认参数提升是值的传递规则,仅保证小整数类型能以int的形式正确传递给可变参数函数,并没有抹除原始类型的语义需求。hh、h修饰符的作用是明确告诉printf:当前拿到的这个被提升后的int值,原始类型是更窄的char/short,需要按照窄类型的规则来解释输出。
最典型的使用场景是负数/溢出值的范围解释,举个例子:
// 1. signed char 负数场景 signed char c = -1; printf("%d\n", c); // 输出 -1,按有符号int解释符合预期 printf("%u\n", c); // 32位int环境下输出 4294967295,因为提升后的int值为0xFFFFFFFF,按无符号int解释 printf("%hhu\n", c); // 输出 255,按无符号char解释,符合预期 // 2. short 溢出场景 short s = 32768; // 16位signed short的范围是-32768~32767,这里赋值后s实际值为-32768 printf("%d\n", s); // 输出 -32768,按有符号int解释符合预期 printf("%u\n", s); // 32位int环境下输出 4294934528 printf("%hu\n", s); // 输出 32768,按无符号short解释,符合预期
除此之外,hh、h修饰符也可以用来快速截断大整数的高位,只输出低8位/低16位的数值,不需要手动做位运算截断。
内容的提问来源于stack exchange,提问作者ikegami
相关产品推荐
相关产品推荐

