描述
在C语言中,当我们无法确认传入函数实参的数量和类型时,经常会使用省略号参数表
int sum(int , ...),那么这是如何实现的呢,其实是使用了 va_start,va_arg,va_end这三个宏。
下面是这三个宏的声明。
void va_start ( va_list ap, prev_param );
type va_arg ( va_list ap, type );
void va_end ( va_list ap );
详解
1、void va_start ( va_list ap, prev_param ); 初始化ap变量
参数
- ap -- 这是一个 va_list 类型的对象,它用来存储通过 va_arg 获取额外参数时所必需的信息。
- prev_param -- 最后一个传递给函数的已知的固定参数。
2、type va_arg ( va_list ap, type ); 获取参数
参数
- ap -- va_list 类型的对象。
- type -- 获取数据的类型。
3、void va_end ( va_list ap ); 释放ap指针
参数
- ap -- va_list 类型的对象。
原理
函数参数是以数据结构:栈的形式存取,从右至左入栈。
首先是参数的内存存放格式:参数存放在内存的堆栈段中,在执行函数的时候,从最后一个开始入栈。因此栈底高地址,栈顶低地址,举个例子如下:
void func(int x, float y, char z);
那么,调用函数的时候,实参 char z 先进栈,然后是 float y,最后是 int x,因此在内存中变量的存放次序是 x->y->z,因此,从理论上说,我们只要探测到任意一个变量的地址,并且知道其他变量的类型,通过指针移位运算,则总可以顺藤摸瓜找到其他的输入变量。va_arg就是通过此种方式,来获取传入的参数。
实例
#include <stdio.h>
#include <stdarg.h>
int sum(int num, ...)
{
int Sum = 0;
va_list ap;
int i;
va_start(ap, num);
for(i = 0; i < num; i++){
Sum += va_arg(ap, int);
}
va_end(ap);
return Sum;
}
int main(int argc, const char *argv[])
{
printf("%d\n", sum(5, 1, 3, 5, 6,7));
printf("%d\n", sum(4, 10, 11, 12, 13));
return 0;
}
输出:22 46