h和hh修饰符用于printf的目的是什么?

除了%hn%hhn (其中hhh指定指向对象的大小), printf格式说明符的hhh修饰符有什么意义?

由于标准要求的可变参数函数的默认升级,不可能将charshorttypes的参数(或其任何有符号/无符号变体)传递给printf

根据7.19.6.1(7), h修饰符:

指定后面的d,i,o,u,x或X转换规则适用于short int或unsigned short int参数(参数将根据整数提升进行提升,但其值将转换为short int或打印之前无符号短整型); 或者一个下面的n转换规范适用于一个短int参数的指针。

如果参数实际上是shorttypes或unsigned shorttypes,那么将int提升为int ,然后将其转换为shortunsigned short将会产生与提升为int相同的 ,而不会返回任何转换。 因此,对于types为shortunsigned short%d%u等应该给%hd%hu等等(对于chartypes和hh同样)。

据我所知, hhh修饰符可能有用的唯一情况是当参数在shortunsigned short范围外传递一个int ,例如

 printf("%hu", 0x10000); 

但我的理解是,传递这样的错误types会导致不确定的行为,所以你不能期望它打印0。

我见过的一个真实世界的例子是这样的代码:

 char c = 0xf0; printf("%hhx", c); 

尽pipe实现了一个简单的chartypes(在这种情况下, printf("%x", c)会打印fffffff0或类似的东西),作者期望它打印f0 。 但是这个期望值得吗?

(注意:原来的types是char ,它被提升为int并被转换回unsigned char而不是char ,从而改变了被打印的值。但是标准是否指定了这个行为,或者它是一个实现细节破碎的软件可能依靠?)

一个可能的原因:在格式化input函数中使用这些修饰符的对称性? 我知道这不是绝对必要的,但也许有价值的东西呢?

虽然他们没有提到C99基本原理文档中的“h”和“hh”修饰符的对称性的重要性,但委员会确实提到它是为了考虑为什么fscanf()支持“%p”转换说明符即使这对于C99来说并不是新鲜事物 – “%p”支持在C90中):

使用%p的input指针转换被添加到C89,虽然它显然是有风险的,因为与fprintf的对称性。

fprintf()的章节中,C99的基本原理文件确实讨论了“hh”被添加了,但是只是把读者引用到fscanf()部分:

在C99中添加了%hh和%ll长度修饰符(参见§7.19.6.2)。

我知道这是一个微不足道的线索,但无论如何我都在猜测,所以我想我会提出任何可能的论点。

此外,为了完整性,“h”修饰符是在原来的C89标准 – 大概是在那里,即使它不是严格必要的,因为广泛的现有用途,即使可能没有技术要求使用修饰符。

%...x模式下,所有值都被解释为无符号。 因此负数将被打印为无符号转换。 在大多数处理器使用的二进制补码algorithm中,在有符号负数和它的正无符号等价物之间的比特模式没有差别,这是由模数algorithm定义的(将场的最大值加上一个负数,根据到C99标准)。 许多软件(特别是最有可能使用%x的debugging代码)使得无声的假设是:有符号负值的位表示和无符号强制转换是相同的,这在2的补码机器上才是真实的。

这种转换的机制是这样的:hex值的表示总是暗示,可能不准确的是,一个数字已经以二进制补码表示,只要它不碰到不同整数表示具有不同范围的边缘条件。 这甚至适用于算术表示,其中值0不是用全0的二进制模式表示的。

因此,在hex中显示为unsigned long整数的负short将在任何机器上填充f ,这是由于在促销中隐式的符号扩展, printf将打印。 价值是一样的,但是在视觉上误导了场的大小,意味着大量的范围,根本不存在。

%hx截断显示的表示,以避免这种填充,就像您从现实世界的用例中得出的结论一样。

printf的行为在short应该被打印为short的范围之外传递一个int时是不确定的,但是最简单的实现到目前为止只是通过原始的downcast丢弃高位,所以虽然spec 不需要任何特定的行为,几乎任何理智的实现将只执行截断。 不过,通常有更好的方法来做到这一点。

如果printf不是填充值或显示带符号值的无符号表示, %h不是很有用。

我能想到的唯一的用途是传递一个unsigned shortunsigned char并使用%x转换说明符。 你不能简单地使用纯粹的%x – 值可能被提升为int而不是unsigned int ,然后你有未定义的行为。

你的select要么明确地把参数转换为unsigned ; 或者使用%hx / %hhx作为参数。

printf()等的可变参数会使用默认转换自动提升,所以在传递给函数时,任何shortchar值都会被提升为int

在缺lesshhh修饰符的情况下,您将不得不掩盖传递的值以可靠地获取正确的行为。 使用修饰符,您不再需要掩饰值; printf()实现正确地完成了这项工作。

具体来说,对于格式%hxprintf()的代码可以执行如下操作:

 va_list args; va_start(args, format); ... int i = va_arg(args, int); unsigned short s = (unsigned short)i; ...print s correctly, as 4 hex digits maximum ...even on a machine with 64-bit `int`! 

我很乐意假设short是一个16位的数量; 当然这个标准实际上并不能保证。

我同意你的观点,这并不是绝对必要的,所以仅仅因为这个原因,在C库函数中是不行的:)

这对于不同标志的对称性可能是“好的”,但是由于它隐藏了“转换为int ”规则,所以它大部分适得其反。