除了 %hn%hhn (在哪里 h 或者 hh 指定大小 指向 对象),什么是 hhh 修饰符 printf 格式指定符?

由于默认促销措施,标准需要用于变异功能,因此不可能通过类型的参数 char 或者 short (或其任何签名/未签名的变体) printf.

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

指定以下d,i,o,u,x或x转换指定的特定版本适用于简短的int或unsigned简短int参数(该论点将根据整数促销促进,但其价值应转换为简短的int int int int或打印前未签名的简短int);或以下n转换规格适用于指向简短的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, ,因此更改打印的值。但是标准是否指定了此行为,还是破碎的软件可能依赖的实现细节?)

有帮助吗?

解决方案

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

尽管他们没有提及对称性对“ H”和“ HH”修饰符的重要性 C99理由文件, ,委员会确实提到了这一点,以考虑为什么支持“%p”转换指定符 fscanf() (即使对于C99-“%p”支持并不是什么新鲜事物),在C90中):

对于与FPRINTF的对称性,尽管它显然有风险,但使用%P的输入指针转换添加到C89中。

在有关的部分 fprintf(), ,C99理由文件确实讨论了“ HH”,但仅将读者引用到 fscanf() 部分:

在C99中添加了%HH和%LL长度修饰符(请参阅第7.19.6.2节)。

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

另外,为了完整性,“ H”修饰符是在原始C89标准中 - 大概是在存在的情况下,即使不是严格必要的,即使存在广泛的现有用途,也可能没有使用修饰符的技术要求。

其他提示

%...x 模式,所有值都被解释为未签名。因此,负数被打印为其未签名的转换。在大多数处理器使用的2的补体算术中,签名的负数及其正面的无符号等价物之间的位模式没有差异,这是由模量算术定义的(将字段的最大值加上一个负数添加到负数,根据到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 有一个裸露的争论。

variadic论点 printf() Et al会使用默认转换自动促进 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)更干净。

它方便的另一个位置是SNPRINTF尺寸检查。 GCC7使用SNPRINTF时添加了尺寸检查,以便这将失败

char arr[4];
char x='r';
snprintf(arr,sizeof(arr),"%d",r);

因此,它迫使您在格式化char时使用%d时使用更大的字符

这是一个提交的提交,显示这些修复程序而不是增加Char数组的大小,而将其更改为%H。这也提供了更准确的描述

https://github.com/mellanox/libvma/commit/B5CB1E34A04B40427D195B14763E462A0A705D23#DIFF-6258D00A11A11A11A1A437206803737FEE

我同意您的观点,这不是严格必要的,因此仅出于这个原因,在C库函数中并不是一件好事:)

对于不同旗帜的对称性,它可能是“不错的” int“ 规则。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top