C语言可变参数设计的理解

原创 2013年12月03日 22:44:52
曾经看过C语言可变参数函数的设计原理,今天与同事交流的时候居然没有话题,全忘了。赶快记下来。顺便做一些理解。
为了使用C可变参数的一整套机制,第一步就是要包含其头文件
#include <stdarg.h>
这样前提工作就做好了,免得当在你自己的可变函数内部定义
va_list var_ptr
报出以下错误
error: 'va_list' was not declared in this scope
然后,就让我们使用可变参数相关的变量实现功能吧。我在此也不想过多的描写可变参数相关变量的使用方法,只想描述一下它的实现原理。
首先还是看看几个变量的实现
va_list atg_ptr;
typedef char* va_list;//va_list就是一个char*的类型
va_start()宏函数的定义如下
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) ) 
按照va_start()的使用方法,这个v就是你的可变参数函数中最后一个固定参数,比如说printf()函数,就是双引号“”中的参数。
这个宏函数的作用按照字面理解就是使ap指向固定参数v后面那个参数。既然是后面那个,到底是哪个呢?这就由偏移值_INTSIZEOF(v)决定了
_INTSIZEOF()也是个宏函数,它的定义如下
#define _INTSIZEOF(n) ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) )
一眼看上去真不理解是啥意思,不过将这个宏单独拿出来做了几个实验之后发现,它的返回值是4的倍数,例如4,8。
好了,到目前为止va_start()的作用介绍完毕了,接下来看看下一个函数va_arg()
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) ) 

这个宏有点复杂,因为()太多了,剥离一下看看

ap += _INTSIZEOF(t);//先使ap指向下一个参数
*(t*)(ap - _INTSIZEOF(t))//在用已经指向下一个参数的指针减去偏移量取值
哦,原来是这个意思,每一次调用va_arg(),按照效果来看是先返回当前的值,再把指针指向下一个参数
最后,当参数全部取完了,该做收尾工作了调用
va_end()
#define va_end(ap) ( ap = (va_list)0)
使指针指向0,防止野指针
以上是关于可变参数相关变量的使用说明,有以下几点需要注意的。
1.可变参数重在一个“变”字,那么如何控制变的数目呢?必须指定可变参数的数目,所以可变参数前固定参数中必须要有一个变量用来指定可变参数的数目。
对于printf()函数,它的可变参数的数目是由第一个参数字符串“”中%d,%f,%c等的数目决定的。
2.为什么va_start()的宏实现中是"+"号而不是减号?因为当前C语言函数入栈的顺序是从右往左的,最有边的参数在高地址,越往左参数所在的地址越低。形如下图

void fun(int a,int b,int c) {}
           a		  b		          c
.....|___________|____________|_____________|.......
   0x01     0x02	        0x03	       0x04    地址
假定a是固定参数,b和c是可变参数,如果知道了a的地址,那么b和c的地址只要加上适当的偏移就可以得到了。
3._INTSIZEOF()做了一个什么事?这个宏估计就是为了实现一个准则:如果一个函数的形参类型未知, 例如使用了Old Style C风格的函数声明,或者函数的参数列表中有 ...,那么调用函数时要对相应的实参做Integer Pormotion,此外,相应的实参如果是float型的也要被提升为double类型,这条规则称为Default Argument Promotion"。关于float类型的提升,我猜printf()源码里面势必有这样的逻辑
if(*arg == 'f')
{
     double tmp;
     tmp = va_arg(va_ptr,double);
}
这将是下一个研究的地方


谢谢观赏!-_-|||排版太搓了,见谅。

全面解析C语言中可变参数列表

全面解析c语言中可变参数列表问题:1、大众版:大家习惯的使用方式;2、真实版:真正的使用方式;3、实际原理:具体原理,本质;4、自己实现可变参数列表:理解本质后,自己用自己的方法来实现可变参数列表;5...
  • YuZhiHui_No1
  • YuZhiHui_No1
  • 2015年02月11日 15:27
  • 7047

C语言可变参数的函数实现

在C语言中,有一种参数个数、类型不固定的函数,称之为变参函数,比如常用的printf函数。当我们在输出log信息时,也希望能写一个变参函数作为接口。这里介绍下如何写变参函数。  一、参数宏 ...
  • hzyong_c
  • hzyong_c
  • 2011年03月15日 23:17
  • 4030

C语言之传递可变参数

C语言之传递可变参数文章链接:知识点 传递int可变参数个数; 传递char类型可变参数个数; 传递logn类型可变参数个数; 传递可变参数个数特例; 新名词记录{stdarg.h, va_list,...
  • qq_16628781
  • qq_16628781
  • 2017年05月24日 22:18
  • 366

C语言可变参数全解

cstdarg(stdarg.h) 可变参数的处理     这个头文件定义了一些宏,可以通过这些宏来逐个访问被调函数的没有名字的参数列表,这些参数的个数 和类型都是不知道的。     一个函数...
  • chenlong12580
  • chenlong12580
  • 2012年04月25日 10:06
  • 1554

C语言可变参数宏定义方法

在GNU C中,宏可以接受可变数目的参数,就象函数一样,例如:  1 2 #define pr_debug(fmt,arg...) \ ...
  • skyflying2012
  • skyflying2012
  • 2014年08月08日 11:21
  • 3942

C语言可变参数在宏定义中的应用

在C语言的标准库中,printf、scanf、sscanf、sprintf、sscanf这些标准库的输入输出函数,参数都是可变的。在调试程序时,我们可能希望定义一个参数可变的输出函数来记录日志,那么用...
  • xyang81
  • xyang81
  • 2014年11月17日 23:06
  • 4161

C语言可变参数函数的使用方法讲解

http://blog.chinaunix.net/uid-21736802-id-1817658.html 本文主要介绍可变参数的函数使用,然后分析它的原理,程序员自己如何对它们实现和封装...
  • zangchaodotcnatgmail
  • zangchaodotcnatgmail
  • 2015年12月28日 11:02
  • 1251

C语言中可变参数va_list/va_start/value_arg/va_end的理解

va_list/va_start/va_arg/va_end这几个宏,都是用于函数的可变参数的。 我们来看看在vs2008中,它们是怎么定义的: 1: ///stdarg.h 2: ...
  • zx642288904
  • zx642288904
  • 2017年03月07日 16:20
  • 400

[C语言]利用可变参数列表求平均值。

题目:设计一个函数可以求任意多个数的平均值(使用可变参数列表) 思路: 利用可变参数列表即可,可变参数列表是通过宏来实现的,这些宏定义在stdarg.h中。注意的是可变参数列表传参时,第一个传的参...
  • qq1010234991
  • qq1010234991
  • 2016年06月08日 00:16
  • 401

C语言学习小结--参数个数可变的函数

先来回顾一下C语言固定参数个数的函数形式:int func(int a, char b);可见,该函数有2个参数,分别是int型变量a和char型变量b。但是为什么标准输入输出库里面的printf函数...
  • mr_bean_1031
  • mr_bean_1031
  • 2017年07月23日 15:30
  • 242
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:C语言可变参数设计的理解
举报原因:
原因补充:

(最多只允许输入30个字)