浅析可变参数
来源:互联网 发布:深圳软件协会会长 编辑:程序博客网 时间:2024/06/12 21:13
在函数原型中,列出了函数期望的参数,但原型只能显示固定数目的参数,在求平均数的时候,如果数字有少数的几个,那么我们可以依次给函数传参,可是,如果数字多的话,难道要写出每个形参吗?那么让一个函数在不同时候接受不同数目的参数是否可以呢?答案是ok的,因为C语言很聪明,提供了一系列的可变参数来解决这个问题。
1,stdarg宏
可变参数可变参数列表是通过宏来实现的,这些宏定义在stdarg.h头文件中。这个头文
件声明了一个类型va_list和三个宏va_start,va_arg和va_end。
(1)va_list:声明变量,用于访问参数列表的未确定部分;
(2)va_start:初始化va_list申明的变量,它的第一个参数是va_list变量的名字,第二个参数是省略号前最后一个有名字的参数。通过初始化把va_list变量设置为指向可变参数部分的第一个参数;
(3)va_arg:用于访问参数,它的第一个参数是va_list变量的名字,第二个参数是参数列表中下一个参数的类型。在有些函数中,参数类型都是一样的,但有些函数可能要通过前面获得的数据来判断下一个参数的类型。va_arg返回这个参数的值,并使va_list变量指向下一个可变参数,如果此时已经是最后一个参数,则指向NULL。
(4)va_end:当访问最后一个可变参数之后,用va_end置空va_list变量.
注意:可变参数必须从头到尾按照顺序逐个访问,但也可以在访问几个参数之后半途中止。但是,不能从中间开始访问。此外,由于参数列表中的可变参数部分并没有原型,所以,所有作为可变参数传递给函数的值都将执行缺省参数类型提升。
2.代码实现:
实现可变参数函数:average,求平均值。
#include <stdio.h>#include <stdarg.h>#include <stdlib.h>int average(int n,...){ va_list arg; int i = 0; int sum = 0; va_start(arg,n); //以固定参数的地址为起点确定可变参数的内存起始地址 for(i=0; i<n; i++) { sum += va_arg(arg,int);//得到下一个可变参数的值 } va_end(arg); return sum/n;}int main(){ int ret = 0; ret = average(5,1,2,3,4,5); printf("%d\n",ret); return 0;}
实现可变参数函数:Max,求最大值。
#include <stdio.h>#include <stdarg.h>#include <stdlib.h>int Max(int n,...){ va_list arg; int i = 0; int max = 0; va_start(arg,n);//</span><span style="line-height: 26px; font-family: 宋体;"><span style="font-size:18px;">以固定参数的地址为起点确定可变参数的内存起始地址</span></span><span style="font-size:24px;"> for(i=0; i<n; i++) { int tmp = 0; tmp = va_arg(arg,int); //得到下一个可变参数的值 if(tmp > max) max = tmp; } va_end(arg); return max;}int main(){ int ret = 0; ret = Max(5,1,2,3,4,5); printf("%d\n",ret); system("pause"); return 0;}
3.可变参数在编译器中的处理
typedef char * va_list;#define _INTSIZEOF(n) ((sizeof(n) + sizeof(int) - 1) (sizeof(int) - 1) )#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )#define va_end(ap) ( ap = (va_list)0 )
代码的含义:
(1)首先把va_list被定义成char*,这是因为在我们目前所用的PC机上,字符指针类型可以用来存储内存单元地址。而在有的机器上va_list是被定义成void*的
(2)定义_INTSIZEOF(n)主要是为了某些需要内存的对齐的系统.这个宏的目的是为了得到最后一个固定参数的实际内存大小。在我的机器上直接用sizeof运算符来代替,对程序的运行结构也没有影响。(后文将看到我自己的实现)。
(3)va_start的定义为 &v+_INTSIZEOF(v)
,这里&v是最后一个固定参数的起始地址,再加上其实际占用大小后,就得到了第一个可变参数的起始内存地址。所以我们运行va_start(ap,v)以后,ap指向第一个可变参数在的内存地址.(4)va_arg():有了va_start的良好基础,我们取得了第一个可变参数的地址,在va_arg()里的任务就是根据指定的参数类型取得本参数的值,并且把指针调到下一个参数的起始地址。
(5)va_end宏的解释:ap=(char*)0.
4.实现简单的printf可变参数的函数。
#include <stdio.h>#include <stdarg.h>#include <stdlib.h>int my_printf(char *val,...){ va_list arg; va_start(arg,val); while(*val) { if(*val == 'c') putchar(va_arg(arg,char));//输出字符 else if(*val == 's') puts(va_arg(arg,char*));//输出字符串 val++; } va_end(arg);}int main(){ my_printf("s ccc","hello", 'b','i','t'); return 0;}
注意:如果在va_arg中指定了错误类型,其结果不可预测。
- 浅析可变参数列表
- 浅析可变参数
- 浅析可变参数列表
- 浅析可变参数
- 可变参数列表浅析
- 可变参数浅析
- 浅析可变参数列表
- 参数个数可变函数浅析
- 浅析可变参数列表的使用
- 浅析可变参数列表及实例分析
- C语言中可变参数函数实现原理浅析
- C语言中可变参数函数实现原理浅析
- 可变参数
- 可变参数
- 可变参数
- 可变参数
- 可变参数
- 可变参数
- 听唐僧谈用人之道
- hibernate简介及原理
- bzoj 3560 DZY Loves Math V
- opencv StereoBM各个参数的意义
- 华为:MateBook会失败?不存在的
- 浅析可变参数
- 模型/视图实战之仿QQ好友列表面板
- java MessagePack的数据处理
- php接收post原始数据
- 计算a+b的和
- mysql存储过程,结果集遍历,if -else
- 利用同一个Activity界面实现二个不同的界面之间的跳转
- Android开发之高级渲染
- LevelDB:一个快速轻量级的key-value存储库(译)