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

c how does function receive variable length argument?

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

void print_arg_addr (int n, ...) 

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

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

据我了解,

va_startap指向第二个参数,

va_arg 移动 ap 到下一个参数,

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 , %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 that you are not allowed to pass to printf. Its implementation is private to your compiler (and processor architecture), and related to the ABI and calling conventions. Compile your code with all warnings and debug info: gcc -Wall -Wextra -g. You'll get warnings, and you have undefined behavior so you should be very scared

换句话说,考虑 va_listva_startva_end(以及所有 stdarg(3) ...) as some magic provided by the compiler. That is why they are part of the C11 specification (read n1570)并且通常作为编译器内置函数实现。

如果您需要了解 va_list 和朋友的 内部结构 (但 您不应该 需要),跳转在你的编译器中(并研究 your ABI). Since GCC is free software 数百万行源代码,你可能会花很多年研究它。在你的情况下,我认为不值得付出努力。

您还可以使用 gcc -O -S -fverbose-asm

查看生成的汇编代码(.s 文件)

当前调用约定使用处理器寄存器。这就是理解可变参数调用的细节很复杂的原因。在 1980 年代,参数被压入机器堆栈,那时 va_start 将一些指针返回到堆栈中。现在事情要复杂得多,您不想深入了解那种复杂性。