C语言可变长参数函数与默认参数提升(4)

运行gcc4.4.6生成的程序时,运行到第23行时,输出Illegal instruction,程序退出。查看了一下gcc4.4.6生成的汇编代码,发现没有为read_args_from_va_bad()生成有效的代码。

astrol@astrol:~/c$ gdb va_arg -q
Reading symbols from /home/astrol/c/va_arg...done.
(gdb) run
Starting program: /home/astrol/c/va_arg
c 0 1.100000
c
0
1.100000

Program received signal SIGILL, Illegal instruction.
0x08048452 in read_args_from_va_bad (i=0) at va_arg.c:44
44              va_start(arg_ptr, i);
(gdb) x/i $pc
=> 0x8048452 <read_args_from_va_bad+12>:        ud2
(gdb)

UD2是一种让CPU产生invalid opcode exception的软件指令. 内核发现CPU出现这个异常, 会立即停止运行

在VC中运行的结果是不正确的:

C语言可变长参数函数与默认参数提升

以下摘自《C陷阱与缺陷》 下载见
这里有一个陷阱需要避免:
va_arg宏的第2个参数不能被指定为char、short或者float类型。
因为char和short类型的参数会被转换为int类型,而float类型的参数会被转换为double类型 ……
例如,这样写肯定是不对的:
c = va_arg(ap,char);
因为我们无法传递一个char类型参数,如果传递了,它将会被自动转化为int类型。上面的式子应该写成:
c = va_arg(ap,int);
——《C陷阱与缺陷》p164

可能有人会问,VC中的三个宏不是已经实现了自动int对齐了吗? 如下:

#define _INTSIZEOF(n)  ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )

#define va_start(ap,v)  ( ap = (va_list)&v + _INTSIZEOF(v) )
#define va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define va_end(ap)      ( ap = (va_list)0 )

下面是linux 2.6.22中的实现,其实是一样的意思

#define  _AUPBND                (sizeof (acpi_native_int) - 1)
#define  _ADNBND                (sizeof (acpi_native_int) - 1)

/*
 * Variable argument list macro definitions
 */
#define _bnd(X, bnd)            (((sizeof (X)) + (bnd)) & (~(bnd)))
#define va_arg(ap, T)          (*(T *)(((ap) += (_bnd (T, _AUPBND))) - (_bnd (T,_ADNBND))))
#define va_end(ap)              (void) 0
#define va_start(ap, A)        (void) ((ap) = (((char *) &(A)) + (_bnd (A,_AUPBND))))

不过我想说的是:

C标准对默认实际参数提升规则有明确定。
也就是说, 带有可变长参数列表的函数, 绝对不会接受到char类型的实际参数。

C标准对va_arg是否自动对齐没有任何说明

也就是说自动对齐工作,编译器可做可不做。

在所有C实现上,能保证第点,不能保证第点,所以尽管编译器实现了自动对齐,也要按标准来。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/b60962e878050aa4d5178d1f8d78b66c.html