Rom*_*man 34 c syntax variadic-functions
在这里,我找到了如何在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;
}
Run Code Online (Sandbox Code Playgroud)
我只能在某种程度上理解这个例子.
我不清楚为什么使用它们va_start(ap, count);
.据我所知,通过这种方式我们将迭代器设置为它的第一个元素.但是为什么默认情况下它没有设置到开头?
我不清楚为什么我们需要count
作为一个论点.C不能自动确定参数的数量?
我不清楚为什么使用它们va_end(ap)
.它有什么变化?它是否将迭代器设置为列表的末尾?但它是不是通过循环设置到列表的末尾?而且,为什么我们需要它呢?我们不再使用ap
了; 我们为什么要改变它?
Som*_*ude 35
请记住,参数在堆栈上传递.该va_start
函数包含"魔术"代码,用于va_list
使用正确的堆栈指针初始化.它必须在函数声明中传递最后一个命名参数,否则它将不起作用.
什么va_arg
是使用这个保存的堆栈指针,并为提供的类型提取正确的字节数,然后修改ap
,使其指向堆栈上的下一个参数.
实际上,这些函数(va_start
,va_arg
和va_end
)实际上不是函数,而是实现为预处理器宏.实际的实现还取决于编译器,因为不同的编译器可以具有不同的堆栈布局以及它如何在堆栈上推送参数.
但是为什么默认情况下它没有设置到开头?
也许是因为编译器不够聪明的历史原因.也许是因为你可能有一个varargs函数原型,它实际上并不关心varargs,并且设置varargs在特定系统上碰巧是昂贵的.也许是因为您执行的操作更复杂,va_copy
或者您希望多次重新使用参数并多次调用va_start
.
简短版本是:因为语言标准是这样说的.
其次,我不清楚为什么我们需要把数作为一个论点.C++不能自动确定参数的数量?
这不是那一切count
.它是函数的最后一个命名参数.va_start
需要它来弄清楚varargs在哪里.这很可能是出于旧编译器的历史原因.我不明白为什么今天不能以不同方式实施.
作为问题的第二部分:不,编译器不知道向函数发送了多少个参数.它甚至可能不在同一个编译单元甚至是同一个程序中,编译器也不知道如何调用该函数.想象一下具有类似varargs功能的库printf
.编译libc时,编译器不知道程序何时以及如何调用printf
.在大多数ABI上(ABI是如何调用函数,如何传递参数等的约定),没有办法找出函数调用得到多少个参数.将这些信息包含在函数调用中并且几乎不需要它是浪费的.因此,您需要有一种方法来告诉varargs函数它获得了多少参数.访问va_arg
超出实际传递的参数数量是未定义的行为.
然后我不清楚为什么我们使用va_end(ap).它有什么变化?
在大多数架构va_end
上没有做任何相关的事情.但是有一些架构具有复杂的参数传递语义,va_start
甚至可能具有malloc内存,因此您需要va_end
释放该内存.
这里的简短版本也是:因为语言标准是这样说的.
va_start初始化变量参数列表.您始终将最后一个命名函数参数作为第二个参数传递.这是因为您需要提供有关堆栈中位置的信息,其中变量参数开始,因为参数被压入堆栈,编译器无法知道变量参数列表的开头在哪里(没有区别).
对于va_end,它用于在va_start调用期间释放为变量参数列表分配的资源.
归档时间: |
|
查看次数: |
40391 次 |
最近记录: |