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

标签 c macros variable-length

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

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 , %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_listva_startva_end(以及所有 stdarg(3) ...)视为一些魔法由编译器提供。这就是为什么它们是 C11 规范的一部分(阅读 n1570 )并且通常作为编译器内置函数实现。

如果您需要了解 va_list 和 friend 的内部(但您不应该需要它),请深入了解您的编译器(并且研究your ABI)。自 GCCfree software数百万行源代码,你可能会花很多年的时间来研究它。就您而言,我认为不值得付出努力。

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

当前调用约定使用处理器寄存器。这就是为什么理解可变参数调用的细节很复杂。在 20 世纪 80 年代,参数被压入机器堆栈,当时 va_start 返回一些指针到堆栈中。现在事情变得更加复杂,您不想深入研究这种复杂性。

关于c 函数如何接收可变长度参数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47069228/

相关文章:

c - 如何推广方阵乘法来处理任意维度

python-3.x - 读取存储在列表中的索引

c - GCC 链接函数时没有错误

c - 使用 peg/leg 时缓冲区溢出

c++ - 如果明确给出多维数组,为什么 char[][] = {{...}, {...}} 不可能?

c - 预处理器数据检索宏

c - 如何在 C 中使用迭代器宏来防止阴影

c++ - 使用 C++ 宏创建新作用域?

c - C 中的移位和掩码

python - Pytorch Dataloader 如何处理可变大小的数据?