参数个数可变函数的定义和调用
基础知识
参数个数可变函数是指函数每次被调用时可接收不同个数的实参。在定义和声明一个参数可变的函数时,形式参数的最后必须放置一个省略号。如printf函数的引用性声明为:
int printf(const char*format, ...);
其中const char*表示调用函数时必须至少有一个实参,该实参类型为char*;省略号...表示第一个形参之后的参数都是无名参数,无名参数的数量和类型时可变的。
头文件stdarg.h定义了数据类型va_list,该类型的数据对象用于存放省略号部分的参数。va_start是一个宏定义,用来把(…)部分的参数复制到程序中定义的va_list对象中。va_arg也是一个宏定义,用于访问(…)部分的参数。第一次调用访问(…)部分的第一个参数,第二次调用访问(…)部分第二个参数,以此类推。
VA_LIST的使用:
(1)首先在函数里定义一具VA_LIST型的变量,这个变量是指向参数的指针;
(2)然后用VA_START宏初始化变量刚定义的VA_LIST变量;
(3)然后用VA_ARG返回可变的参数,VA_ARG的第二个参数是你要返回的参数的类型(如果函数有多个可变参数的,依次调用VA_ARG获取各个参数);
(4)最后用VA_END宏结束可变参数的获取。
VA_LIST的实现(以VC6.0_x86为例):
typedef char * va_list; // TC中定义为void* #define _INTSIZEOF(n) ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) ) //为了满足需要内存对齐的系统 #define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) ) //ap指向第一个变参的位置,即将第一个变参的地址赋予ap #define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) ) //获取变参的具体内容,t为变参的类型,如有多个参数,则通过移动ap的指针来获得变参的地址,从而获得内容 #define va_end(ap) ( ap = (va_list)0 ) //清空va_list,即结束变参的获取
程序实现
例1 average函数计算并返回形参接收的n个浮点数的平均值,n的值由第一个形参接收。
#include<stdio.h> #include<stdarg.h> double average(int i,...); int main() { double j; j=average(2,1.5,2.5); printf("\naverage=%f\n",j); j=average(4,1.0,2.0,3.0,4.0); printf("\naverage=%f\n",j); } double average(int n,...) { int i; double y=0; va_list ap; va_start(ap,n); for(i=0;i<n;i++) y+=va_arg(ap,double); return y/n; }
#include <stdarg.h> int AveInt(int,...); void main() { printf("%d/t",AveInt(2,2,3)); printf("%d/t",AveInt(4,2,4,6,8)); return; } int AveInt(int v,...) { int ReturnValue=0; int i=v; va_list ap ; va_start(ap,v); while(i>0) { ReturnValue+=va_arg(ap,int) ; i--; } va_end(ap); return ReturnValue/=v; }
例2 模仿实现printf函数。
输出格式数据:
#include<stdio.h> #include<stdarg.h> int mprintf(const char *format, ...); int main() { mprintf("%d %f %c", 1, 2.3, 'a'); return 0; } int mprintf(const char*format,...) { int i, cnt = 0; double d; char c; char *p=NULL; va_list ap; va_start(ap, format); for (p = format; *p;p++){ if(*p!='%'){ putchar(*p); continue; } switch(*++p){ case 'd': i = va_arg(ap,int); printf("%d", i); cnt++; break; case 'f': d = va_arg(ap,double); printf("%f", d); cnt++; break; case 'c': c = va_arg(ap,int); printf("%c", c); cnt++; } } va_end(ap); return cnt; }
输出字符串:
#include<stdio.h> #include<stdarg.h> int my_printf(char* str,...) { va_list ap;//定义char* 变量 ap int count = 0; char* str_tmp = NULL; va_start(ap,str);//为ap进行初始化 while(*str != '\0') { switch(*str) { case 'c': putchar(va_arg(ap,int));//取下一个参数的字符,并打印 count++; break; case 's': str_tmp = (char*)va_arg(ap,int);//取下一个参数的地址,因为这个是字符串 while(*str_tmp != '\0')//利用解引用进行输出 { putchar(*str_tmp); count++; str_tmp++; } break; default: putchar(*str); //不为'c'或's',那么直接将它打印 count++; break; } str++; } va_end(ap);//将ap指向空,防止野指针 return count; } int main() { my_printf("s ccccc","hello",'w','o','r','l','d'); return 0; }
葡萄美酒夜光杯,欲饮琵琶马上催。

浙公网安备 33010602011771号