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

R..*_*R.. 55 c printf promotions variadic-functions format-specifiers

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

由于标准要求的默认促销适用于可变函数,因此不可能将类型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).

据我所知,h或者hh修饰符可能有用的唯一情况是当参数将其传递int到范围之外时,short或者unsigned short,例如

printf("%hu", 0x10000);
Run Code Online (Sandbox Code Playgroud)

但我的理解是,传递错误的类型会导致不确定的行为,所以你不能指望它打印0.

我见过的一个真实案例是这样的代码:

char c = 0xf0;
printf("%hhx", c);
Run Code Online (Sandbox Code Playgroud)

作者希望它打印,f0尽管实现具有char签名的普通类型(在这种情况下,printf("%x", c)将打印fffffff0或类似).但这种期望值得保证吗?

(注意:正在发生的是原始类型char,它被提升int并转换回unsigned char而不是char,因此改变了打印的值.但标准是否指定了这种行为,或者它是一个破坏软件的实现细节可能依赖?)

Mic*_*urr 15

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

虽然他们没有提到C99 Rationale文档中 "h"和"hh"修饰符对称性的重要性,但委员会确实提到它是为什么支持"%p"转换说明符的考虑因素fscanf()(即使对C99而言并不新鲜 - "%p"支持在C90中):

使用%p的输入指针转换被添加到C89,尽管对于fprintf的对称性显然存在风险.

在上一节中fprintf(),C99基本原理文件确实讨论了添加"hh",但仅仅引用了读者的fscanf()部分:

在H99中添加%hh和%ll长度修饰符(参见§7.19.6.2).

我知道这是一个微妙的线索,但无论如何我都在猜测,所以我想我会给出任何可能存在的争论.

此外,为了完整性,"h"修饰符符合最初的C89标准 - 可能即使由于现有的广泛使用而非严格必要,即使可能没有使用修饰符的技术要求,它也会存在. .

  • 您是否同意我的初步评估,即一致的实现可以忽略`h`和`hh`修饰符? (2认同)
  • 基于这样的文字,我觉得这是合理的实现"转换到短int或unsigned short int类型"使用优化的代码,假定它的转换价值的确是推广的结果为标准说,这是.可以想象,所述优化代码可以做出具有超出范围值的无意义的事情,因此实现至少有一个合理的声明,它*应该是*未定义的行为,并且代码违反了要求标准. (2认同)

Ada*_*erg 5

%...x模式下,所有值都被解释为无符号.因此,负数会被打印为未经签名的转换.在大多数处理器使用的2的补码算法中,有符号的负数和它的正无符号等价之间的位模式没有区别,后者由模运算定义(将字段的最大值加1加到负数,根据符合C99标准).许多软件 - 特别是最有可能使用的调试代码%x- 做出了一个默认的假设,即带符号的负值及其无符号转换的位表示是相同的,这仅适用于2的补码机器.

这个演员的机制是这样的,十进制的值表示总是暗示,可能是不准确的,数字已经以2的补码呈现,只要它没有达到不同整数表示具有不同范围的边缘条件.这甚至适用于算术表示,其中值0未用全0的二进制模式表示.

因此,在任何机器上short显示为负unsigned long十六进制的负数将f由于促销中的隐式符号扩展而被填充,其printf将被打印.的是相同的,但它是作为对字段的大小真正视觉上误导,暗示的范围内的量显著,根本不存在.

%hx 截断显示的表示以避免此填充,正如您从实际用例中得出的结论.

printf当传递到应该打印为a int的范围之外时,行为是未定义shortshort,但到目前为止最简单的实现只是通过原始向下丢弃丢弃高位,所以虽然规范不需要任何特定的行为,几乎任何理智的实现只是执行截断.不过,通常有更好的方法可以做到这一点.

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

  • 你在哪里得到关于负数字印在他们的位形式的东西?据我所知,为任何无符号格式说明符(`%x`,`%u`或`%o`)传递负值会导致未定义的行为.另外,据我所知,一致的实现可以简单地*忽略*存在任何`h`或`hh`修饰符,除了'%n`. (2认同)
  • 您的信息有点不正确.C根据*值*定义转换(隐式或强制转换),而不是位模式.无符号类型的转换由标准定义,其方式等同于模运算.转换为已签名类型是实现定义的,除非值在未经修改的情况下适合目标类型. (2认同)

caf*_*caf 5

我能想到的唯一用途是传递unsigned shortunsigned char使用%x转换说明符.你不能简单地使用裸%x- 可以提升值int而不是unsigned int,然后你有未定义的行为.

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

  • 有趣.我怀疑这是无意的.也许我应该查看标准,看看是否有任何例子如`printf("%x",1);`(你的推理需要`1U`而不是'1`). (3认同)
  • 如果`unsigned short`或`unsigned char`被提升为`int`,它仍然是正数,所以C要求表示匹配`unsigned`的表示.据我所知,签名不匹配在可变参数函数参数和没有原型的函数的参数中是有效的,只要该值为正值为有符号值即可.当然`%x`只是​​为了积极地用于`int`参数... (2认同)
  • @R.:对于一般的可变参数函数,你是对的 - 但对于 `printf` 系列的特定情况,标准给出 `unsigned int` 作为 `%x` 的参数类型,后来说 *"如果任何参数不是相应转换规范的正确类型,则行为未定义。"* - 我不认为它允许您传递 `int`。 (2认同)