35

根据C标准(6.5.2.2第6段)

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

  • 一种提升类型是有符号整数类型,另一种提升类型是对应的无符号整数类型,并且值可以在两种类型中表示;
  • 这两种类型都是指向字符类型或 void 的限定或非限定版本的指针。

因此,一般来说,只要传递的值适合这两种类型,将 an 传递int给期望 an unsigned int(反之亦然)的可变参数函数没有任何问题。但是,printf阅读规范(7.19.6.1 第 9 段):

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

有符号/无符号不匹配也不例外。

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

4

6 回答 6

16

I believe it is technically undefined, because the "correct type" for %x is specified as unsigned int - and as you point out, there is no exception for signed/unsigned mismatch here.

The rules for printf are for a more specific case and thus override the rules for the general case (for another example of the specific overriding the general, it's allowable in general to pass NULL to a function expecting a const char * argument, but it's undefined behaviour to pass NULL to strlen()).

I say "technically", because I believe an implementation would need to be intentionally perverse to cause a problem for this case, given the other restrictions in the standard.

于 2011-01-12T00:54:23.673 回答
8

不,因为 %x 格式化了一个 unsigned int,而常量表达式 1 的类型是 int,而它的值可以表示为一个 unsigned int。操作不是UB。

于 2011-01-12T00:16:47.680 回答
3

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

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

在您的示例情况下,这更加奇怪,因为1反过来又不允许使用无符号类型的陷阱表示。因此,要使其成为“真实”示例,您必须使用-1.

我认为仍然没有任何架构可供人们编写具有这些特性的 C 编译器,因此,如果标准的更新版本可以废除这种令人讨厌的情况,那么最终实现将变得更加容易。

于 2011-01-12T08:20:23.940 回答
0

我相信它是未定义的。具有可变长度参数列表的函数在接受参数时没有隐式转换,因此在过去时不会强制转换为1,从而导致未定义的行为。unsigned intprintf()

于 2016-03-15T03:29:39.253 回答
0

TL;DR 它不是 UB。

作为 n。'代词' m。在这个答案中指出,C标准说有符号整数类型的所有非负值都具有与相应的无符号类型完全相同的表示,因此只要值在两种类型的范围内,就可以互换使用.

来自 C99 标准 6.2.5 类型 - 第 9 段和脚注 31:

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

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

完全相同的文本在 C11 标准的 6.2.5 类型 - 第 9 段和脚注 41 中。

于 2020-09-17T11:06:48.470 回答
-1

该标准的作者通常不会尝试在每个可以想象的极端情况下明确规定行为,特别是当所有实现都 100% 共享明显正确的行为时,并且没有理由期望任何实现做任何其他事情。尽管标准明确要求有符号和无符号类型对适合两者的值具有匹配的内存表示,但理论上可以实现将它们以不同的方式传递给可变参数函数。该标准并没有禁止这种行为,但我没有看到作者故意允许这种行为的证据。最有可能的是,他们根本没有考虑这种可能性,因为从来没有(据我所知,从来没有)以这种方式工作过。

如果代码在有符号值上使用 %x ,则清理实现可能是合理的,尽管质量清理实现还应该提供一个选项来静默接受此类代码。如果在诊断/清理模式下使用,除了将传递的值处理为无符号或 squawk 之外,理智的实现没有理由做任何事情。虽然标准可能禁止实现将任何在有符号值上使用 %x 的代码视为不可访问,但任何认为实现应该利用这种自由的人都应该被视为白痴。

专门针对健全的非诊断性实现的程序员在输出诸如“uint8_t”值之类的东西时不必担心添加强制转换,但是那些代码可能被提供给低能实现的程序员可能想要添加这样的强制转换以防止编译器从“优化”这样的实现可能会强加。

于 2017-06-16T20:41:53.983 回答