c语言va_arg使用 C语言 函数变参数的问题(va_list,va_start,...

作者&投稿:拓蓓 (若有异议请与网页底部的电邮联系)
va_arg是一个宏定义,用于处理函数不确定参数个数时,即可变参数列表时对参数的取用。
1 头文件:

#include <stdarg.h>
2 形式:
type va_arg(va_list ap, type);
3 说明:

这个宏被展开成一个包含类型为type,值为ap的表达式。参数ap应该首先被宏va_start 或 va_copy初始化,但又必须在被宏va_end调用之前使用。每次调用va_arg都会改变ap值使得后续的参数值能被依次添加。参数type应该是一个类型名,并且用type*能够得到该类型的指针类型。如果type为空,或者type和实际参数不匹配, 那么除了以下两种情况,这个宏的行为是未定义的。
1) 一个是带符号整型,另一个是与之对应的无符号整型,并且值可以被表达成这两种类型的任何一种;
2) 一个是空类型指针,另一个是字符类型指针。
第一次调用va_arg返回parmN之后的参数值,后续的调用依次返回剩下的参数值。parmN应为函数中“...”前最后一个参数值。

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <memory.h>

int *oper(int n,...) {
va_list ap;
va_start(ap,n);
int i,*p = (int *)malloc(sizeof(int) * n);
memcpy(p,va_arg(ap,int *),sizeof(int) * n);
va_end(ap);
return p;
}

int main() {
int a[] = {36,45,89,9,12,31,44,67};
int i,*cb;
int n = sizeof(a)/sizeof(a[0]);
cb = oper(n,a);
for(i = 0; i < n; ++i)
printf("%d ",cb[i]);
printf("
");
free(cb);
return 0;
}


C语言的变参技术,va_start,va_arg,va_end这几个函数怎么用?~

#include // 必须包含的头文件

int Add(int start,...) // ...是作为占位符
{
va_list arg_ptr; // 定义变参起始指针
int sum=0; // 定义变参的和
int nArgValue =start; //

va_start(arg_ptr,start); // arg_ptr指向第一个变参
do
{
sum+=nArgValue; // 求和
nArgValue = va_arg(arg_ptr,int); // arg_ptr指向下一个变参
}
while(nArgValue != 0); // 判断结束条件;结束条件是自定义为=0时结束

va_end(arg_ptr); // 复位指针
return sum;
}

函数的调用方法为Add(1,2,3,0);这样,必须以0结尾,因为变参函数结束的判断条件就是读到0停止。

解释:

所使用到的宏:
void va_start( va_list arg_ptr, prev_param );
type va_arg( va_list arg_ptr, type );
void va_end( va_list arg_ptr );

typedef char * va_list;
#define _INTSIZEOF(n) ((sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define va_end(ap) ( ap = (va_list)0 )

1、首先把va_list被定义成char*,这是因为在我们目前所用的PC机上,字符指针类型可以用来存储内存单元地址。而在有的机器上va_list是被定义成void*的

2、定义_INTSIZEOF(n)主要是为了某些需要内存的对齐的系统.这个宏的目的是为了得到最后一个固定参数的实际内存大小。在我的机器上直接用sizeof运算符来代替,对程序的运行结构也没有影响。(后文将看到我自己的实现)。

3、va_start的定义为 &v+_INTSIZEOF(v) ,这里&v是最后一个固定参数的起始地址,再加上其实际占用大小后,就得到了第一个可变参数的起始内存地址。所以我们运行va_start(ap, v)以后,ap指向第一个可变参数在的内存地址,有了这个地址,以后的事情就简单了。


这里要知道两个事情:
⑴在intel+windows的机器上,函数栈的方向是向下的,栈顶指针的内存地址低于栈底指针,所以先进栈的数据是存放在内存的高地址处。
(2)在VC等绝大多数C编译器中,默认情况下,参数进栈的顺序是由右向左的,因此,参数进栈以后的内存模型如下图所示:最后一个固定参数的地址位于第一个可变参数之下,并且是连续存储的。
|--------------------------|
| 最后一个可变参数 | ->高内存地址处
|--------------------------|
|--------------------------|
| 第N个可变参数 | ->va_arg(arg_ptr,int)后arg_ptr所指的地方,
| | 即第N个可变参数的地址。
|--------------- |
|--------------------------|
| 第一个可变参数 | ->va_start(arg_ptr,start)后arg_ptr所指的地方
| | 即第一个可变参数的地址
|--------------- |
|------------------------ --|
| |
| 最后一个固定参数 | -> start的起始地址
|-------------- -| .................
|-------------------------- |
| |
|--------------- | -> 低内存地址处

(4) va_arg():有了va_start的良好基础,我们取得了第一个可变参数的地址,在va_arg()里的任务就是根据指定的参数类型取得本参数的值,并且把指针调到下一个参数的起始地址。
因此,现在再来看va_arg()的实现就应该心中有数了:
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
这个宏做了两个事情,
①用用户输入的类型名对参数地址进行强制类型转换,得到用户所需要的值
②计算出本参数的实际大小,将指针调到本参数的结尾,也就是下一个参数的首地址,以便后续处理。

(5)va_end宏的解释:x86平台定义为ap=(char*)0;使ap不再 指向堆栈,而是跟NULL一样.有些直接定义为((void*)0),这样编译器不会为va_end产生代码,例如gcc在linux的x86平台就是这样定义的. 在这里大家要注意一个问题:由于参数的地址用于va_start宏,所以参数不能声明为寄存器变量或作为函数或数组类型. 关于va_start, va_arg, va_end的描述就是这些了,我们要注意的 是不同的操作系统和硬件平台的定义有些不同,但原理却是相似的.

void showname (int num,...);//第一个参数最好存放参数个数
showname (2,locitem1,locitem2);//调用
void showname (int num,...)
{
va_list ap;
Item loc_item;
va_start (ap, num);
for(;num;num--){
loc_item = va_arg(ap,Item);
printf("%s is %s
",loc_item.name,loc_item.kind);
}
va_end (ap);
}

va_start (ap, num);通过判断num的长度来确定 变长参数列表里第一个参数的地址
所以它判断的不是num的值而是num所占内存的大小

c语言va_arg使用
答:va_arg是一个宏定义,用于处理函数不确定参数个数时,即可变参数列表时对参数的取用。1 头文件:include <stdarg.h> 2 形式:type va_arg(va_list ap, type);3 说明:这个宏被展开成一个包含类型为type,值为ap的表达式。参数ap应该首先被宏va_start 或 va_copy初始化,但又必须在被宏va_end...

【求解释va_list、va_start、va_arg、va_end】
答:获取省略号指定的参数: 在函数体中声明一个va_list,然后用va_start函数来获取参数列表中的参数,使用完毕后调用va_end()结束。va_start使arg_ptr指向第一个可选参数。va_arg返回参数列表中的当前参数并使arg_ptr指向参数列表中的下一个参数。va_end把arg_ptr指针清为NULL。函数体内可以多次遍历这些...

关于C++的可变参数问题。
答:va_list用于存取可变参数。va_start用来初始化va_list变量。va_arg用来获取下一个可变参数。va_end用来反初始化va_list变量。va_arg函数根据当前给定的类型来取得当前指向的函数参数,并且递增内部的一个指针指向下一个参数。比如 va_arg(arguments, char*)这里假设当前参数是char*类型,并修改内部指针,...

VS 中 __VA_ARGS__
答:std::string fun(int d,const char* h,...) { va_list ap; va_start(ap,h); std::string sf =h; for (int i = 1; i < d; i++) { sf += va_arg(ap,const char*); } va_end(ap); return sf.data(); } ...

C语言头文件ansidecl.h中定义的宏VA_OPEN和VA_FIXEDARG和VA_CLOSE表示...
答:这个是用于处理可变参数的,其实C标准只定义了 va_start ,va_end ,va_copy ,va_arg 这几个宏,而 va_list 是一个存储可变参数信息的对象。va_start 用于初始化可变参数列表 va_copy 将参数列表拷贝一份,而不直接使用源参数列表,当然,这个拷贝参数列表中的参数信息和源列表是一样的。var_arg ...

变元个数可变的函数问题,看不懂。
答:宏va_arg()、va_start()和va_end()一起使用,便可以完成向函数传入数目可变的变元操作。取可变数目变元的典型例子是函数printf()。类型va_list是在<stdarg.h>中定义的,上述的宏原型如下所示:type va_arg(va_list argptr, type);void va_end(va_list argptr);void va_start(va_list arg...

C语言 函数变参数的问题(va_list,va_start,va_arg,va_end)
答:这句话用错了,va_start 是用来初始化ap的,num应该改为传递的第一个对象,也就是item。loc_item = va_arg(ap,Item);这句的作用是loc_item 被赋给ap的下一对象的值。include <stdio.h> include <stdarg.h> typedef struct item { char name[20];char kind[20];}Item;void showname (...

c++怎样编写不确定参数个数的函数
答:VA_LIST的用法:首先在函数里定义一具VA_LIST型的变量,这个变量是指向参数的指针;然后用VA_START宏初始化刚定义的VA_LIST变量;然后用VA_ARG返回可变的参数,VA_ARG的第二个参数是你要返回的参数的类型(如果函数有多个可变参数的,依次调用VA_ARG获取各个参数);最后用VA_END宏结束可变参数的获取...

c语言 怎么写 可变 参数函数
答://初始化,va_start()调用的第一个参数是所编写的可变参数函数的最后一个确定的参数for (i = 0; i<argc; i++) {srt+=(int)va_arg(vl,int); //从参数列表按提取一个int类型的值参与相加运算}va_end(vl); //清理return srt;}int main(int argc, char* argv[]){printf("%ld...

【大神请进】c++ 如何将 类型的名称 作为函数的参数传入
答:接上一个人的回复,va_arg这个宏是用来实现C语言里面的可变参数列表这个功能的,本身只是一个宏函数,只是做字符串替换用的。C语言本身没有传递类型的功能,但是借助定义宏函数可以间接实现