可变参数列表解析

来源:互联网 发布:手机尤克里里打谱软件 编辑:程序博客网 时间:2024/05/17 06:15

在函数的原型中,列出了函数期望接受的参数,但原型只能显示固定数目的参数。让一个函数在不同的时候接受不同数目的参数是不是可以呢?
首先我们来看看main函数的原型:

int main( int argc, char *argv[ ] , char *envp[ ] ){program-statements}

第一个参数: argc是个整型变量, 表示命令行参数的个数(含第一个参数) 。
第二个参数: argv是个字符指针的数组, 每个元素是一个字符指针, 指向一个字
符串。 这些字符串就是命令行中的每一个参数(字符串) 。
第三个参数: envp是字符指针的数组, 数组的每一个原元素是一个指向一个环境
变量(字符串) 的字符指针。

下面我们来看一个例子:
使用可变参数,实现函数求未知参数部分n个数的平均值。

#include<stdio.h>#include<stdlib.h>#include<string.h>#include<stdarg.h>//可变参数列表是通过宏实现的,这些宏定义于stdarg.h头文件中int average(int n,...)//省略号提示此处可能传递数量和类型未确定的参数{    va_list arg;  //相当于char*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=average(5, 1,2,3,4,5);    printf("ret=%d\n",ret);    return 0;

这里写图片描述

注:
stdarg.h宏是标准库的一部分,这个头文件声明了一个类型va_list和三个宏——va_start,va_arg和va_end。我们可以声明一个类型为va_list的变量,与这几个宏配合使用,访问参数的值。
对上面代码分析:
这里写图片描述
这里写图片描述

可变参数的实现过程是使用宏的封装。 只要完成替换, 我们就可以自行分析了。va_start(arg,n);转到定义#define _crt_va_start(ap,v)  ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )替换后:#define va_start(arg,n)  (arg=(va_list)&n+ _INTSIZEOF(n) //向上取整sum+=va_arg(arg,int);转到定义#define _crt_va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )替换后:sum+=(*(int*)((arg+=4)-4));

同理使用可变参数,实现函数求未知参数部分n个数中的最大值。

#include<stdio.h>#include<stdlib.h>#include<string.h>#include<stdarg.h>int Max(int n,...){    int i=0;    int max=0;    int tmp=0;    va_list arg;    va_start(arg,n);    for(i=0;i<n;i++)    {        if(max<(tmp=va_arg(arg,int)))        {            max=tmp;        }    }    return tmp;}int main(){    int max=Max(5, 1,2,3,4,5);    printf("%d\n",max);    return 0;}

这里写图片描述

可变参数的限制
1,可变参数必须从头到尾逐个访问。 如果你在访问了几个可变参数之后想半途,终止, 这是可以的, 但是, 如果你想一开始就访问参数列表中间的参数, 那是不行的。
2,参数列表中至少有一个命名参数。 如果连一个命名参数都没有, 就无法使用va_start。
3,这些宏是无法直接判断实际存在参数的数量。
4,这些宏无法判断每个参数的是类型。
5,如果在va_arg中指定了错误的类型, 那么其后果是不可预测的。

原创粉丝点击