可变参数列表的实现
2015-05-19 20:15
155 查看
在学习C语言的过程中,大家是不是和我一样,认为printf是一个神一样的函数?他可以接受不同数目,不同类型的参数,他到底是怎么实现的呢?
让我们去看一下它的源代码:
printf源代码:
它采用的是可变参数列表,可变参数列表主要有以下两个缺点:
1)无法确定可变参数列表的长度,这也是printf提供通配符的原因所在;
2)不能提供类型检查,在实参向形参的拷贝过程中可能会出现问题,一般建议只传递基本数据类型
一个类型:va_list ;三个宏:va_start ;
首先介绍一个用可变参数列表实现的一个函数,在分别介绍他们(一类型,三宏)
1、va_list
1)用法:
如上,定义了一个 va_list类型的变量 arg,可以用该变量作为保存可变参数列表的指针。实际使用中,如果把可变参数列表的入参看做一个数组 array,那么这个 va_list 就相当于其迭代器 iterator。
这个宏本身只是个定义,真正赋予其意义的在于下面的几个宏。
2)实现:实际上只是一个 char * 类型的指针,原因这里不能判定类型,所以用size为1的char类型指针会方便移动。
2、va_start
1)用法:
这个宏需要两个参数,第一个是上面定义的 va_list, 第二个是可变参数列表之前的那个参数。
作用就是使得 va_list 的变量指向可变参数列表的首地址。这才是一般意义上的对 va_list的初始化。
2)实现:
很容易看懂,就是将ap指向v后的地址。
3)注意:
按照规范,va_start中的参数,一定要是最后一个参数,也就是...之前的那个参数,不然可能会有问题。尤其是windows和linux的函数参数入栈顺序不同,会有可移植性问题
3、va_arg
1)用法:
同样有两个参数,第一个是前面va_list已经初始化好的变量arg ,第二个参数是类型,比如这里可变参数列表的第一个参数是int类型,那么就传int。
这个函数实现了类似于迭代器的功能,他的返回值是当前itor指向的 int类型值(类型是第二个参数所描述的),同时会移动 itor,使得其指向可变参数列表的下一个参数。
4、va_end
1)用法:
2)实现:这个宏实际上是个空实现,更多是提高代码的可读性,同时方便后面的扩展。
5、缺少的....
作为一个迭代器,缺少的最关键一环就是判定结尾,可惜这里是没法提供的。还是老老实实的结尾传个NULL吧。
让我们去看一下它的源代码:
printf源代码:
int printf(const char *fmt,...) { int res; va_list arg; va_start(arg,fmt); res = vprintf(fmt,arg); va_end(arg); return res; }
它采用的是可变参数列表,可变参数列表主要有以下两个缺点:
1)无法确定可变参数列表的长度,这也是printf提供通配符的原因所在;
2)不能提供类型检查,在实参向形参的拷贝过程中可能会出现问题,一般建议只传递基本数据类型
一个类型:va_list ;三个宏:va_start ;
首先介绍一个用可变参数列表实现的一个函数,在分别介绍他们(一类型,三宏)
#include<stdarg.h> #include<iostream> using namespace std; /*可变参数列表实现求一组不确定数目数值的和*/ int sum(int n,int v1,...) { va_list arg; va_start(arg,v1); int sum = v1; for(int i=0;i<n-1;++i)/*第一个数一开始就加入sum中,所以剩余n-1个数,所以需要加n-1次*/ { int tmp = va_arg(arg,int); sum += tmp; } va_end(arg); return sum; } int main() { int ret = sum(4,1,2,3,4); cout<<ret<<endl; return 0; }
1、va_list
1)用法:
int sum(int n,int v1,...) { va_list arg; 。。。。。。。 }
如上,定义了一个 va_list类型的变量 arg,可以用该变量作为保存可变参数列表的指针。实际使用中,如果把可变参数列表的入参看做一个数组 array,那么这个 va_list 就相当于其迭代器 iterator。
这个宏本身只是个定义,真正赋予其意义的在于下面的几个宏。
2)实现:实际上只是一个 char * 类型的指针,原因这里不能判定类型,所以用size为1的char类型指针会方便移动。
2、va_start
1)用法:
int sum(int n,int v1,...) { va_list arg; va_start(arg,v1); 。。。。。。 }
这个宏需要两个参数,第一个是上面定义的 va_list, 第二个是可变参数列表之前的那个参数。
作用就是使得 va_list 的变量指向可变参数列表的首地址。这才是一般意义上的对 va_list的初始化。
2)实现:
#define va_start ( ap, v ) ( ap = (va_list)&v + _INTSIZEOF(v) )
很容易看懂,就是将ap指向v后的地址。
3)注意:
按照规范,va_start中的参数,一定要是最后一个参数,也就是...之前的那个参数,不然可能会有问题。尤其是windows和linux的函数参数入栈顺序不同,会有可移植性问题
3、va_arg
1)用法:
int tmp = va_arg(arg,int);
同样有两个参数,第一个是前面va_list已经初始化好的变量arg ,第二个参数是类型,比如这里可变参数列表的第一个参数是int类型,那么就传int。
这个函数实现了类似于迭代器的功能,他的返回值是当前itor指向的 int类型值(类型是第二个参数所描述的),同时会移动 itor,使得其指向可变参数列表的下一个参数。
4、va_end
1)用法:
va_end(arg);
2)实现:这个宏实际上是个空实现,更多是提高代码的可读性,同时方便后面的扩展。
5、缺少的....
作为一个迭代器,缺少的最关键一环就是判定结尾,可惜这里是没法提供的。还是老老实实的结尾传个NULL吧。
相关文章推荐
- C语言之利用可变参数列表实现简易的printf
- 可变参数列表详解(内附求平均数和实现简单的printf)
- 可变参数列表及printf函数的实现
- 可变参数列表函数实现
- c语言,可变参数列表实现任意个数求平均值
- 可变参数列表的实现--平均值
- 用可变参数列表模拟实现printf函数
- 使用varargh.h来实现可变参数列表以及varargh.h实现
- 用可变参数列表模拟实现printf函数
- 【c语言】实现可变参数列表
- 可变参数列表及printf函数的实现
- 创建函数利用可变参数列表的形式模拟实现printf的功能
- [C语言]可变参数列表和简单模拟实现printf
- 可变参数列表的实现
- Linux 源码系列之可变参数列表实现
- C自定义函数的可变参数列表实现 Windows APIS目录遍历程序 [李园7舍_404]
- 可变参数列表通过宏来实现(头文件stdarg.h)
- 将参数字符串中的字符反向排列(递归实现),可变参数列表函数实现
- 利用可变参数列表简单实现printf函数的功能
- 可变参数列表解析理解及使用可变参数实现求平均值