C 这会调用未定义的行为吗?

C 这会调用未定义的行为吗?,c,undefined-behavior,C,Undefined Behavior,考虑以下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; } #包括 int main(){ int a=-1; 无符号b=-1; 如果(a==b) printf(“%d%d”,a,b); 其他的 printf

考虑以下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;
 }
#包括
int main(){
int a=-1;
无符号b=-1;
如果(a==b)
printf(“%d%d”,a,b);
其他的
printf(“不平等”);
返回0;
}

在printf的
行中(“%d%d”,a,b)
“%d”
用于打印无符号类型。这是否会调用未定义的行为及其原因?

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

正如@bdonlan在一篇评论中所指出的,如果
b
(在本例中,对于某些
N
,对于
2^N-1
)的值在
int
中不可表示,那么在任何情况下,尝试使用
va_arg
int
的形式访问该值都是未定义的行为。这仅适用于
无符号
表示至少使用一个填充位的平台,而相应的
int
表示具有符号位


即使在
(unsigned)-1的值可以用
int
表示的情况下,我仍然认为这是技术上未定义的行为。作为实施的一部分,似乎允许实现使用内置魔术而不是
va_args
来访问
printf
的参数,如果您将某个内容作为
未签名的
传递,其中需要
int
,则您在技术上违反了
printf
的合同,
if
将始终计算为true,而
printf
将尝试将
未签名的
打印为
已签名的
。由于
有符号
类型可能有陷阱表示,如果符号表示是一个人的补码,则可能是UB

在这一点上,标准不是100%清楚。一方面,您得到了
va_arg
的规范,其中说明(§7.15.1.1/2):

如果没有实际的下一个参数,或者 类型与实际下一个参数的类型不兼容(根据 对于默认参数,该行为未定义,但以下情况除外 案例:

  • 一种类型是有符号整数类型,另一种类型是相应的无符号整数 类型,并且值在这两种类型中都可以表示
  • 一种类型是指向void的指针,另一种类型是指向字符类型的指针
另一方面,您得到了
printf
(§7.19.6.1/9)的规范:

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


考虑到
printf
将检索带有
va_arg
的参数,我想说的是,使用可以在目标类型中表示的值是非常安全的,但不是其他类型。由于在传递它之前已将-1转换为无符号,因此该值将超出可以在有符号I中表示的范围nt,因此行为将是未定义的。

7.15.1.1/2中的异常如下:“如果类型与实际下一个参数的类型不兼容[…],则行为是未定义的,除非[其中]一种类型是有符号整数类型,另一种类型是相应的无符号整数类型,并且值在两种类型中都可表示。”(强调矿山).由于
-1
在这两种类型中都不可表示,因此即使没有7.19.6.1,行为也未定义/9@bdonlan:从技术上讲,
b
没有值
-1
,对于某些
N
,它有值
2^N-1
。此值是否在
int
未签名的
中都可以表示,取决于rm依赖-通常不是,我同意你。如果一个行为只有在某个实现定义的因子为真时才是未定义的,那么它实际上是未定义的,因为一致性实现可以自由选择INT_MAX和UINT_MAX的值,这将允许它在有问题的printf调用时召唤鼻魔。@bdonlan:它不是“有效未定义的”“因为实现必须记录
INT_MAX
和UINT_MAX`,所以可以确定它是否不是特定实现的UB。除非您的意思是“实际上未定义”?它在任何情况下都不是通用的可移植的。我的意思是,一个程序通常不能为它假设任何特定的语义。当然,如果INT_MAX>=(UINT_MAX-1)
,你可以做一个
,但是,在实践中,你最好假设它总是UB,并且用iteexcept(unsigned)-1变成(UINT_MAX-1),这通常超出了INT_MAX的范围…我想知道是否有任何实现使用
%X
和带符号值的friends除了显示基础位的十六进制表示之外还能做什么?如果没有,这似乎是标准中应该提供的有用行为,特别是因为现有代码的性能依赖于该行为。