C语言 在 C 中使用 varargs 的示例
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15784729/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
An example of use of varargs in C
提问by Roman
HereI found an example of how varargs can be used in C.
在这里我找到了一个如何在 C 中使用 varargs 的例子。
#include <stdarg.h>
double average(int count, ...)
{
va_list ap;
int j;
double tot = 0;
va_start(ap, count); //Requires the last fixed parameter (to get the address)
for(j=0; j<count; j++)
tot+=va_arg(ap, double); //Requires the type to cast to. Increments ap to the next argument.
va_end(ap);
return tot/count;
}
I can understand this example only to some extent.
我只能在某种程度上理解这个例子。
It is not clear to me why we use
va_start(ap, count);. As far as I understand, in this way we set the iterator to its first element. But why it is not set to the beginning by default?It is not clear to me why we need to give
countas an argument. Can't C automatically determine the number of the arguments?It is not clear to me why we use
va_end(ap). What does it change? Does it set the iterator to the end of the list? But is it not set to the end of the list by the loop? Moreover, why do we need it? We do not useapanymore; why do we want to change it?
我不清楚为什么我们使用
va_start(ap, count);. 据我了解,通过这种方式我们将迭代器设置为它的第一个元素。但是为什么默认不设置为开头呢?我不清楚为什么我们需要给出
count一个论点。C不能自动确定参数的数量吗?我不清楚为什么我们使用
va_end(ap). 它有什么变化?它是否将迭代器设置到列表的末尾?但是它不是被循环设置到列表的末尾吗?此外,我们为什么需要它?已经不使用ap了;我们为什么要改变它?
回答by Some programmer dude
Remember that arguments are passed on the stack. The va_startfunction contains the "magic" code to initialize the va_listwith the correct stack pointer. It mustbe passed the last named argument in the function declaration or it will not work.
请记住,参数是在堆栈上传递的。该va_start函数包含va_list用正确的堆栈指针初始化 的“魔术”代码。它必须通过函数声明中的最后一个命名参数,否则它将不起作用。
What va_argdoes is use this saved stack pointer, and extract the correct amount of bytes for the type provided, and then modify apso it points to the next argument on the stack.
什么va_arg是使用这个保存的堆栈指针,并为提供的类型提取正确的字节数,然后修改ap它以指向堆栈上的下一个参数。
In reality these functions (va_start, va_argand va_end) are not actually functions, but implemented as preprocessor macros. The actual implementation also depends on the compiler, as different compilers can have different layout of the stack and how it pushes arguments on the stack.
实际上,这些函数 ( va_start,va_arg和va_end) 实际上并不是函数,而是作为预处理器宏实现的。实际的实现还取决于编译器,因为不同的编译器可以有不同的堆栈布局以及它如何将参数压入堆栈。
回答by Art
But why it is not set to the beginning by default?
但是为什么默认不设置为开头呢?
Maybe because of historical reasons from when compilers weren't smart enough. Maybe because you might have a varargs function prototype which doesn't actually care about the varargs and setting up varargs happens to be expensive on that particular system. Maybe because of more complex operations where you do va_copyor maybe you want to restart working with the arguments multiple times and call va_startmultiple times.
也许是因为编译器不够聪明的历史原因。也许是因为您可能有一个可变参数函数原型,它实际上并不关心可变参数,并且在该特定系统上设置可变参数恰好是昂贵的。也许是因为您执行的操作更复杂,va_copy或者您想多次重新开始使用参数并多次调用va_start。
The short version is: because the language standard says so.
简短的版本是:因为语言标准是这样说的。
Second, it is not clear to me why we need to give count as an argument. Can't C++ automatically determine the number of the arguments?
其次,我不清楚为什么我们需要将 count 作为一个论点。C++ 不能自动确定参数的数量吗?
That's not what all that countis. It is the last namedargument to the function. va_startneeds it to figure out where the varargs are. Most likely this is for historical reasons on old compilers. I can't see why it couldn't be implemented differently today.
这不是全部count。它是函数的最后一个命名参数。va_start需要它找出可变参数的位置。这很可能是由于旧编译器的历史原因。我不明白为什么今天不能以不同的方式实施。
As the second part of your question: no, the compiler doesn't know how many arguments were sent to the function. It might not even be in the same compilation unit or even the same program and the compiler doesn't know how the function will be called. Imagine a library with a varargs function like printf. When you compile your libc the compiler doesn't know when and how programs will call printf. On most ABIs (ABI is the conventions for how functions are called, how arguments are passed, etc) there is no way to find out how many arguments a function call got. It's wasteful to include that information in a function call and it's almost never needed. So you need to have a way to tell the varargs function how many arguments it got. Accessing va_argbeyond the number of arguments that were actually passed is undefined behavior.
作为您问题的第二部分:不,编译器不知道向函数发送了多少参数。它甚至可能不在同一个编译单元中,甚至不在同一个程序中,并且编译器不知道将如何调用该函数。想象一个带有可变参数函数的库,如printf. 当您编译 libc 时,编译器不知道程序何时以及如何调用printf. 在大多数 ABI(ABI 是如何调用函数、如何传递参数等的约定)上,无法找出函数调用获得了多少个参数。将这些信息包含在函数调用中是一种浪费,而且几乎从不需要。所以你需要有一种方法来告诉 varargs 函数它有多少个参数。访问va_arg超出实际传递的参数数量是未定义的行为。
Then it is not clear to me why do we use va_end(ap). What does it change?
那么我不清楚为什么我们使用 va_end(ap)。它有什么变化?
On most architectures va_enddoesn't do anything relevant. But there are some architectures with complex argument passing semantics and va_startcould even potentially malloc memory then you'd need va_endto free that memory.
在大多数架构va_end上不做任何相关的事情。但是有些架构具有复杂的参数传递语义,va_start甚至可能会va_end分配内存,然后您需要释放该内存。
The short version here is also: because the language standard says so.
这里的简短版本也是:因为语言标准是这样说的。
回答by W.B.
va_start initalises the list of variable arguments. You always pass the last named function argumentas the second parameter. It is because you need to provide information about location in the stack, where variable arguments begin, since arguments are pushed on the stack and compiler cannot know where there's a beginning of variable argument list (there's no differentiation).
va_start 初始化变量参数列表。您始终将最后一个命名的函数参数作为第二个参数传递。这是因为您需要提供有关堆栈中变量参数开始位置的信息,因为参数被压入堆栈并且编译器无法知道变量参数列表的开始位置(没有区别)。
As to va_end, it is used to free resources allocated for the variable argument list during the va_start call.
对于 va_end,它用于在 va_start 调用期间释放为变量参数列表分配的资源。
回答by ForEveR
It's C macroses. va_startsets internal pointer to address of first element. va_endcleanup va_list. If there is va_startin code and there is no va_end- it's UB.
这是 C 宏。va_start设置内部指针指向第一个元素的地址。va_end清理va_list。如果有va_start代码而没有va_end- 它是 UB。
The restrictions that ISO C places on the second parameter to the va_start() macro in header are different in this International Standard. The parameter parmN is the identifier of the rightmost parameter in the variable parameter list of the function definition (the one just before the ...). If the parameter parmN is declared with a function, array, or reference type, or with a type that is not compatible with the type that results when passing an argument for which there is no parameter, the behavior is undefined.
ISO C 对标头中 va_start() 宏的第二个参数的限制在本国际标准中是不同的。参数 parmN 是函数定义的可变参数列表中最右边参数的标识符(就在 ... 之前的那个)。如果使用函数、数组或引用类型声明参数 parmN,或者使用与传递没有参数的参数时产生的类型不兼容的类型,则行为未定义。

