printf的h和hh修饰符的用途是什么?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了printf的h和hh修饰符的用途是什么?相关的知识,希望对你有一定的参考价值。
除了%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);
但我的理解是,传递错误的类型会导致不确定的行为,所以你不能指望它打印0。
我见过的一个真实案例是这样的代码:
char c = 0xf0;
printf("%hhx", c);
作者希望它打印f0
,尽管实施有一个简单的char
类型签署(在这种情况下,printf("%x", c)
将打印fffffff0
或类似)。但这种期望值得保证吗?
(注意:正在发生的是原始类型是char
,它被提升为int
并转换回unsigned char
而不是char
,从而改变了打印的值。但标准是否指定了这种行为,或者它是一个实现细节破碎的软件可能依赖?)
一个可能的原因:在格式化的输入函数中使用那些修饰符的对称性?我知道这不是绝对必要的,但是可能有价值吗?
虽然他们没有提到the C99 Rationale document中“h”和“hh”修饰符对称性的重要性,但委员会确实提到它为什么fscanf()
支持“%p”转换说明符的考虑(尽管那不是'对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
的行为在int
范围之外传递short
时应该被打印为short
,但是最简单的实现只是通过原始的向下丢弃丢弃高位,所以虽然规范不需要任何特定的行为,几乎任何理智的实现都只是执行截断。不过,通常有更好的方法可以做到这一点。
如果printf没有填充值或显示有符号值的无符号表示,则%h
不是很有用。
我能想到的唯一用途是传递unsigned short
或unsigned char
并使用%x
转换说明符。你不能简单地使用一个简单的%x
- 值可能会提升为int
而不是unsigned int
,然后你有未定义的行为。
你的选择要么明确地将论证转换为unsigned
;或者使用%hx
/ %hhx
。
使用默认转换自动提升printf()
等人的可变参数,因此任何short
或char
值在传递给函数时都会被提升为int
。
在没有h
或hh
修饰符的情况下,您必须屏蔽传递的值以可靠地获得正确的行为。使用修改器,您不再需要屏蔽值; printf()
实施正确地完成了工作。
具体来说,对于格式%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`!
我很乐意假设short
是16位数量;当然,标准并不能保证这一点。
我发现在将无符号字符格式化为十六进制时避免强制转换很有用:
sprintf_s(tmpBuf, 3, "%2.2hhx", *(CEKey + i));
这是一个简单的编码方便,看起来比多个演员(IMO)更干净。
我同意你的看法,这并不是绝对必要的,所以单凭这个理由对C库函数来说并不好:)
它对于不同旗帜的对称性可能是“好的”,但它主要是适得其反,因为它隐藏了“转换为int
”规则。
另一个方便的是snprintf尺寸检查。 gcc7在使用snprintf时添加了大小检查,因此这将失败
char arr[4];
char x='r';
snprintf(arr,sizeof(arr),"%d",r);
因此在格式化char时使用%d时会强制使用更大的char
这是一个提交,显示这些修复,而不是增加他们将%d更改为%h的char数组大小。这也给出了更准确的描述
以上是关于printf的h和hh修饰符的用途是什么?的主要内容,如果未能解决你的问题,请参考以下文章