无符号字符的格式说明符
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
提升为int
或unsigned
由于可变参数的标准提升,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 在打印char
、unsigned char
、signed char
... int
、unsigned
时,考虑什么是定义并从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
范围内)并且值在int
、short
、signed char
、B)“…u”、“…o”、“ ...x”, “...X” 带有unsigned
参数(或int
范围内的unsigned
参数),任何值。我认为值超出范围的 A) 的情况取决于实现。【参考方案3】:
unsigned char
和unsigned short
始终可以安全地使用%u
打印。默认参数提升将它们转换为int
或unsigned int
。如果将它们提升为后者,则一切正常(格式说明符和传递的类型匹配),否则适用 C11 (n1570) 6.5.2.2 p6,第一个项目符号:
一种提升类型是有符号整数类型,另一种提升类型是对应的无符号整数类型,并且值可以在两种类型中表示;
标准非常明确,默认参数提升适用于printf
的可变参数,例如再次提到(大部分没用的)h
和 hh
长度修饰符(同上。7.21.6.1 p7,emph。我的):
hh
-- 指定以下d
、i
、o
、u
、x
或X
转换说明符适用于signed char
或unsigned char
参数( 该参数将根据整数提升进行提升,但其值应在打印前转换为signed char
或unsigned char
); [...]
【讨论】:
是的,h
和 hh
对于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 位可寻址单元时导致编译错误而不是错误行为时使用。以上是关于无符号字符的格式说明符的主要内容,如果未能解决你的问题,请参考以下文章