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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了printf的h和hh修饰符的用途是什么?相关的知识,希望对你有一定的参考价值。

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

由于标准要求应用于可变函数的默认促销,不可能将charshort类型的参数(或其任何有符号/无符号变量)传递给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参数的指针。

如果参数实际上是shortunsigned short类型,那么升级到int然后转换回shortunsigned short将产生与升级到int相同的价值而不进行任何转换。因此,对于shortunsigned short类型的论证,%d%u等应该给%hd%hu等提供相同的结果(同样对于char类型和hh)。

据我所知,hhh修饰符可能有用的唯一情况是当论证通过intshort范围之外的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 shortunsigned char并使用%x转换说明符。你不能简单地使用一个简单的%x - 值可能会提升为int而不是unsigned int,然后你有未定义的行为。

你的选择要么明确地将论证转换为unsigned;或者使用%hx / %hhx

另一答案

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

在没有hhh修饰符的情况下,您必须屏蔽传递的值以可靠地获得正确的行为。使用修改器,您不再需要屏蔽值; 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位数量;当然,标准并不能保证这一点。

另一答案

我发现在将无符号字符格式化为十六进制时避免强制转换很有用:

        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数组大小。这也给出了更准确的描述

https://github.com/Mellanox/libvma/commit/b5cb1e34a04b40427d195b14763e462a0a705d23#diff-6258d0a11a435aa372068037fe161d24

以上是关于printf的h和hh修饰符的用途是什么?的主要内容,如果未能解决你的问题,请参考以下文章

面试题解五 访问修饰符的区别

访问修饰符 - 目的是啥?

什么是修饰符?它有什么作用?举例说明

volatile 修饰符的有过什么实践?

Java 和 Scala 中 C# 的访问修饰符的等价物是啥?

C++ 中数据类型修饰符的顺序