c函数如何接收可变长度的参数



如果我有一个函数接收可变长度的参数

void print_arg_addr (int n, ...) 

我可以使用这三个宏来解析参数

va_start(ap,v)
va_arg(ap,t)
va_end(ap)  

据我所知,

va_startap指向第二个参数

va_argap移动到下一个参数

va_endap指向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 , %dn",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类似)是不允许传递给printf的一些抽象数据类型。它的实现对于编译器(和处理器体系结构)是私有的,并且与ABI和调用约定有关。编译包含所有警告和调试信息的代码:gcc -Wall -Wextra -g。你会收到警告,并且你有未定义的行为,所以你应该非常害怕。

换句话说,考虑va_listva_startva_end(以及所有stdarg(3)…)作为编译器提供的一些魔术。这就是为什么它们是C11规范(读作n1570)的一部分,并且通常作为编译器内建实现。

如果您需要了解va_list和朋友的内部(但您不应该需要),请深入编译器(并研究ABI)。由于GCC是一个拥有数百万行源代码的免费软件,你可以花很多年的时间来研究它。就你而言,我认为这不值得付出努力。

您还可以使用gcc -O -S -fverbose-asm查看生成的汇编代码(.s文件)

当前的调用约定使用处理器寄存器。这就是为什么理解变调呼叫的细节是复杂的。在20世纪80年代,参数被推送到机器堆栈上,当时va_start将一些指针返回到堆栈中。现在的事情要复杂得多,你不想陷入那种复杂性

相关内容

  • 没有找到相关文章

最新更新