printf("%x",1)是否会调用未定义的行为?

R..*_*R.. 31 c standards printf variadic-functions language-lawyer

根据C标准(6.5.2.2第6段)

如果表示被调用函数的表达式具有不包含原型的类型,则对每个参数执行整数提升,并将具有float类型的参数提升为double.这些被称为默认参数促销.如果参数的数量不等于参数的数量,则行为未定义.如果函数是使用包含原型的类型定义的,并且原型以省略号(,...)结尾,或者促销后的参数类型与参数类型不兼容,则行为未定义.如果函数是使用不包含原型的类型定义的,并且促销后的参数类型与促销后的参数类型不兼容,则行为未定义,但以下情况除外:

  • 一个提升类型是有符号整数类型,另一个提升类型是相应的无符号整数类型,并且该值可在两种类型中表示;
  • 这两种类型都是指向字符类型或空格的限定或不合格版本的指针.

因此,一般来说,只要传递的值适合两种类型,传递int到需要unsigned int(或反之亦然)的可变函数是没有错的.但是,printf读取规范(7.19.6.1第9段):

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

签名/未签名不匹配也不例外.

这是否意味着printf("%x", 1)调用未定义的行为?

caf*_*caf 15

我认为它在技术上是未定义的,因为"正确类型" %x被指定为unsigned int- 并且正如您所指出的,这里没有签名/无符号不匹配的例外.

规则适用printf于更具体的情况,因此会覆盖一般情况的规则(对于通用的特定覆盖的另一个示例,通常允许传递NULL给期望const char *参数的函数,但是传递NULL给它的未定义行为strlen()) .

我说"技术上",因为我认为,鉴于标准中的其他限制,实施需要故意反常导致此案件出现问题.

  • @Chris Lutz:这种解释对标准的*意图*没有任何意义,它只是提出了关于标准的实际规范性措辞的效果的一系列论据. (2认同)
  • @Karl:实际上它是将`NULL`传递给`strlen`的行为,这是未定义的.这是因为标准库函数是由它们的行为正式定义的,而不是由C实现定义的.请参见7.4.1/1:"如果函数的参数具有无效值(例如函数域外的值,或程序地址空间外的指针,或空指针,或指向当相应参数不是常量限制时的非可修改存储)或具有可变数量参数的函数不期望的类型(提升之后),行为是未定义的. (2认同)

Jon*_*pan 8

不,因为%x格式化unsigned int,并且常量表达式1的类型是int,而它的值可表示为unsigned int.操作不是UB.

  • 默认参数提升通常不会导致`int`参数转换为`unsigned int`,因此1必须表示为`unsigned int`的事实是无关紧要的.如果`printf`保证使用`va_arg`宏,那么你会期望7.12.1.1中的异常成立,但这不是必需的.默认参数提升后的参数类型仍然是`int`,而不是`unsigned int`和(正如其他人所说的那样)7.19.6.1明确指出:"如果任何参数不是相应转换规范的正确类型,那么行为未定义." (8认同)
  • 6.5.2.2定义了可变函数的一般行为,但7.19.6.1转过来说,除非类型与格式说明符匹配,否则行为是未定义的.如果这是意图,似乎应该省略或修复此段落以提及签名/未签名不匹配的例外. (4认同)
  • FWIW我的观点是这个代码*应该是合法的,但标准没有定义它,我认为标准有缺陷.通过查看`l`修饰符规范可以看出明显的不一致,该规范清楚地定义了`"%lx"`可能对应于参数`1L`和`-1L`! (3认同)
  • 它格式化两者.:) variadic参数规范覆盖了printf规范,前者允许使用int,其中unsigned int是预期的. (2认同)

Jen*_*edt 5

这是未定义的行为,与将整数类型的指针重新解释为相反符号的互补类型的原因相同。不幸的是,这在两个方向上都是不允许的,因为一个方向上的有效表示可能是另一个方向上的陷阱实现。

我看到从有符号到无符号的重新解释可能存在陷阱表示的唯一原因是符号表示的这种变态情况,其中无符号类型只是屏蔽了符号位。不幸的是,从标准 6.2.6.2 开始,这种情况是允许的。在这样的架构上,有符号类型的所有负值都可能是无符号类型的陷阱表示。

在您的示例中,这甚至更奇怪,因为1不允许使用无符号类型的陷阱表示。因此,要使其成为“真实”的示例,您必须使用-1.

我认为人们仍然没有为任何架构编写具有这些功能的 C 编译器,因此如果标准的新版本可以废除这种令人讨厌的情况,那么生活肯定会变得更加容易。

  • 我不相信标准允许这样做。据我所知,可以用该类型的有符号和无符号版本表示的值需要具有相同的表示形式。请注意,“类型表示”中的别名规则明确允许作为符号不匹配类型进行访问。 (3认同)