现在先用一个使用过程讲解一下:

◎用法: 
func( Type para1, Type para2, Type para3, … ) 

/****** Step 1 ******/ 
va_list ap; 
va_start( ap, para3 ); //一定要“…”之前的那个参数 ,而且这个参数不能使引用类型,因为引用类型不能根据其地址获取后面参数的地址

/****** Step 2 ******/ 
//此时ap指向第一个可变参数 
//调用va_arg取得里面的值

Type xx = va_arg( ap, Type );

//Type一定要相同,如: 
//char *p = va_arg( ap, char *); 
//int i = va_arg( ap, int );

//如果有多个参数继续调用va_arg

/****** Step 3 ******/ 
va_end(ap); //For robust! 

然后写个小程序,大家看看就明白了stdarg.h的用法了

#include <iostream>
#include <stdarg.h>
const int N=5;
using namespace std;
void Stdarg(int a1,…)
{
    va_list argp;
    int i;
    int  ary[N];
    va_start(argp,a1);
    ary[0]=a1;
    for(i=1;i< N;i++)
       ary[i]=va_arg(argp,int);
    va_end(argp);
    for(i=0;i< N;i++)
       cout<<ary[i]<<endl;
}
void main()
{
    Stdarg(5,12,64,34,23);
}

最后我们来分析一下stdarg.h这个头文件(呵呵,本人也是看的不太懂)

typedef char * va_list;

#define va_start _crt_va_start 
#define va_arg _crt_va_arg 
#define va_end _crt_va_end

#define _crt_va_start(ap,v) ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) ) 
#define _crt_va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) – _INTSIZEOF(t)) ) 
#define _crt_va_end(ap) ( ap = (va_list)0 ) 
va_list argptr; 
C语言的函数是从右向左压入堆栈的,调用va_start后, 
按定义的宏运算,_ADDRESSOF得到v所在的地址,然后这个 
地址加上v的大小,则使ap指向第一个可变参数如图:

栈底 高地址 
| ……. 
| 函数返回地址 
| ……. 
| 函数最后一个参数 
| …. 
| 函数第一个可变参数 <–va_start后ap指向 
| 函数最后一个固定参数 
| 函数第一个固定参数 
栈顶 低地址

然后,用va_arg()取得类型t的可变参数值, 先是让ap指向下一个参数: 
ap += _INTSIZEOF(t),然后在减去_INTSIZEOF(t),使得表达式结果为 
ap之前的值,即当前需要得到的参数的地址,强制转换成指向此参数的 
类型的指针,然后用*取值

最后,用va_end(ap),给ap初始化,保持健壮性。

example:(chenguiming)

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

int average( int first, … ) //变参数函数,C++里也有 

int count=0,i=first,sum=0; 
va_list maker; //va_list 类型数据可以保存函数的所有参数,做为一个列表一样保存 
va_start(maker,first); //设置列表的起始位置 
while(i!=-1) 

sum+=i; 
count++; 
i=va_arg(maker,int);//返回maker列表的当前值,并指向列表的下一个位置 

return sum/count;

}

void main(void) 

printf( "Average is: %d\n", average( 2, 3, 4,4, -1 ) ); 
}

Linux下的stdarg.h

#ifndef _STDARG_H 
#define _STDARG_H

typedef char *va_list; /* 定义va_list 是一个字符指针类型*/

/* Amount of space required in an argument list for an arg of type TYPE. 
TYPE may alternatively be an expression whose type is used. */ 
/* 下面给出了类型为TYPE 的arg 参数列表所要求的空间容量。 
TYPE 也可以是使用该类型的一个表达式 */

// 下面这句定义了取整后的TYPE 类型的字节长度值。是int 长度(4)的倍数。 
#define __va_rounded_size(TYPE) \ 
(((sizeof (TYPE) + sizeof (int) – 1) / sizeof (int)) * sizeof (int))

// 下面这个函数(用宏实现)使AP 指向传给函数的可变参数表的第一个参数。 
// 在第一次调用va_arg 或va_end 之前,必须首先调用该函数。 
// 17 行上的__builtin_saveregs()是在gcc 的库程序libgcc2.c 中定义的,用于保存寄存器。 
// 它的说明可参见gcc 手册章节“Target Description Macros”中的 
// “Implementing the Varargs Macros”小节。 
#ifndef __sparc__ 
#define va_start(AP, LASTARG) \ 
(AP = ((char *) &(LASTARG) + __va_rounded_size (LASTARG))) 
#else 
#define va_start(AP, LASTARG) \ 
(__builtin_saveregs (), \ 
AP = ((char *) &(LASTARG) + __va_rounded_size (LASTARG))) 
#endif

// 下面该宏用于被调用函数完成一次正常返回。va_end 可以修改AP 使其在重新调用 
// va_start 之前不能被使用。va_end 必须在va_arg 读完所有的参数后再被调用。 
void va_end (va_list); /* Defined in gnulib *//* 在gnulib 中定义 */ 
#define va_end(AP)

// 下面该宏用于扩展表达式使其与下一个被传递参数具有相同的类型和值。 
// 对于缺省值,va_arg 可以用字符、无符号字符和浮点类型。 
// 在第一次使用va_arg 时,它返回表中的第一个参数,后续的每次调用都将返回表中的 
// 下一个参数。这是通过先访问AP,然后把它增加以指向下一项来实现的。 
// va_arg 使用TYPE 来完成访问和定位下一项,每调用一次va_arg,它就修改AP 以指示 
// 表中的下一参数。 
#define va_arg(AP, TYPE) \ 
(AP += __va
_rounded_size (TYPE), \ 
*((TYPE *) (AP – __va_rounded_size (TYPE))))

#endif /* _STDARG_H */

使用stdarg.h实现可变长度参数的更多相关文章

  1. (转)用库函数stdarg.h实现函数参数的可变

    原文地址:https://blog.csdn.net/jinkui2008/article/details/1967055 #define _INTSIZEOF(n)   ( (sizeof(n) + ...

  2. 可变参数函数(stdarg.h)的使用

    2013/5/3记录: stdarg.h是C语言中C标准函数库的头文件,stdarg是由standard(标准) arguments(参数)简化而来,主要目的为让函数能够接收可变参数.   stdar ...

  3. 第 16 章 C 预处理器和 C 库(可变参数:stdarg.h)

    /*------------------------------------------------- varargs.c -- use variable number of arguments -- ...

  4. #include<stdarg.h> 可变参数使用

    今天上计算方法这课时觉得无聊至极,于是拿出C++编程之道来看了看..无意之中看到了#include<stdarg.h> va_list,va_start,va_end等东西,不知是怎么用的 ...

  5. C 可变参数列表 stdarg.h

    内容来自<c和指针>,整理后方便个人理解 stdarg.h 菜鸟教程 - <stdarg.h> 类型 va_list 宏 va_start va_arg va_end #inc ...

  6. #include <stdarg.h>

    名称描述相容  // 作用描述 va_start使va_list指向起始的参数 va_arg检索参数C89 va_end释放va_list va_copy拷贝va_list的内容 实例解析: #inc ...

  7. C语言函数可变长度参数剖析

    C语言中的很多函数的入参被定义为可变参数,最典型的 int printf (const char * fmt, ...) 要对其中的可变参数进行处理,就要用到va_list类型和 VA_START, ...

  8. 《C标准库》——之<stdarg.h>

    C语言有个很强大的功能,依靠它,实现了printf等这类有着变长参数列表的函数或者宏.它就是在<stdarg.h>里的变长参数. 内容: va_list :它是一个适合保存va_start ...

  9. stdarg.h详解

    读Linux内核中的vsprintf函数的时候遇到了C语言的可变参数调用,查了挺多资料还是这篇比较详细,而且自己验证了下,确实如此 (一)写一个简单的可变参数的C函数  下面我们来探讨如何写一个简单的 ...

随机推荐

  1. Serializable 和 Parcelable 区别

    http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0204/2410.html http://www.cnblogs.com/blu ...

  2. android中xmlns:tools属性详解

    今天读到一篇总结的非常棒的文章,写的逻辑很清晰也很实用,很少见到如此棒的文章了.就原文转发过来,我把格式给整理了一下,分享给园子里的各位朋友!好久没写博客了,就为2015年的11月留份纪念吧.希望对你 ...

  3. 【原】MyEclipse8.5集成Tomcat7时启动错误:Exception in thread “main” java.lang.NoClassDefFoundError

    解决方法: MyEclipse->Window->Preferences->MyEclipse->Servers->Tomcat->Tomcat 6.x->L ...

  4. Web前端知识技能大汇总

    项目起源 还记得@jayli 的这幅前端知识结构图么. 图片的形式具有诸多的不便.缺失源图的我们,无法为此图贡献些什么,随着时间的迁移,或许有些技术点会发生改变,所以有了这个GitHub项目.我们可以 ...

  5. 如何启动ResourceManager和NodeManager

    登录到bigtop1上,vagrant ssh bigtop1 将/usr/lib/hadoop/libexec/init-hdfs.sh文件内容替换为: #!/bin/bash -ex # # Li ...

  6. 博为iHospital-HIS医院信息系统产品系列

    博为软件iHospital-HIS产品系列式面向大中型医院应用的完整医院流程信息化产品,覆盖了医院主要的业务流程,管理职能,和病人在医院诊疗的各个环节.将医院流程的优化与软件系统完美结合,为建立数字化 ...

  7. java单例之enum实现方式

    传统的两私有一公开(私有构造方法.私有静态实例(懒实例化/直接实例化).公开的静态获取方法)涉及线程安全问题(即使有多重检查锁也可以通过反射破坏单例), 目前最为安全的实现单例的方法是通过内部静态en ...

  8. 8 个最优秀的 Android Studio 插件

    Android Studio是目前Google官方设计的用于原生Android应用程序开发的IDE.基于JetBrains的IntelliJ IDEA,这是Google I/O 2013第一个宣布的作 ...

  9. 用Jquery load text文本到網頁遇到的問題

    HTML <div id="divText"></div> Javascript $('#divText').load(fileName ,function ...

  10. iis,w3wp一直出现WerFault.exe应用程序错误

    这个进程是Windows错误报告技术里的一个东西,来收集软件崩溃或者挂起后的数据然后向微软反馈报告.关闭系统的错误报告功能后看看 1:打开 运行 (热键:win+R)输入 gpedit.msc 打开  ...