◎用法:
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!
}

◎研究:
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 */

转载于:https://www.cnblogs.com/mywolrd/archive/2008/11/19/1930716.html

C++:函数参数不确定时用cstdarg(stdarg.h)相关推荐

  1. python *args用法_python函数参数*args**kwargs用法实例

    #coding=utf8 __author__ = 'Administrator' # 当函数的参数不确定时,可以使用*args和**kwargs.*args没有key值,**kwargs有key值 ...

  2. python3函数参数(必选参数、默认参数、关键字参数、可变参数)

    python3函数参数 形参是参数在函数定义过程中的状态,这个过程中没有赋予实际的数值,实参是参数在函数调用过程中的状态,当参数被赋予实际的数值后,它会由形参转为实参. 必选参数在前,默认参数在后,默 ...

  3. python args函数_Python函数参数*args 和**kwargs的用法

    args就是所有参数的数组,kwargs就是当你传入key=value是存储的字典,当函数的参数不确定时,可以使用*args 和kwargs,*args 没有key值,kwargs有key值 def ...

  4. C-指针,二级指针,二维数组作为函数参数使用,C语言链表(详解)

    一级指针 int *p;            //表示定义一个int型(4字节)的指针p &p                 //表示p自身的地址位置 p                  ...

  5. 字符串函数参数传入传出(去空格)

    字符串作为函数参数传入传出 /*** delSpace.c ***/ #include<stdio.h> #include<string.h>int DelSpace(char ...

  6. 函数参数中带省略号的用法

     [转]函数参数中带省略号的用法 本文摘自CDSN<可变参数学习笔记>,原帖链接:http://topic.csdn.net/t/20041124/09/3582660.html 前言 ...

  7. C语言 数组作为函数参数

    1.数组元素作为函数实参 int a[10];  //相当于定义了10个变量,a[0]~a[9],那么数组元素就是:a[0]~a[9],数组元素就可以当成变量使用. #include<stdio ...

  8. Python 函数参数的分类及使用方法

    文章目录 一.参数的作用 二.参数的分类 1.必选参数 2.默认参数[省缺参数] 3.可选参数 4.关键字参数 三.可变参数与关键字参数混合使用 一.参数的作用 为了得到外部的数据,进而实现特定的功能 ...

  9. python 函数参数_python之函数(二)——函数参数详解

    今天,我们要学习的是函数中的一个重要的概念--参数. 废话不多说,直接进入正题,下面我们看一段简单代码: def add(a, b): # 1 形参 sum = a + b return sum a ...

最新文章

  1. JVM虚拟机参数配置官方文档
  2. 1357篇ECCV 2020论文打包下载!奖项公布:李飞飞高徒获最佳论文奖
  3. java游戏加入图片,急!急!求大神给这个小游戏加个背景图片
  4. python安装requests库pip不是_python pip安装requests时报错,怎么解决?
  5. java实现Kafka生产者示例
  6. linux创建更改目录,如何使用Linux中的单个命令创建新目录并更改它 | MOS86
  7. centos7创建asm磁盘_Oracle ASM 磁盘组基础知识整理(收藏版)
  8. 基于用户投票的排名算法(一):Delicious和Hacker News
  9. Snagit好用吗?Snagit 2022最全快捷键指南
  10. VS-001-概述-003-系统架构及业务流程--004-常见问题定位思路
  11. 闲来无事写写-Huffman树的生成过程
  12. MySQL数据库的一些基本语法
  13. 学习笔记 | 读完《公开募集证券投资基金销售机构监督管理办法》的几点感受
  14. NLP中文常用停用词表
  15. Sql Server 2005 64位安装包
  16. pythonpdf识别文字_python 提取pdf文字
  17. java实现根据身份证计算年龄的两种方式
  18. bugku-web-滑稽
  19. 高德地图地理编码和逆地理编码,以及逆地理编码的时候如何去掉省、市、镇
  20. 大数据下的空气监测如何改变你的生活方式?

热门文章

  1. spring中bean的高级属性之list, set, map以及props元素(含举例)
  2. 微信支付 - 构建商户端支付成功的回调接口
  3. 数据库-聚合函数-max函数
  4. Spring Boot整合@CacheEvict注解使用
  5. 函数--函数的快速体验
  6. 单一职责原理讲解coding
  7. Spring AbstractAutowireCapableBeanFactory
  8. 【Homework】说出 == 和 equals 的区别
  9. sketchup作品_18级园林工程技术专业课程实训作品展
  10. 如何使用 Java 中执行 Windows 的 CMD 命令