可变参数 函数 的 定义也是从 C 继承过来的
对于可变参数函数来说 , 参数最少也要有 1 个 ,该参数作为第一个参数, 对于函数声明中已经列出的参数而言 , 其类型可以为任何类型
定义形式为 : type function( argument1 , ... );
例如 printf 的定义就是 int printf(const char* , ... );
实现可变参数的函数的时候 ,要包含的头文件 为 <stdarg.h> 在C++中就是 <cstdarg>
主要用到该头文件中的 三个 宏 和 一个类型 , 分别是 : va_start() va_arg() va_end() 和 va_list 类型
va_list 类型用来定义指向参数列表的指针
va_start( va_list 参数列表类型的指针 , int 参数个数 ): 该宏用于 让 va_list 指向参数列表 , 并告知要取几个参数
va_arg(va_list ,类型名 type) : 该宏 返回 type 类型的 参数
va_end( va_list) : 结束获取参数列表只的参数
根据以上描述 , 写出一下的示例代码
1 #include<iostream> 2 #include<string> 3 #include<cstdarg> 4 5 using namespace std; 6 7 void func(int i, ...) 8 { 9 10 int index = 0; 11 va_list arg_ptr; 12 va_start(arg_ptr ,i ); 13 for(; index < i ; index ++){ 14 cout << va_arg(arg_ptr , int ) <<endl; 15 } 16 va_end(arg_ptr); 17 } 18 19 int main() 20 { 21 func(10,1,2,3,4,5,6,7,8,9,0); 22 return 0; 23 }
输出的结果如下
1
2
3
4
5
6
7
8
9
0
结果出来了 是正确无误的 , 那么现在就来看看 可变参数的原理 。
对于C++来说 , 调用函数的时候, 参数通常会被放在 空闲的寄存器中, 如果 没有空闲的 寄存器 , 参数就会 压到栈中 。
而对于可变参数的函数 , 编译器编译的时候不会指定 寄存器用来存放 参数 , 而是统一的使用栈 。
va_start(指向参数列表的指针 , 第一个参数 )
我们知道 , 在函数调用时参数压栈的顺序是 从右向左 依次压栈的 , 那么 第一个参数就是处在栈顶的位置 , 然而 , 在操作系统中
栈顶的位置是位于高地址的 , 栈是向低地址方向增长的 ,所以可知 , 第一个参数所在的地址为所有参数所在的地址中 最小的。
func(10,1,2,3,4,5,6); 在栈中的情况如上图所示
实际上 , 在可变参数函数被调用的时候 , 栈中所有的类型都按 4字节 边界对齐
下面看自己实现的获取可变参数列表的代码
1 #include<iostream> 2 #include<string> 3 #include<cstdarg> 4 5 using namespace std; 6 struct abc{ 7 double n1; 8 char n2; 9 int n3; 10 short n4; 11 int n5; 12 }; 13 void func(int i,...) 14 { 15 char *p = reinterpret_cast<char*>(&i); 16 for(int index = 0 ; index < 7 ; index++){ 17 if(index == 3 ){ 18 cout<< *reinterpret_cast<double*>(p) <<endl; 19 p += sizeof(double); 20 }else if(index == 5){ 21 cout<< "结构体变量a" <<endl; 22 p += sizeof(struct abc); 23 }else{ 24 cout<< *reinterpret_cast<int*>(p) <<endl; 25 p += sizeof(int); 26 } 27 } 28 } 29 30 int main() 31 { 32 struct abc a = {1,2,3,4,5}; 33 printf("%d\n",sizeof(a)); 34 func(10,(char)1,(short)2,3.14159,4,a,6); 35 return 0; 36 }
执行结果如下:
24
10
1
2
3.14159
4
结构体变量a
6