2

在C99 标准的第 7.19.6.1 节第 8 段中:

c如果不存在l长度修饰符,则将int参数转换为 unsigned char,并写入结果字符。

在C99 标准的第 7.19.6.1 节第 9 段中:

如果任何参数不是相应转换规范的正确类型,则行为未定义。

  • fprintf函数是否需要int参数?

例如,会unsigned int以未定义的行为传递结果:

unsigned int foo = 42;

fprintf(fp, "%c\n", foo); /* undefined behavior? */

这让我很担心,因为实现可以定义为具有与(第 6.2.5 节第 15 节)char相同的行为。unsigned char

对于这些情况,整数提升可能会要求在某些实现上char提升unsigned int。因此,让以下代码冒着在这些实现上出现未定义行为的风险:

char bar = 'B';

fprintf(fp, "%c\n", bar); /* possible undefined behavior? */
  • int变量和文字常量是使用说明符int传递值的唯一安全方法吗?fprintf%c
4

2 回答 2

5

%c的转换规范fprintf需要一个int参数。该值必须是默认参数 Promotionsint之后的类型。

unsigned int foo = 42;
fprintf(fp, "%c\n", foo);

未定义的行为:foo必须是int.

char bar = 'B';
fprintf(fp, "%c\n", bar);

不是未定义的行为:bar被提升(默认参数提升)intfprintf可变参数函数。

编辑:公平地说,仍然有一些非常罕见的实现可能是未定义的行为。例如,如果char是一个无符号类型,并非所有char值都可以在 an 中表示int(如在此实现中),则默认参数提升为unsigned int.

于 2013-07-26T14:14:50.010 回答
3

是的,printfwith"%c"需要一个int论点——或多或少。

如果参数的类型比 窄int,那么它将被提升。在大多数情况下,促销是 to int,具有明确定义的行为。在极少数情况下,plainchar是无符号的并且sizeof (int) == 1(这意味着CHAR_BIT >= 16),一个char参数被提升为unsigned int,这可能导致未定义的行为。

字符常量已经是 type int,因此printf("%c", 'x')即使在外来系统上也有很好的定义。(题外话:在 C++ 中,字符常量的类型是char。)

这:

unsigned int foo = 42;
fprintf(fp, "%c\n", foo);

严格来说有未定义的行为。 N1570 7.1.4p1 说:

如果函数的参数具有......具有可变数量参数的函数不期望的类型(提升后),则行为未定义。

fprintf电话显然与此相冲突。(感谢 ouah 指出这一点。)

另一方面,6.2.5p6 说:

对于每个有符号整数类型,都有一个对应的(但不同的)无符号整数类型(用关键字 unsigned指定),它使用相同的存储量(包括符号信息)并具有相同的对齐要求。

和 6.2.5p9 说:

有符号整数类型的非负值范围是对应无符号整数类型的子范围,相同值在每种类型中的表示是相同的。

带脚注:

相同的表示和对齐要求意味着作为函数的参数、函数的返回值和联合成员的可互换性。

脚注说函数参数类型intunsigned int是可互换的,只要值在两种类型的可表示范围内。(对于典型的 32 位系统,这意味着该值必须在 0 到 2 31 -1 的范围内;int从 -2 31到 -1 的unsigned int值,以及从 2 31到 2 32 -1 的值超出范围其他类型,不可互换。)

但是 C 标准中的脚注是非规范性的。它们通常旨在澄清规范性文本中陈述的要求,而不是强加新的要求。但是这里的规范文本只是说明相应的有符号和无符号类型具有相同的表示形式,这并不一定意味着它们以与函数参数相同的方式传递。原则上,编译器可以忽略该脚注,例如,在不同的寄存器中传递int和参数,从而导致未定义。unsigned intfprintf(fp, "%c\n", foo);

但在实践中,实现没有理由玩这种游戏,您可以依靠它fprintf(fp, "%c\n", foo);按预期工作。我从未见过或听说过它不起作用的实现。

就个人而言,我不喜欢依赖它。如果我正在编写该代码,我会通过强制转换添加显式转换,这样一开始就不会出现这些问题:

unsigned int foo = 42;
fprintf(fp, "%c\n", (int)foo);

或者我会首先做foo一个int

于 2013-07-26T14:55:37.393 回答