C语言 printf 的 h 和 hh 修饰符的目的是什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4586962/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What is the purpose of the h and hh modifiers for printf?
提问by R.. GitHub STOP HELPING ICE
Aside from %hnand %hhn(where the hor hhspecifies the size of the pointed-toobject), what is the point of the hand hhmodifiers for printfformat specifiers?
除了%hnand %hhn(其中horhh指定指向对象的大小),格式说明符的handhh修饰符的意义是什么printf?
Due to default promotions which are required by the standard to be applied for variadic functions, it is impossible to pass arguments of type charor short(or any signed/unsigned variants thereof) to printf.
由于标准要求应用于可变参数函数的默认提升,不可能将类型char或short(或其任何有符号/无符号变体)的参数传递给printf.
According to 7.19.6.1(7), the hmodifier:
根据 7.19.6.1(7),h修饰符:
Speci?es that a following d, i, o, u, x, or X conversion speci?er applies to a short int or unsigned short int argument (the argument will have been promoted according to the integer promotions, but its value shall be converted to short int or unsigned short int before printing); or that a following n conversion speci?er applies to a pointer to a short int argument.
指定以下 d、i、o、u、x 或 X 转换说明符适用于 short int 或 unsigned short int 参数(该参数将根据整数提升而提升,但其值应为打印前转换为 short int 或 unsigned short int);或者后面的 n 转换说明符适用于指向短整型参数的指针。
If the argument was actually of type shortor unsigned short, then promotion to intfollowed by a conversion back to shortor unsigned shortwill yield the same valueas promotion to intwithout any conversion back. Thus, for arguments of type shortor unsigned short, %d, %u, etc. should give identical results to %hd, %hu, etc. (and likewise for chartypes and hh).
如果参数实际上是shortor类型unsigned short,则提升 toint后跟转换回shortorunsigned short将产生与提升 to相同的值,而int无需任何转换回。因此,对于类型的参数short或unsigned short,%d,%u等应给予相同的结果到%hd,%hu等。(并且同样地对于char类型和hh)。
As far as I can tell, the only situation where the hor hhmodifier could possibly be useful is when the argument passed it an intoutside the range of shortor unsigned short, e.g.
据我所知,horhh修饰符可能有用的唯一情况是参数传递给它int的范围超出shortor的范围unsigned short,例如
printf("%hu", 0x10000);
but my understanding is that passing the wrong type like this results in undefined behavior anyway, so that you could not expect it to print 0.
但我的理解是,像这样传递错误的类型会导致未定义的行为,因此您不能期望它打印 0。
One real world case I've seen is code like this:
我见过的一个真实案例是这样的代码:
char c = 0xf0;
printf("%hhx", c);
where the author expects it to print f0despite the implementation having a plain chartype that's signed (in which case, printf("%x", c)would print fffffff0or similar). But is this expectation warranted?
f0尽管实现具有char签名的普通类型(在这种情况下,printf("%x", c)将打印fffffff0或类似),作者仍希望它打印。但这种期望有根据吗?
(Note: What's going on is that the original type was char, which gets promoted to intand converted back to unsigned charinstead of char, thus changing the value that gets printed. But does the standard specify this behavior, or is it an implementation detail that broken software might be relying on?)
(注意:发生的事情是原始类型是char,它被提升int并转换回unsigned char而不是char,从而改变了打印的值。但是标准是否指定了这种行为,或者它是否是损坏软件可能的实现细节靠什么?)
采纳答案by Michael Burr
One possible reason: for symmetry with the use of those modifiers in the formatted input functions? I know it wouldn't be strictly necessary, but maybe there was value seen for that?
一个可能的原因:为了与格式化输入函数中使用这些修饰符的对称性?我知道这不是绝对必要的,但也许可以看到它的价值?
Although they don't mention the importance of symmetry for the "h" and "hh" modifiers in the C99 Rationale document, the committee does mention it as a consideration for why the "%p" conversion specifier is supported for fscanf()(even though that wasn't new for C99 - "%p" support is in C90):
尽管他们在C99 基本原理文档中没有提到“h”和“hh”修饰符的对称性的重要性,但委员会确实提到它是考虑为什么支持“%p”转换说明符fscanf()(尽管对 C99 来说并不是新东西——“%p”支持在 C90 中):
Input pointer conversion with %p was added to C89, although it is obviously risky, for symmetry with fprintf.
使用 %p 的输入指针转换被添加到 C89,尽管它显然是有风险的,为了与 fprintf 对称。
In the section on fprintf(), the C99 rationale document does discuss that "hh" was added, but merely refers the reader to the fscanf()section:
在关于 的部分中fprintf(),C99 基本原理文档确实讨论了添加了“hh”,但只是让读者参考以下fscanf()部分:
The %hh and %ll length modifiers were added in C99 (see §7.19.6.2).
%hh 和 %ll 长度修饰符是在 C99 中添加的(参见 §7.19.6.2)。
I know it's a tenuous thread, but I'm speculating anyway, so I figured I'd give whatever argument there might be.
我知道这是一个微不足道的话题,但无论如何我都是在猜测,所以我想我会给出任何可能的论点。
Also, for completeness, the "h" modifier was in the original C89 standard - presumably it would be there even if it wasn't strictly necessary because of widespread existing use, even if there might not have been a technical requirement to use the modifier.
此外,为了完整起见,“h”修饰符在原始 C89 标准中 - 即使由于广泛的现有使用而并非绝对必要,即使可能没有使用修饰符的技术要求,它也可能存在.
回答by Adam Norberg
In %...xmode, all values are interpreted as unsigned. Negative numbers are therefore printed as their unsigned conversions. In 2's complement arithmetic, which most processors use, there is no difference in bit patterns between a signed negative number and its positive unsigned equivalent, which is defined by modulus arithmetic (adding the maximum value for the field plus one to the negative number, according to the C99 standard). Lots of software- especially the debugging code most likely to use %x- makes the silent assumption that the bit representation of a signed negative value and its unsigned cast is the same, which is only true on a 2's complement machine.
在%...x模式下,所有值都被解释为无符号。因此,负数被打印为它们的无符号转换。在大多数处理器使用的 2 的补码算法中,有符号负数与其无符号正等价物之间的位模式没有区别,后者由模数算法定义(将字段的最大值加一到负数,根据符合 C99 标准)。许多软件——尤其是最有可能使用的调试代码%x——都默默地假设有符号负值的位表示和它的无符号转换是相同的,这仅在 2 的补码机上才成立。
The mechanics of this cast are such that hexidecimal representations of value always imply, possibly inaccurately, that a number has been rendered in 2's complement, as long as it didn't hit an edge condition of where the different integer representations have different ranges. This even holds true for arithmetic representations where the value 0 is not represented with the binary pattern of all 0s.
这个转换的机制是这样的,值的十六进制表示总是暗示,可能不准确,一个数字已经以 2 的补码呈现,只要它没有达到不同整数表示具有不同范围的边缘条件。这甚至适用于不使用全 0 的二进制模式表示值 0 的算术表示。
A negative shortdisplayed as an unsigned longin hexidecimal will therefore, on any machine, be padded with f, due to implicit sign extension in the promotion, which printfwill print. The valueis the same, but it is truly visually misleading as to the size of the field, implying a significant amount of range that simply isn't present.
因此,由于促销中的隐式符号扩展,在任何机器上short显示为unsigned long十六进制的否定将被填充f,这printf将打印。该值是相同的,但它确实在视觉上误导了字段的大小,这意味着大量的范围根本不存在。
%hxtruncates the displayed representation to avoid this padding, exactly as you concluded from your real-world use case.
%hx截断显示的表示以避免这种填充,正如您从实际用例中得出的结论一样。
The behavior of printfis undefined when passed an intoutside the range of shortthat should be printed as a short, but the easiest implementation by far simply discards the high bit by a raw downcast, so while the spec doesn't requireany specific behavior, pretty much any sane implementation is going to just perform the truncation. There're generally better ways to do that, though.
printf当传递int超出范围时的行为是未定义的short,应该打印为 a short,但到目前为止最简单的实现只是通过原始向下转换丢弃高位,因此虽然规范不需要任何特定行为,但几乎任何理智的实现将只执行截断。不过,通常有更好的方法来做到这一点。
If printf isn't padding values or displaying unsigned representations of signed values, %hisn't very useful.
如果 printf 不是填充值或显示有符号值的无符号表示,%h则不是很有用。
回答by caf
The only use I can think of is for passing an unsigned shortor unsigned charand using the %xconversion specifier. You cannot simply use a bare %x- the value may be promoted to intrather than unsigned int, and then you have undefined behaviour.
我能想到的唯一用途是传递一个unsigned shortorunsigned char并使用%x转换说明符。您不能简单地使用裸%x- 该值可能会被提升为int而不是unsigned int,然后您就会有未定义的行为。
Your alternatives are either to explicitly cast the argument to unsigned; or to use %hx/ %hhxwith a bare argument.
您的替代方法是将参数显式转换为unsigned; 或使用%hx/%hhx与一个裸参数。
回答by mzimmers
I found it useful to avoid casting when formatting unsigned chars to hex:
我发现在将无符号字符格式化为十六进制时避免强制转换很有用:
sprintf_s(tmpBuf, 3, "%2.2hhx", *(CEKey + i));
It's a minor coding convenience, and looks cleaner than multiple casts (IMO).
这是一个次要的编码便利,看起来比多次转换(IMO)更干净。
回答by rafi wiener
another place it's handy is snprintf size check. gcc7 added size check when using snprintf so this will fail
另一个方便的地方是 snprintf 大小检查。gcc7 在使用 snprintf 时添加了大小检查,因此这将失败
char arr[4];
char x='r';
snprintf(arr,sizeof(arr),"%d",r);
so it forces you to use bigger char when using %d when formatting a char
因此,在格式化字符时使用 %d 时,它会强制您使用更大的字符
here is a commit that shows those fixes instead of increasing the char array size they changed %d to %h. this also give more accurate description
这是一个提交,显示了这些修复,而不是增加他们将 %d 更改为 %h 的字符数组大小。这也给出了更准确的描述
回答by Jonathan Leffler
The variadic arguments to printf()et al are automatically promoted using the default conversions, so any shortor charvalues are promoted to intwhen passed to the function.
printf()使用默认转换自动提升 et al的可变参数,因此任何short或char值int在传递给函数时都会被提升。
In the absence of the hor hhmodifiers, you would have to mask the values passed to get the correct behaviour reliably. With the modifiers, you no longer have to mask the values; the printf()implementation does the job properly.
在没有horhh修饰符的情况下,您必须屏蔽传递的值才能可靠地获得正确的行为。使用修饰符,您不再需要屏蔽值;在printf()实施适当做这项工作。
Specifically, for the format %hx, the code inside printf()can do something like:
具体来说,对于 format %hx,里面的代码printf()可以做如下事情:
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`!
I'm blithely assuming that shortis a 16-bit quantity; the standard does not actually guarantee that, of course.
我很高兴地假设这short是一个 16 位的数量;当然,该标准实际上并不能保证这一点。
回答by Jens Gustedt
I agree with you that it is not strictly necessary, and so by that reason alone is no good in a C library function :)
我同意你的看法,这不是绝对必要的,因此仅凭这个原因在 C 库函数中是不好的:)
It might be "nice" for the symmetry of the different flags, but it is mostly counter-productive because it hides the "conversion to int" rule.
不同标志的对称性可能“很好”,但它通常会适得其反,因为它隐藏了“转换为int”规则。

