R..*_*R.. 55 c printf promotions variadic-functions format-specifiers
除了%hn
和%hhn
(指定h
或hh
指定指向对象的大小),格式说明符的h
和hh
修饰符有printf
什么意义?
由于标准要求的默认促销适用于可变函数,因此不可能将类型char
或short
(或其任何有符号/无符号变体)的参数传递给printf
.
根据7.19.6.1(7),h
修饰符:
指定以下d,i,o,u,x或X转换规范适用于short int或unsigned short int参数(该参数将根据整数提升进行提升,但其值应转换为short int或打印前的unsigned short int); 或者后续的n转换规范适用于指向short int参数的指针.
如果参数实际上是类型short
或unsigned short
,则促销int
后转换回short
或unsigned short
将产生与促销相同的值int
而不进行任何转换.因此,对于类型的参数short
或unsigned short
,%d
,%u
等应给予相同的结果到%hd
,%hu
等.(并且同样地对于char
类型和hh
).
据我所知,h
或者hh
修饰符可能有用的唯一情况是当参数将其传递int
到范围之外时,short
或者unsigned short
,例如
printf("%hu", 0x10000);
Run Code Online (Sandbox Code Playgroud)
但我的理解是,传递错误的类型会导致不确定的行为,所以你不能指望它打印0.
我见过的一个真实案例是这样的代码:
char c = 0xf0;
printf("%hhx", c);
Run Code Online (Sandbox Code Playgroud)
作者希望它打印,f0
尽管实现具有char
签名的普通类型(在这种情况下,printf("%x", c)
将打印fffffff0
或类似).但这种期望值得保证吗?
(注意:正在发生的是原始类型char
,它被提升int
并转换回unsigned char
而不是char
,因此改变了打印的值.但标准是否指定了这种行为,或者它是一个破坏软件的实现细节可能依赖?)
Mic*_*urr 15
一个可能的原因:在格式化的输入函数中使用那些修饰符的对称性?我知道这不是绝对必要的,但是可能有价值吗?
虽然他们没有提到C99 Rationale文档中 "h"和"hh"修饰符对称性的重要性,但委员会确实提到它是为什么支持"%p"转换说明符的考虑因素fscanf()
(即使对C99而言并不新鲜 - "%p"支持在C90中):
使用%p的输入指针转换被添加到C89,尽管对于fprintf的对称性显然存在风险.
在上一节中fprintf()
,C99基本原理文件确实讨论了添加"hh",但仅仅引用了读者的fscanf()
部分:
在H99中添加%hh和%ll长度修饰符(参见§7.19.6.2).
我知道这是一个微妙的线索,但无论如何我都在猜测,所以我想我会给出任何可能存在的争论.
此外,为了完整性,"h"修饰符符合最初的C89标准 - 可能即使由于现有的广泛使用而非严格必要,即使可能没有使用修饰符的技术要求,它也会存在. .
在%...x
模式下,所有值都被解释为无符号.因此,负数会被打印为未经签名的转换.在大多数处理器使用的2的补码算法中,有符号的负数和它的正无符号等价之间的位模式没有区别,后者由模运算定义(将字段的最大值加1加到负数,根据符合C99标准).许多软件 - 特别是最有可能使用的调试代码%x
- 做出了一个默认的假设,即带符号的负值及其无符号转换的位表示是相同的,这仅适用于2的补码机器.
这个演员的机制是这样的,十进制的值表示总是暗示,可能是不准确的,数字已经以2的补码呈现,只要它没有达到不同整数表示具有不同范围的边缘条件.这甚至适用于算术表示,其中值0未用全0的二进制模式表示.
因此,在任何机器上short
显示为负unsigned long
十六进制的负数将f
由于促销中的隐式符号扩展而被填充,其printf
将被打印.的值是相同的,但它是作为对字段的大小真正视觉上误导,暗示的范围内的量显著,根本不存在.
%hx
截断显示的表示以避免此填充,正如您从实际用例中得出的结论.
printf
当传递到应该打印为a int
的范围之外时,行为是未定义short
的short
,但到目前为止最简单的实现只是通过原始向下丢弃丢弃高位,所以虽然规范不需要任何特定的行为,几乎任何理智的实现只是执行截断.不过,通常有更好的方法可以做到这一点.
如果printf没有填充值或显示有符号值的无符号表示,%h
则不是很有用.
我能想到的唯一用途是传递unsigned short
或unsigned char
使用%x
转换说明符.你不能简单地使用裸%x
- 可以提升值int
而不是unsigned int
,然后你有未定义的行为.
你的选择要么明确地将论证转化为unsigned
; 或者使用%hx
/ %hhx
使用裸参数.