2011-05-01 38 views
5

考虑下面的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"用于打印的无符号类型。这是否会调用未定义的行为,为什么?

回答

5

虽然明确允许使用va_arg宏从<stdarg.h>检索被作为unsignedint(7.15.1.1/2)传递一个参数,该文档在fprintf(7.19.6.1/9)的也适用于printf,它明确规定,如果任何参数是不正确的类型格式说明 - 针对未修改%d,那就是int - 则行为没有定义。

正如评论@bdonlan笔记,如果b(对于一些N在这种情况下2^N - 1)的值不是在int表示的话那就未定义行为尝试使用访问值作为int无论如何都是va_arg。其中一个unsigned的表示中使用至少一个填充位,其中相应的int表示有符号位这只会在平台上工作。

即使在的(unsigned)-1值可以在int来表示的情况下,我仍然阅读此为技术上未定义的行为。作为执行工作的一部分,它似乎被允许采用工具使用内置的魔法,而不是va_args访问参数printf,如果你传递的东西如其中需要int那么你已经在技术上违反了合同的unsignedprintf

+0

7.15的异常.1.1/2的内容如下:“如果 类型与实际下一个参数[...]的类型不兼容,则行为未定义,但[其中]一种类型是有符号整数类型,另一种类型是对应的无符号整数 类型,并且该值可以在两个types_“(强调mine)中表示。由于'-1'在两种类型中都不可表示,所以即使没有7.19.6.1/9 – bdonlan 2011-05-01 21:42:16

+0

@bdonlan,行为也是不确定的:从技术上讲,'b'没有值'-1',它的值是'2^N -1'代表一些'N'。这个值是否可以用int和unsigned来表示,取决于平台 - 通常不是,我授予你。 – 2011-05-01 21:45:12

+0

如果只有某些实现定义的因子为真时才会定义某个行为,则该行为实际上是未定义的,因为符合的实现可以自由选择INT_MAX和UINT_MAX的值,以允许其在有问题的printf调用中召唤鼻子恶魔。 – bdonlan 2011-05-01 21:48:45

1

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

2

的标准是不是100%清楚这一点。一方面,你得到va_arg规范,它说(§7.15.1.1/ 2):

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

  • 一种类型是一个带符号的整数类型,其他类型是相应的无符号整数 类型,价值可以在两种类型中表示;
  • 一种类型是指向void,而另一个是一个指针,指向字符类型。

在另一方面,你得到的printf(§7.19.6规范。1/9):

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

因为这几乎是一个给定的printf将检索论点。 va_arg,我会说你非常安全,可以在目标类型中表示值,但不能以其他方式表示。由于在传递之前已将-1转换为无符号,所以该值将超出范围可以用带符号的整数表示,所以行为将是未定义的。

+0

除(无符号)-1成为(UINT_MAX - 1),通常超出INT_MAX的范围... – bdonlan 2011-05-01 21:50:05

+0

@bdonlan:糟糕 - 您是对的。编辑... – 2011-05-01 21:54:24

+0

我不知道是否有任何实现使用'%X'和带有符号值的朋友除了显示底层位的十六进制表示之外什么都不做?如果不是这样,这似乎是标准中应该提供的有用行为,尤其是因为很多现有代码都依赖于该行为。 – supercat 2015-12-16 18:36:29

相关问题