what is the correct format for a 1 byte signed number?
%hh和您选择的整数转换说明符(例如,?hhX.请参阅C11标准,§7.21.6.1p5:
hh
Specifies that a following d, i, o, u, x, or X conversion specifier applies to a signed char or unsigned char argument (the argument will have been promoted according to the integer promotions, but its value shall be converted to signed char or unsigned char before printing);…
带括号的注释很重要.由于对可变参数函数(例如printf)的参数的整数提升,函数永远不会看到char参数.许多程序员认为这意味着不必使用h和hh限定符.当然,你不会通过将它们排除在外来创建未定义的行为,并且大部分时间它都会起作用.
但是,char可能会被签名,整数提升将保留其值,这将使其成为有符号整数.使用无符号格式(例如?X)打印带符号的整数将显示符号扩展的Fs.因此,如果要使用无符号格式显示带符号的字符,则需要告诉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
int main(void) {
char* s = "\u00d1"; /* Ñ */
for (char* p = s; *p; ++p) printf("%02X (%02hhX)\n", *p, *p);
return 0;
}
输出:
$./a.out
FFFFFFC3 (C3)
FFFFFF91 (91)
在注释线程中,存在(或者可能是)关于上述片段是否是未定义行为的相当大的讨论,因为X格式规范需要无符号参数,而char参数(至少在生成呈现输出的实现上)签名.我认为这个论点依赖于§7.12.6.1/ p9:“如果任何参数不是相应转换规范的正确类型,则行为是未定义的.”
但是,在char(和short)整数类型的情况下,在调用函数之前,参数列表中的表达式将提升为int或unsigned int. (值得注意的是,在大多数体系结构中,所有三种字符类型都将被提升为signed int;将unsigned char(或unsigned char)提升为unsigned int只会在sizeof(int)== 1的实现上发生.)
因此,在大多数体系结构中,%hx或%hhx格式转换的参数将被签名,并且如果不使这些格式代码的使用毫无意义,则不能使用未定义的行为.
此外,标准并没有说fprintf(和朋友)会以某种方式恢复原始表达.它所说的是“在打印之前应将值”转换为带符号的字符或无符号字符“(§7.21.6.1/ p5,上面引用,强调添加).
将有符号值转换为无符号值不是未定义的.它甚至没有指定或依赖于实现.它只是(概念上)“重复地加上或减去一个可以在新类型中表示的最大值,直到该值在新类型的范围内”. (§6.3.1.3/ P2)
因此,有一个定义明确的过程将参数表达式转换为(可能已签名的)int参数,以及一个定义良好的过程,用于将该值转换为unsigned char.因此,我认为像上面提到的那样的程序是完全明确的.
为了确证,给定格式说明符%c的fprintf的行为定义如下(§7.21.6.8/ p8),重点补充:
the int argument is converted to an unsigned char, and the resulting character is written.
如果要应用拟议的限制性解释,使上述程序不明确,那么我相信人们也会被迫也认为:
void f(char c) {
printf("This is a '%c'.\n", c);
}
也是UB.然而,我认为几乎每个C程序员都写了类似的东西而没有考虑过它.
问题的关键部分是§7.12.6.1/ p9(以及§7.12.6.1的其他部分)中“论证”的含义. C标准稍微精确一些;它指定如果参数受默认参数提升的影响,“参数的值将在调用之前转换为提升类型”,我将其解释为表示在考虑调用时(例如,调用fprintf),参数现在是提升的值.
我不认为C实际上是不同的,至少在意图上.它使用的措辞如“争论和解决方案;被提升”,并在至少一个地方“推广后的论点”.此外,在变量函数的描述中(va_arg宏,§7.16.1.1),对参数类型的约束以括号方式注释“实际下一个参数的类型(根据默认参数提升而提升)”.
我会自由地同意所有这些都是(a)微妙地阅读不够精确的语言,以及(b)计算舞蹈天使.但是我没有看到任何声明像使用带有char参数的%c这样的标准用法是“技术上”UB的价值;这证明了UB的概念,很难相信这样的禁令是故意的,这让我相信这种解释不是故意的. (也许,应该在编辑上予以纠正.)