考虑以下C程序:
#include <stdio.h>
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"
用于打印无符号类型。这会调用未定义的行为吗?为什么?
答案 0 :(得分:5)
虽然明确允许您使用va_arg
中的<stdarg.h>
宏来检索作为unsigned
int
传递的参数(7.15.1.1/2) ),在同样适用于fprintf
的{{1}}(7.19.6.1/9)的文档中,它明确指出如果任何参数不是格式说明符的正确类型 - 对于未修改的{{ 1}},即printf
- 然后没有定义行为。
正如@bdonlan在评论中指出的那样,如果%d
中的int
(在这种情况下为某些b
2^N - 1
)的值无法表示,那么未定义的行为在任何情况下都尝试使用N
以int
的身份访问该值。这仅适用于int
的表示使用至少一个填充位的平台,其中相应的va_arg
表示具有符号位。
即使在unsigned
中可以表示int
的值的情况下,我仍然将其视为技术上未定义的行为。作为实现的一部分,似乎允许实现使用内置魔术而不是(unsigned)-1
来访问int
的参数,并且如果您将某些内容作为va_args
传递到哪里需要printf
,否则您在技术上违反了unsigned
的合同。
答案 1 :(得分:2)
在这一点上,标准并非100%明确。一方面,你得到va_arg
的规范,其中说(§7.15.1.1/ 2):
如果没有实际的下一个参数,或者如果 type与实际的下一个参数的类型不兼容(如根据提升的那样) 对于默认参数提升),行为是未定义的,除了以下内容 例:
- 一种类型是有符号整数类型,另一种类型是相应的无符号整数 类型,值可以在两种类型中表示;
- 一种类型是指向void的指针,另一种是指向字符类型的指针。
另一方面,你得到printf
(§7.19.6.1/ 9)的规范:
如果任何参数不是相应转换规范的正确类型,则行为未定义。“
鉴于printf
将使用va_arg
检索参数,我认为你可以用目标类型表示的值非常安全,但不是。由于您在传递-1之前已将-1转换为无符号,因此该值将超出可在signed int中表示的范围,因此行为将是未定义的。
答案 2 :(得分:1)
是的,if
将始终评估为true,printf
将尝试将unsigned
打印为signed
。由于signed
类型可能具有陷阱表示,如果符号表示是一个补码,则可以是UB。