在C程序设计语言中,使用printf函数进行标准输出。
int printf ( const char * format, ... );
printf函数申明中"..."代表可变参数。
printf ("floats: %4.2f %+.0e %E \n", 3.1416, 3.1416, 3.1416);
printf ("Width trick: %*d \n", 5, 10);
那么,如何实现可变参数?
近日,在读Linux0.12源代码的过程中,我看到了一个实现。下面,我给出一个demo程序。
#include <iostream>
using namespace std;
typedef char *va_list;
#define __va_rounded_size(TYPE) \
(((sizeof (TYPE) + sizeof (int) - 1) / sizeof (int)) * sizeof (int))
#define va_start(AP, LASTARG) \
(AP = ((char *) &(LASTARG) + __va_rounded_size (LASTARG)))
#define va_end(AP)
#define va_arg(AP, TYPE) \
(AP += __va_rounded_size (TYPE), \
*((TYPE *) (AP - __va_rounded_size (TYPE))))
void print_args(int args, ...)
{
va_list ap;
//在访问任何未命名的参数之前,必须用va_start宏初始化ap一次
va_start(ap,args);
printf("%d\n",args);
printf("%d\n",va_arg(ap,int));
printf("%s",va_arg(ap,char *));
// va_end(ap);
}
int main(void)
{
int arg = 2;
int args1 = 1;
char *args2 = "abcdefg";
print_args(2,args1,args2);
return 0;
}
不妨在print_args程序中设置一个断点。
首先,查看一下args参数的内存地址:
+ &args 0x0028f71c int *
现在,我们查看0x0028f71c处的内存:
0x0028F71C 02 00 00 00 ....
0x0028F720 01 00 00 00 ....
0x0028F724 08 58 cd 00 .X?.
显然, 0x0028f71c处的4个字节为0x00000002,即main函数中的arg参数;
0x0028f720处的4个字节为0x00000001,即main函数中的args1参数;
而0x0028f724处的4个字节内容为0x00cd5808,这是一个内存地址;
0x00CD5808 61 62 63 64 abcd
0x00CD580C 65 66 67 00 efg.
继续查看0x00cd5808处的内存,可以看出正是"abcdefg\0"。
有了上面的基础,我们应当可以理解va_start, va_end和va_arg宏了。实际上,就是对地址的操作,以及强制类型转换。printf函数也是利用上面3个宏实现可变参数功能的。