在 printf 期望 int 的地方给 printf 一个 char 参数是 UB 吗?

Is it UB to give a char argument to printf where printf expects a int?

这个程序导致UB的标准我理解正确吗:

#include <stdio.h>

int main(void)
{
    char a = 'A';
    printf("%c\n", a);
    return 0;
}

当它在 sizeof(int)==1 && CHAR_MIN==0?

的系统上执行时

因为如果 a 是无符号的并且具有与 int 相同的大小 (1),它将被提升为 unsigned int [1] (2),而不是到 int,因为 int 不能表示 char 的所有值。格式说明符 "%c" 需要一个 int [2] 并且在 printf() 中使用错误的符号会导致 UB [3].

相关引用自ISO/IEC9899 for C99

[1] 根据 C99 6.3.1.1:2 升级到 int:

If an int can represent all values of the original type, the value is converted to an int; otherwise, it is converted to an unsigned int. These are called the integer promotions. All other types are unchanged by the integer promotions.

[2] 格式说明符 "%c" 需要一个 int 参数,C99 7.19.6.1:8 c:

If no l length modifier is present, the int argument is converted to an unsigned char, and the resulting character is written.

[3] 根据 C99 7.19.6.1:9:

fprintf() (3) 中使用错误的类型,包括错误的符号,导致 UB

... If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined.

对于 va_arg 宏,但对于 printf() 给出了具有不同符号的相同类型的例外,并且不要求 printf() 使用 va_arg (4 ).

脚注: (标记为 (n))

  1. 这意味着 INT_MAX==SCHAR_MAX,因为 char 没有填充。

  2. 另见这个问题:Is unsigned char always promoted to int?

  3. 同样的规则适用于printf(),见C99 7.19.6.3:2

  4. 另见这个问题:Does printf("%x",1) invoke undefined behavior?

Do i understand the standard correct that this program cause UB:

#include <stdio.h>

int main(void)
{
  char a='A';
  printf("%c\n",a);
  return 0;
}

When it is executed on a system where sizeof(int)==1 && CHAR_MIN==0?

这将是对标准的合理解释。但是,如果具有这种类型特征组合的实现是为真正使用而产生的,我完全相信它会为 %c 指令提供适当的支持——作为扩展,如果有人想解释就那样然后示例程序将具有关于该实现的 well-defined 行为,无论 C 标准是否也被解释为定义该行为。我想我认为 quality-of-implementation 问题是在“真正使用”中汇总的。

一个程序可以有或没有未定义的行为取决于实现的特征

例如执行

的程序
int x = 32767;
x++;

(并且在其他方​​面定义明确)在 INT_MAX > 32767 的实现中具有明确定义的行为,否则为未定义的行为。

你的程序:

#include <stdio.h>

int main(void)
{
  char a='A';
  printf("%c\n",a);
  return 0;
}

对于任何使用 INT_MAX >= CHAR_MAX 的托管实现都有明确定义的行为。在任何此类实现中,'A' 的值被提升为 int,这是 %c 所期望的。

如果 INT_MAX < CHAR_MAX(这意味着普通 char 是无符号的并且 CHAR_BIT >= 16),a 的值被提升为 unsigned int。 N1570 7.21.6.1p9:

If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined.

暗示这有未定义的行为。

在实践中,(a) 这样的实现很少见,可能不存在(我听说过的唯一现有的 C 实现 CHAR_BIT > 8DSPs 并且这样的实现很可能是独立的),以及 (b) 任何此类实现都可能旨在妥善处理此类情况。

TL;DR 没有 UB(无论如何在我的解释中)。

6.2.5 types
6. For each of the signed integer types, there is a corresponding (but different) unsigned integer type (designated with the keyword unsigned) that uses the same amount of storage (including sign information) and has the same alignment requirements.
9. The range of nonnegative values of a signed integer type is a subrange of the corresponding unsigned integer type, and the representation of the same value in each type is the same 41)
41) The same representation and alignment requirements are meant to imply interchangeability as arguments to functions, return values from functions, and members of unions.

此外

7.16.1.1 The va_arg macro
2 The va_arg macro expands to an expression that has the specified type and the value of the next argument in the call. [...] If there is no actual next argument, or if type is not compatible with the type of the actual next argument (as promoted according to the default argument promotions), the behavior is undefined, except for the following cases:

  • one type is a signed integer type, the other type is the corresponding unsigned integer type, and the value is representable in both types;

7.21.6.8 The vfprintf function
288) [...] functions vfprintf, vfscanf, vprintf, vscanf, vsnprintf, vsprintf, and vsscanf invoke the va_arg macro [...]

因此,只要值在范围内,无符号类型就不是“相应(有符号)转换规范的不正确类型”是有道理的.

主要编译器不会警告 signed/unsigned 格式规范不匹配这一事实证实了这一点,即使它们确实警告其他不匹配,即使相应的类型在给定平台上具有相同的表示形式(例如 longlong long).