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_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 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_list
、va_start
、va_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
将一些指针返回到堆栈中。现在事情要复杂得多,您不想深入了解那种复杂性。
如果我有一个接收可变长度参数的函数
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 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_list
、va_start
、va_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
将一些指针返回到堆栈中。现在事情要复杂得多,您不想深入了解那种复杂性。