转载:https://www.cnblogs.com/bwangel23/p/4700496.html

这几个函数和变量是针对可变参数函数的,什么是可变参数函数呢,最经典的莫过于printf和scanf,这两个函数的声明如下:

 int printf(const char *format, ...);
int scanf(const char *format, ...);

这两个函数声明中省略号(...)表示的就是任意个数的参数,可变参数函数就是输入的参数的个数是可变的,那么这个具体是怎么实现的呢?

  要了解这个是怎么实现,首先我们就要先理解一点,参数是如何传递给函数的。众所周知,函数的数据是存放于栈中的,那么给一个函数传递传递参数的过程就是将函数的参数从右向左逐次压栈,例如:

  func(int i, char c, doube d)

  这个函数传递参数的过程就是将d,c,i逐次压到函数的栈中,由于栈是从高地址向低地址扩展的,所以d的地址最高,i的地址最低。

  理解了函数传递参数的过程,再来说一下va_list的原理,通常,可变参数的代码是这么写的:

 void func(char *fmt, ...)
{
va_list ap; va_start(ap, fmt);
va_arg(ap, int);
va_end(va);
}

这里ap其实就是一个指针,指向了参数的地址。

  va_start()所做的就是让ap指向函数的最后一个确定的参数(声明程序中是fmt)的下一个参数的地址。

  va_arg()所做的就是根据ap指向的地址,和第二个参数所确定的类型,将这个参数的中的数据提取出来,作为返回值,同时让ap指向下一个参数。

  va_end()所做的就是让ap这个指针指向0。

  关于这三个参数实现的宏可以参看下面的实现:

 // 使ap指向第一个可变参数的地址
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) ) // 使ap指向下一个可变参数,同时将目前ap所指向的参数提取出来并返回
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) ) // 销毁ap
#define va_end(ap) ( ap = (va_list)0 )

参考文章:

1. http://wenku.baidu.com/view/ecb33901de80d4d8d15a4fe3.html

最新文章

  1. jQuery源码分析系列(37) : Ajax 总结
  2. 手把手教你在ubuntu上安装apache和mysql和php
  3. WPF 资源字典【转】
  4. MFC ListContrl 的使用
  5. MySql变量,
  6. oracle11g 修改字符集
  7. Windows Phone8.1 SDK中的新控件
  8. 详解null
  9. phpcms:五、网站首页(index.html)
  10. 《windows程序设计》学习_2.1:初识消息
  11. 5.oracle建表的时候同时创建主键,外键,注释,约束,索引
  12. 转: .Net 4.0 ExpandoObject 使用
  13. LinkedList 利用的是尾插法
  14. C语言中的数组与指针
  15. 软件测试QA、QC、QM的关系与区别
  16. 从消费者角度评估RestFul的意义
  17. running boot2docker -> error in run: Failed to get machine “boot2docker-vm”: machine does not exist
  18. iOS UIScrollView 3种分页方法,间隔实现
  19. FineUI 修改config表属性
  20. 15-hadoop-eclipse插件的安装

热门文章

  1. javaScript中的toFix(n)方法
  2. UDP协议 sendto 和 recvfrom 浅析与示例
  3. 解决idea无法下载通过maven添加的jar包以及下载网速过慢的问题
  4. java 反射获取设置私有成员变量的值
  5. 前端框架vue学习笔记:环境搭建
  6. python实现网页登录时的rsa加密流程
  7. MYSQL客户端管理工具
  8. Linux下Nginx1.9.9的安装
  9. 使用pycharm创建Django项目,'django-admin' 不是内部或外部命令
  10. docker学习及应用