我做了一些测试代码来演示 Windows 和 Linux 上的不同输出
#include <stdarg.h>
#include <stdio.h>
void print_vint(va_list args)
{
int i = va_arg(args, int);
printf("%d\n", i);
}
void variadic_f(const char* format, ...)
{
va_list args;
va_start(args, format);
print_vint(args);
print_vint(args);
va_end(args);
}
int main(int argc, char* argv[])
{
variadic_f("dummy ", 1, 2);
return 0;
}
print_vint
函数具有通过值传递的 va_list
类型的参数。在 Windows 机器上,它是指向最后一个非变量参数之后的堆栈的指针。参数按值传递给函数,函数在调用 va_arg
时更改它。第二次调用 print_vint
获得相同的指针值(因为函数内的本地副本已更改)并打印与第一次调用相同的输出。1
1
我在Linux上编译相同的源代码并运行。输出不同:
1
2
我的结论是Linux使用指向不透明结构的指针,该结构具有指向堆栈的指针,当通过值传递va_list时,解引用指针和更改结构内部的指针将在连续使用
va_list
中可见。
当我将
print_vint
更改为接受指针时,它在 Linux 和 Windows 上的工作方式相同。
void print_vint(va_list *args)
{
int i = va_arg(*args, int);
printf("%d\n", i);
}
C 标准是否指定
va_arg
应如何表现以及执行如何影响 va_list
参数?
这里的错误是您将
va_list
传递给另一个函数并在该函数中调用 va_arg
。一旦执行此操作,就不允许在调用者中再次使用 va_list
。
C 标准有关变量参数的第 7.16p3 节对此进行了详细说明:
声明的类型是
va_list
这是一个适合保存信息的完整对象类型 宏
、va_start
、va_arg
和va_end
需要。如果访问 对于需要变化的参数,被调用函数应声明 具有类型的对象(在本子条款中通常称为 ap)va_copy
。对象va_list
可以作为参数传递给另一个对象 功能; 如果该函数使用参数ap
调用va_arg
宏, 调用函数中ap
的值是不确定的,应为 在进一步引用ap
va_end
.253) 之前传递给ap
宏
- 允许创建一个指向
的指针并将该指针传递给另一个函数,其中 如果其他函数返回后,原始函数可能会进一步使用原始列表。va_list
请注意,上面的脚注 253 指出您可以将指针传递给
va_list
来执行您想要的操作:
void print_vint(va_list *args)
{
int i = va_arg(*args, int);
printf("%d\n", i);
}
void variadic_f(const char* format, ...)
{
va_list args;
va_start(args, format);
print_vint(&args);
print_vint(&args);
va_end(args);
}