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

◎用法: 
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. css之属性部分

    这篇写的是今天的学习到的属性,一共20个. 属性再多,但也要会使用,会在使用时可以记起它,才能起到它为我们所需要的作用. 样式属性 1.border CSS边框属性允许你指定一个元素边框的样式和颜色. ...

  2. Bruce Eckel:编程生涯(转载)

    Bruce Eckel:编程生涯(转载) 说明:Bruce Eckel 著有大名鼎鼎的<Thinking in C++>和<Thinking in Java>.本文是他对程序员 ...

  3. Bootstrap3.0入门学习系列规划[持续更新]

    详情请看http://aehyok.com/Blog/Detail/5.html 个人网站地址:aehyok.com QQ 技术群号:206058845,验证码为:aehyok 本文文章链接:http ...

  4. .net下BerkeleyDB操作封装C#版(附单元测试)

        using System; using System.Collections.Generic; using System.IO; using System.Linq; using System ...

  5. 【迁移】—Entity Framework实例详解

    好久没有在博客园更新博客了,如今都换了新公司.前段时间写了关于EF迁移的文档,今天拿出来作为这个系列的一篇吧. 一.Entity Framework 迁移命令(get-help EntityFrame ...

  6. linux crontab 文件位置和日志位置

    一.文件位置 位置一般在/var/spool/cron/下,如果你是root用户,那下面有个root文件,建议日常备份,避免误删除导致crontab 文件丢失: 二.日志文件位置 默认情况下,cron ...

  7. JS 获取自定义标签

    <abc-aaa xwe='sdf'>AAAAAAAAAAAAAAAAAAAAAA</abc-aaa> alert($("abc-aaa").attr(&q ...

  8. 1、Orchard商城开发——开发需求

    需要开发的功能: 1.商品详情,可添加商品属性,如颜色,尺寸等. 2.商品类别,可显示该类别下的所有商品,可按品牌.颜色.尺寸等检索,并可按价格.销量等排序游览. 3.商品游览记录,收藏商品,加入购物 ...

  9. U深度利用iso文件制作U盘启动盘

    利用U盘装win10系统: 工具:U深度装机版   文件:win10.iso 步骤1:下载U深度装机版安装 步骤2:打开U深度,制作U盘启动盘,注意选择iso模式,如下图所示 接下来下一步即可,工具会 ...

  10. zookeeper Watcher API 说明

    Watcher 在 ZooKeeper 是一个核心功能,Watcher 可以监控目录节点的数据变化以及子目录的变化,一旦这些状态发生变化,服务器就会通知所有设置在这个目录节点上的 Watcher,从而 ...