printf格式为1字节有符号数

CHR*_*RIS 8 c printf

假设如下:

sizeof(char) = 1
sizeof(short) = 2
sizeof(int) = 4
sizeof(long) = 8
Run Code Online (Sandbox Code Playgroud)

对于4字节有符号数,printf2字节有符号数的格式%hd%d,对于8字节有符号数%ld,但是1字节有符号数的正确格式是什么?

ric*_*ici 7

1字节有符号数的正确格式是什么?

%hh和您选择的整数转换说明符(例如,%02hhX参见C11标准,§7.21.6.1p5:

hh

用于指定后续的d,i,o,u,x,或X转换说明适用于签名char或unsigned char参数(该参数将已经根据整数促销促进,但它的值应被转换为符号的字符或印刷之前无符号字符); ...

带括号的注释很重要.由于对可变参数函数(例如printf)的参数的整数提升,函数永远不会看到char参数.许多程序员认为这意味着没有必要使用hhh限定符.当然,你不会通过将它们排除在外来创建未定义的行为,并且大部分时间它都会起作用.

但是,char可能会签名,整数提升将保留其值,这将使其成为有符号整数.使用无符号格式(例如%02X)打印带符号的整数将显示符号扩展Fs.因此,如果要char使用无符号格式显示签名,则需要使用表示printf整数类型的原始未提升宽度hh.

如果不清楚,一个简单的例子(但有争议的)例子:

/* Read the comments thread to this post; I'll remove
   this note when I edit the outcome of the discussion into
   the answer
 */

#include <stdio.h>
int main(void) {
  char* s = "\u00d1"; /* Ñ */
  for (char* p = s; *p; ++p) printf("%02X (%02hhX)\n", *p, *p);
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

输出:

$ ./a.out
FFFFFFC3 (C3)
FFFFFF91 (91)
Run Code Online (Sandbox Code Playgroud)

在注释线程中,存在(或可能是)关于上述片段是否是未定义行为的相当大的讨论,因为X格式规范需要无符号参数,而char参数(至少在产生所呈现输出的实现上)是有符号的.我认为这个论点依赖于§7.12.6.1/ p9:"如果任何参数不是相应转换规范的正确类型,则行为是未定义的."

但是,在char(和short)整数类型的情况下,参数列表中的表达式将被提升为调用函数intunsigned int在调用函数之前.(值得注意的是,在大多数体系结构中,所有三种字符类型都将被提升为签名int;将unsigned char(或未签名的char)提升为unsigned int遗嘱只会发生在实现中sizeof(int) == 1.)

因此,在大多数体系结构中,将对一个%hx或一个%hhx格式转换的参数进行签名,并且如果不使这些格式代码的使用毫无意义,则不能使用未定义的行为.

此外,标准并没有说fprintf(和朋友)会以某种方式恢复原始表达.它所说的是" 在打印之前应将值" 转换为带符号的字符或无符号字符"(§7.21.6.1/ p5,上面引用,强调添加).

将有符号值转换为无符号值不是未定义的.它甚至没有指定或依赖于实现.它只是(概念上)"重复地加上或减去一个可以在新类型中表示的最大值,直到该值在新类型的范围内".(§6.3.1.3/ P2)

因此,有一个定义明确的过程将参数表达式转换为(可能已签名的)int参数,以及一个定义良好的过程,用于将该值转换为unsigned char.因此,我认为像上面提到的那样的程序是完全明确的.

为了确证,fprintf给定格式说明符的行为%c定义如下(§7.21.6.8/ p8),重点补充:

int参数被转换为一个unsigned char,并将得到的字符被写入.

如果要应用拟议的限制性解释,使上述程序不明确,那么我相信人们也会被迫也认为:

void f(char c) {
  printf("This is a '%c'.\n", c);
}
Run Code Online (Sandbox Code Playgroud)

也是UB.然而,我认为几乎每个C程序员都编写了类似的东西而没有考虑过它.

问题的关键部分是§7.12.6.1/ p9(以及§7.12.6.1的其他部分)中"论证"的含义.C++标准稍微精确一些; 它指定如果参数受默认参数提升的影响,"参数的值将在调用之前转换为提升类型",我将其解释为表示在考虑调用时(例如,调用fprintf),参数现在是提升的值.

我不认为C实际上是不同的,至少在意图上.它使用的措辞如"争论和hellips;被提升",并在至少一个地方" 推广后的论点".此外,在可变函数的描述(va_arg宏,§7.16.1.1)中,对参数类型的约束以括号形式注释"实际下一个参数的类型(根据默认参数促销提升)".

我会自由地同意所有这些都是(a)微妙地阅读不够精确的语言,以及(b)计算舞蹈天使.但我看不到宣称喜欢使用的标准用法任何值%cchar参数是"技术上" UB; 这会破坏UB的概念,很难相信这样的禁令是故意的,这让我相信这种解释不是故意的.(也许,应该在编辑上予以纠正.)