无符号字符的格式说明符

Posted

技术标签:

【中文标题】无符号字符的格式说明符【英文标题】:Format specifier for unsigned char 【发布时间】:2014-12-18 13:10:05 【问题描述】:

说我要打印unsigned char

unsigned char x = 12;

这是正确的。这个:

printf("%d",x);

或者这个:

printf("%u",x);

?

事情在其他地方,所以我遇到了这样的讨论:

-即使将 ch 更改为 unsigned char,C 标准也没有定义代码的行为。这是因为 unsigned char 被提升为 int(在正常的 C 实现中),因此将 int 传递给 printf 的说明符 %u。但是,%u 需要一个 unsigned int,所以类型不匹配,并且 C 标准没有定义行为

-您的评论不正确。 C11 标准规定转换说明符必须与函数参数本身的类型相同,而不是提升类型。这一点在 hh 长度修饰符的描述中也有特别说明:“参数将根据整数提升进行提升,但其值应在打印前转换为有符号字符或无符号字符”

那么哪个是正确的?有可靠消息来源说这件事吗? (从这个意义上说,我们还应该使用 %d 打印 unsigned short int,因为它可以提升为 int?)。

【问题讨论】:

【参考方案1】:

正确的是*:

printf("%d",x);

这是因为 默认参数提升 因为printf() 是可变参数函数。这意味着unsigned char 的值始终提升为int

来自 N1570(C11 草案)6.5.2.2/6函数调用(强调我的未来):

如果表示被调用函数的表达式有一个类型 不包括原型,整数促销在 每个参数和类型为 float 的参数都被提升为 double。这些被称为默认参数提升

6.5.2.2/7 子条款告诉:

函数原型声明器中的省略号表示 参数类型转换在最后一个声明的参数之后停止。 默认参数提升是在尾随参数上执行的

这些整数提升在6.3.1.1/2 中定义布尔值、字符和整数

如果int 可以表示原始类型的所有值(受限制 通过宽度,对于位域),值被转换为int; 否则,它将转换为unsigned int。这些被称为 整数提升.58)所有其他类型都被整数不变 促销活动。

这句话回答了你对unsigned short 的第二个问题(见下面的评论)。


* 超过 8 位 unsigned char 除外(例如,它可能占用 16 位),请参阅 @chux 的 answer。

【讨论】:

【参考方案2】:

unsigned char x = 12 的正确格式说明符取决于多种因素:

如果INT_MAX >= UCHAR_MAX(通常是这种情况),请使用"%d"。在这种情况下,unsigned char 被提升为 int

printf("%d",x);

否则使用"%u"(或"%x""%o")。在这种情况下,unsigned char 被提升为 unsigned

printf("%u",x);

最新的编译器支持 "hh" 长度修饰符,它弥补了这种歧义。应该将x 提升为intunsigned 由于可变参数的标准提升,printf() 在打印前将其转换为unsigned char

printf("%hhu",x);

如果处理没有"hh" 的旧编译器或寻求高度可移植的代码,请使用显式转换

printf("%u", (unsigned) x);

同样的问题/答案适用于unsigned short,除了INT_MAX >= USHRT_MAX,使用"h" 而不是"hh"

【讨论】:

我刚刚得出了同样的结论。将d 与 unsigned char 一起使用会导致 ub,请参阅讨论(请参阅我的最新评论):***.com/a/36350763/4082723 我想听听您的意见。 @2501 在打印charunsigned charsigned char ... intunsigned 时,考虑什么是定义并从2个规范开始: ( 1) 任何低于int/unsigned 的整数都将被提升为int/unsigned。 2) ... 参数:“...一种提升类型是有符号整数类型,另一种提升类型是对应的无符号整数类型,并且值在两种类型中都可以表示”(C11 §6.5.2.2)。所以在考虑”hhd”, “hhu”, … “d”, “u”是否定义printf()时,原始类型无关,只有提升的类型和值是值得关注的。 @2501 ... 那么我们只需要考虑这些情况来查看行为是否定义: A) 说明符“d”、“hd”、“hhd”与一个int 参数(或unsigned 参数在int 范围内)并且值在intshortsigned char、B)“…u”、“…o”、“ ...x”, “...X” 带有unsigned 参数(或int 范围内的unsigned 参数),任何值。我认为值超出范围的 A) 的情况取决于实现。【参考方案3】:

unsigned charunsigned short 始终可以安全地使用%u 打印。默认参数提升将它们转换为intunsigned int。如果将它们提升为后者,则一切正常(格式说明符和传递的类型匹配),否则适用 C11 (n1570) 6.5.2.2 p6,第一个项目符号:

一种提升类型是有符号整数类型,另一种提升类型是对应的无符号整数类型,并且值可以在两种类型中表示;

标准非常明确,默认参数提升适用于printf 的可变参数,例如再次提到(大部分没用的)hhh 长度修饰符(同上。7.21.6.1 p7,emph。我的):

hh -- 指定以下 diouxX 转换说明符适用于 signed charunsigned char 参数( 该参数将根据整数提升进行提升,但其值应在打印前转换为signed charunsigned char); [...]

【讨论】:

是的,hhh 对于printf()基本没用,但它们主要是为了对称而添加的,因为它们对 scanf() 没有用处。跨度> 我认为这可以解释。我对此感到疑惑并遇到了这个。【参考方案4】:

对于跨平台开发,我通常使用inttypes.h绕过推广问题

http://pubs.opengroup.org/onlinepubs/009695399/basedefs/inttypes.h.html

此标头(在 C99 标准中)定义了基本类型的所有 printf 类型。因此,如果你想要一个 uint8_t(我强烈建议使用它而不是 unsigned char 的语法)我会使用

#include <inttypes.h>
#include <stdint.h>
uint8_t x;
printf("%" PRIu8 "\n",x);

【讨论】:

跨平台开发,unsigned char的范围可能与uint8_t的范围不匹配。兼容的 C11 编译器不必实现 uint8_t - “这些类型是可选的。” —— @chux uint8_t 会在平台没有 8 位可寻址单元时导致编译错误而不是错误行为时使用。

以上是关于无符号字符的格式说明符的主要内容,如果未能解决你的问题,请参考以下文章

Python字符串格式化符号及转义字符含义

Python字符串格式化符号及转义字符含义

带有无符号字符的文本格式

将无符号字符读入字符串

字符串格式符

printf格式字符