如何写可变参数函数
void va_start(va_list pvar, name): 根据name参数获得可变参数的指针 pvar。
(type *) va_arg(va_list pvar, type): 获得下一个可变参数,需要指定参数的类型。
void va_end(va_list pvar): 结束使用可变参数,使它不再指向内存有效地址。
例如:
void simple_va_func (int i, ...)
{
va_list arg_ptr;
int j = 0;
va_start (arg_ptr, i);
//这里的arg_ptr可以传递给任意函数作为参数,在那个函数内需要使用va_* 宏去获得对应的参数。
j = va_arg (arg_ptr, int);
va_end (arg_ptr);
return;
}
如何实现可变参数
可变参数的实现与具体的编译器/硬件平台相关,我们这里简单看一下 cdecl calling convention 下的参数传递。
cdecl模式下参数从右向左压入堆栈,而且是由调用者来做这个操作。
内存高地址(x86平台)
+-----------------------------+
| 最后一个可变参数 |
+-----------------------------+
| 倒数第二个可变参数 |
+-----------------------------+
| ...... |
+-----------------------------+
| 第一个可变参数 | va_start 之后,arg_ptr 指向这里
+-----------------------------+
| 最后一个固定参数 | 这个参数的名字需要传递给va_start
+-----------------------------+
| ...... |
+-----------------------------+
| 第一个参数 |
+-----------------------------+
| 函数返回地址 |
+-----------------------------+
| 调用者的 BP | 被调用函数的栈基址
+-----------------------------+
| 局部变量 |
+-----------------------------+
注意事项
- 实际调用时,被调用函数不知道实际传来的参数的个数,因此需要在程序内使用某种方式来判断是否是最后一个参数,例如NULL, -1 ,或者使用固定参数来说明参数的个数类似于main函数,等等
- 不能没有固定参数只有可变参数,例如 int f (...) { }
- va_arg()参数类型的使用需要注意: char, short 会被扩展成 int, float会被扩展成double。
- 使用函数作为一个可变参数时需要特别注意,参见reference2
参考
C语言中可变参数的用法
水滴石穿C语言之可变参数问题
CSDN C/C++电子杂志第一期 之 可变参数学习笔记