如果我有一个接收可变长度参数的函数
void print_arg_addr (int n, ...)
我可以使用这三个宏来解析参数
va_start(ap,v)
va_arg(ap,t)
va_end(ap)
据我了解,
va_start让ap指向第二个参数,
va_arg 将 ap 移至下一个参数,
va_end 让 ap 指向 NULL。
所以我使用下面的代码片段来检查我的理解, 但事实证明 ap 没有改变, 我预计 ap 每次都会增加 4。
void print_arg_addr (int n, ...)
{
int i;
int val;
va_list vl;
va_start(vl,n);
for (i=0;i<n;i++)
{
val=va_arg(vl,int);
printf ("ap:%p , %d\n",vl,val);
}
va_end(vl);
printf ("ap:%p \n",vl);
}
int main()
{
print_arg_addr(5,1,2,3,4,5);
}
输出:
ap:0x7ffc62fb9890 , 1
ap:0x7ffc62fb9890 , 2
ap:0x7ffc62fb9890 , 3
ap:0x7ffc62fb9890 , 4
ap:0x7ffc62fb9890 , 5
ap:0x7ffc62fb9890
谢谢!
最佳答案
va_list
(如您的vl
)是一些abstract data type不允许您传递给 printf
。它的实现对于您的编译器(和 processor architecture )来说是私有(private)的,并且与 ABI 相关。和 calling conventions 。编译包含所有警告和调试信息的代码:gcc -Wall -Wextra -g
。您会收到警告,并且您有 undefined behavior所以你应该非常scared .
换句话说,将 va_list
、va_start
、va_end
(以及所有 stdarg(3) ...)视为一些魔法由编译器提供。这就是为什么它们是 C11 规范的一部分(阅读 n1570 )并且通常作为编译器内置函数实现。
如果您需要了解 va_list
和 friend 的内部(但您不应该需要它),请深入了解您的编译器(并且研究your ABI)。自 GCC是 free software数百万行源代码,你可能会花很多年的时间来研究它。就您而言,我认为不值得付出努力。
您还可以使用 gcc -O -S -fverbose-asm
查看生成的汇编代码(.s
文件)。
当前调用约定使用处理器寄存器。这就是为什么理解可变参数调用的细节很复杂。在 20 世纪 80 年代,参数被压入机器堆栈,当时 va_start
返回一些指针到堆栈中。现在事情变得更加复杂,您不想深入研究这种复杂性。
关于c 函数如何接收可变长度参数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47069228/