首页 > 代码库 > C可变参数实现原理

C可变参数实现原理

在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个宏实现可变参数功能的。




C可变参数实现原理