问题引入:我们知道,C语言给我们提供了一个不确定参数个数传参的方法,这个方法就是在传参列表后面加上一个...。具体形式如下:
#include <stdio.h>
void print(int a,...)
{
}
其中获取参数列表的方法如下:
#include <iostream>
#include <cstdarg>
using namespace std;
void print(int begin, ...) //可变参数列表需要指定一个传入参数begin来获取其他传入参数的地址
{
va_list v;
va_start(v,begin);
int record = begin;
while (1)
{
cout << record;
record = va_arg(v, int);
if (record == 0)
break;
}
va_end(v);
}
int main()
{
int a = 10; //这里定义a这个变量只是用于方便观看调用print函数后,压栈后的内存变化
print(1,2,3,4,5,6,7,0); //这里的0是结束的标记
return 0;
}
原理如下:C语言在调用函数时,会把函数参数压栈,而且函数参数在内存上的排列是连续的。通过VS强大的调试器我们可以轻松观测到内存的变化,在调用print函数后,内存的变化如下:
可以看到上图变红的部分就是在调用print函数压栈后的内存变化。分别是0x00000001,0x00000002,....0x00000007,0x00000000。他们是连续的。我们在看变量v的地址和值:
可以看到变量v的值是0x00cff60c,我们顺着上面图中的内存指示可以看到,0x00cff60c正好是上面图中压栈后值2(0x00000002)的地址(这是在执行va_start(v,begin)之后v指示的值)。讲到这里,大家应该大概明白了va_list获取参数列表的原理。其实就是v指向了压栈后的那块内存的首地址,然后我们通过va_arg(v,int)得到v指向的那块地址的值(即0x00000002=2),并将v指向的内存往后偏移4个字节从而指向后面的参数的地址,这里的int只是提供了一个偏移量,好让v正确的指向下一个参数的地址,当然int也可以是double,char或者任意长度(前提是你知道你想要获取的是什么)。 当v指向我们指定的末尾值(在这里是0)的地址时,就意味着我们已经读取了所有传入的参数。
可能有讲得不对的地方,欢迎大家批评指正。