11

我最近读到

unsigned char x=1;
printf("%u",x);

调用未定义的行为,因为由于格式说明符 %u,printf 需要一个无符号整数。但我仍然想了解这个例子中发生了什么。

我认为积分提升规则适用于由 .printf("%u",x) 表示的表达式和值x

A.6.1 积分促销

一个字符、一个短整数或一个整数位域,无论是否有符号,或枚举类型的对象,都可以在可以使用整数的表达式中使用。如果一个 int 可以表示原始类型的所有值,则将该值转换为 int;否则,该值将转换为 unsigned int。这个过程称为积分提升。

这里的“可以使用”是什么意思?这是否意味着“语法正确”或“已定义行为”?

在这个例子中 x 是如何提升的?我读过它被提升为int,但如果printf("%u", (int x))仍然是未定义的行为,那么我真的不明白为什么......

4

3 回答 3

4

由于printf使用可变参数列表,因此整数提升应用于其整数参数。在任何正常的 C 实现中,整数提升将 an 转换unsigned char为 an int。然后你正在格式化一个intWith 说明符 for unsigned int,所以行为是未定义的。

说可以在可以使用整数的地方使用字符与您的语句具有 C 标准未定义的行为这一事实之间没有冲突。尽管您可以使用字符代替整数,但有关打印内容的规则%u仍然适用。如果使用字符产生适合说明符的整数,则定义行为。如果使用字符导致整数不适合说明符,则行为未由 C 标准定义。

Stack Overflow 上其他地方的讨论得出的结论是,一个奇异的 C 实现在理论上可能符合 C 标准,同时具有与char类型一样宽的类型(普通、有符号和无符号)int。在这样的实现中,并且int不能代表 an 的所有值unsigned char,因此unsigned char必须将 an 提升为 an unsigned int。但是,这样的实现会很奇怪而且很麻烦(尤其是在处理 EOF 时),在实践中您可能会忽略它。

于 2013-10-18T09:49:00.637 回答
1

如果您的平台的intcan 代表所有值而不是unsigned charcan,则促销是 to int,否则 to unsigned int。所以这取决于你的平台。

至于“为什么”,那是因为您x作为可变参数传递,而可变参数的规则表明发生标准提升(大概是为了简化实现)。

于 2013-10-18T08:25:31.567 回答
1

由于printf使用变量参数列表,它将通过va_arg. C++ 指的是va_arg规则的 C 标准。C99 标准规定如下:

va_arg宏扩展为具有指定类型和调用中下一个参数的值的表达式。该参数ap应已由va_startor宏初始化(没有对相同va_copy的宏的介入调用)。宏的每次调用都会修改,以便依次返回连续参数的值。参数类型应该是一个指定的类型名称,这样指向具有指定类型的对象的指针的类型可以通过简单地通过对type加一个后缀来获得。如果没有实际的下一个参数,或者类型不兼容va_endapva_argap*实际下一个参数的类型(根据默认参数提升),行为未定义,但以下情况除外:

  • 一种类型是有符号整数类型,另一种类型是对应的无符号整数类型,并且值在两种类型中都是可表示的;
  • 一种类型是指向 void 的指针,另一种是指向字符类型的指针。

显然,在确定实际类型和预期类型是否匹配时会考虑整数提升。第一个要点涵盖了有符号与无符号的不匹配。

因为x = 1肯定是一个可以由 表示的值unsigned int,并且提升unsigned char生成signed int(if INT_MAX >= UCHAR_MAX) 或unsigned int(if INT_MAX < UCHAR_MAX),所以这是完全合法的。

于 2015-09-11T18:48:46.980 回答