这是否会调用未定义的行为?

考虑以下C程序:

#include  int main(){ int a =-1; unsigned b=-1; if(a==b) printf("%d %d",a,b); else printf("Unequal"); return 0; } 

在行printf("%d %d",a,b);"%d"用于打印无符号类型。 这会调用未定义的行为吗?为什么?

尽管明确允许您使用va_arg宏来检索作为unsigned作为int (7.15.1.1/2)传递的参数,但在fprintf (7.19.6.1/9)的文档中也适用于printf ,它明确指出如果任何参数不是格式说明符的正确类型 – 对于未修改的%d ,即int – 则不定义行为。

正如@bdonlan在评论中指出的那样,如果b的值(在这种情况下为某些N 2^N - 1 )在int无法表示,则尝试使用va_arg作为int访问该值将是未定义的行为任何情况。 这仅适用于unsigned表示使用至少一个填充位的平台,其中相应的int表示具有符号位。

即使在(unsigned)-1的值可以用int表示的情况下,我仍然将其视为技术上未定义的行为 。 作为实现的一部分,似乎允许一个实现使用内置的魔术而不是va_args来访问printf的参数,如果你传递一些东西作为unsigned需要int ,那么你在技术上违反了合同printf

在这一点上,标准并非100%明确。 一方面,您获得了va_arg的规范,其中说(§7.15.1.1/ 2):

如果没有实际的下一个参数,或者type与实际的下一个参数的类型不兼容(根据默认参数提升而提升),则行为是未定义的,除了以下情况:

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

另一方面,你得到printf的规范(§7.19.6.1/ 9):

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

鉴于printf将使用va_arg检索参数几乎是给定的,我会说你可以用目标类型表示的值非常安全,但不是。 由于在传递-1之前已将-1转换为unsigned,因此该值将超出可在signed int中表示的范围,因此行为将是未定义的。

是的, if将始终评估为true, printf将尝试将unsigned打印为signed 。 由于有signed类型可能具有陷阱表示,如果符号表示是一个补码,则可以是UB。