在第7.19.6.1节 C99标准第8段中:
c
如果不存在l
长度修饰符,则会转换int
参数到了unsigned char
,并且会写出结果字符。
在第7.19.6.1节 C99标准第9段中:
如果任何参数不是相应转换规范的正确类型,则行为未定义。
fprintf
函数是否需要int
参数?例如,将unsigned int
结果传递给未定义的行为:
unsigned int foo = 42;
fprintf(fp, "%c\n", foo); /* undefined behavior? */
这令我担心,因为实施可能已将char
定义为与unsigned char
具有相同的行为(第6.2.5节第15段)。
对于这些情况,整数提升可能会要求char
为promoted to unsigned int
on some implementations。因此,将以下代码留给那些实现的未定义行为风险:
char bar = 'B';
fprintf(fp, "%c\n", bar); /* possible undefined behavior? */
int
变量和文字int
常量是使用fprintf
说明符将值传递给%c
的唯一安全方法吗?答案 0 :(得分:5)
%c
的 fprintf
转换规范需要int
个参数。在默认参数促销之后,该值必须为int
类型。
unsigned int foo = 42;
fprintf(fp, "%c\n", foo);
未定义的行为:foo
必须是int
。
char bar = 'B';
fprintf(fp, "%c\n", bar);
未定义的行为:bar
被提升(默认参数提升)到int
,因为fprintf
是一个可变函数。
编辑:公平地说,仍有一些非常罕见的实现,它们可能是未定义的行为。例如,如果char
是一个无符号类型,且char
中没有所有int
值可表示(如this implementation中所示),则默认参数提升为{{1} }}
答案 1 :(得分:3)
是的,printf
"%c"
需要int
参数 - 或多或少。
如果参数的类型比int
窄,那么它将提升。在大多数情况下,促销是int
,具有明确定义的行为。在极少数情况下,普通char
是未签名的sizeof (int) == 1
(暗示CHAR_BIT >= 16
),char
参数会提升为unsigned int
,其中可以导致未定义的行为。
字符常量已经是int
类型,因此即使在异国情调的系统上也可以很好地定义printf("%c", 'x')
。 (偏离主题:在C ++中,字符常量的类型为char
。)
此:
unsigned int foo = 42;
fprintf(fp, "%c\n", foo);
严格来说有未定义的行为。 N1570 7.1.4p1说:
如果函数的参数有...类型(促销后)没有 期望由具有可变数量的参数的函数,行为 未定义。
并且fprintf
电话显然违背了这一点。 (感谢ouah指出这一点。)
另一方面,6.2.5p6说:
对于每个有符号整数类型,都有一个对应的(但是 另外)无符号整数类型(用关键字指定) 未签名)使用相同数量的存储空间(包括签名信息)并具有相同的对齐要求。
和6.2.5p9说:
有符号整数类型的非负值范围是子范围 相应的无符号整数类型,以及表示形式 每种类型的相同值都是相同的。
脚注:
相同的表示和对齐要求意味着暗示 可互换性作为函数的参数,返回值 职能和工会成员。
脚注表示类型int
和unsigned int
的函数参数是可互换的,只要该值在两种类型的可表示范围内即可。 (对于典型的32位系统,这意味着该值必须在0到2 31 -1范围内; int
值来自-2 31 从-1 31 到2 32 -1的{-1}和unsigned int
值超出了另一种类型的范围,并且不可互换。)< / p>
但是C标准中的脚注是非规范。它们通常旨在澄清规范性文本中规定的要求,而不是强加新的要求。但是这里的规范性文本仅仅表明相应的有符号和无符号类型具有相同的表示,这不会必然暗示它们以与函数参数相同的方式传递。原则上,编译器可以忽略该脚注,例如,在不同的寄存器中传递int
和unsigned int
个参数,使fprintf(fp, "%c\n", foo);
未定义。
但实际上,没有理由让实现这种游戏,你可以依靠fprintf(fp, "%c\n", foo);
按预期工作。我从未见过或听说过一个不起作用的实现。
就个人而言,我宁愿不依赖于此。如果我正在编写该代码,我会通过演员添加一个显式转换,这样就不会出现这些问题:
unsigned int foo = 42;
fprintf(fp, "%c\n", (int)foo);
或者我首先让foo
成为int
。