可变参数浅析

来源:互联网 发布:华为机顶盒软件下载 编辑:程序博客网 时间:2024/06/05 22:31

在函数的原型中,列出了函数期望接受的参数,但原型只能显示固定数目的参数,那么,让一个函数在不同需求下接受不同的参数是不是可以呢?答案是肯定的,比如我们的printf函数就是一个参数可变的函数。下面以一个求取平均值程序为例,帮助我们理解可变参数列表

#define _CRT_SECURE_NO_DEPRECATE#include <stdio.h>#include <stdarg.h>#include <Windows.h>int average(int num, ...)//num代表第一个参数,...代表可变参数//{    /*char*p = &num;定义一个指针,并指向第一个形参地址,即指针初始化    int i = 0;    int sum = 0;    while (i <= num)    {        sum += *(int *)(p + 4 * i);从第一个形参地址处开始取值,直到取出所有数并求和        i++;    }//    va_list arg;//功能类似定义一个char*p    int sum = 0;    int n = 0;    va_start(arg, num);/* 初始化,找到第一个形参地址*/    while (n--)    {        sum += va_arg(arg, int); /*功能类似sum += *(int *)(p + 4 * i);*/    }    va_end(arg);/*arg=NULL*/    return sum / num;/*求平均值*/}int main(int argc,char*argv[],char *env[]){    int a = 2;    int b = 4;    int c = 6;    int avg1 = average(a, b, c);    int avg2 = average( a, b);    printf("avg1=%d\n", avg1);    printf("avg2=%d\n", avg2);    system("pause");    return 0;}

可变参数列表是通过宏来实现的。这些宏类似于stdarg.h头文件,他是标准库的一部分。这个头文件声明了一个类型va_list和三个宏———va_start,va_end,va_arg,有了这个宏以后,我们可以声明一个类型为va_list的变量(va_list arg类似一个定义一个 char *p),与这几个宏配合使用来访问参数的值
函数声明了一个va_arg用来访问参数列表中未确定的部分,这个变量通过调用va_start来初始化,他的第一个参数是va_list变量的名字,初始化过程把va_arg变量设置为指向可变参数部分的第一个参数,
为了访问参数,我们需要,需要使用va_arg,这个宏接受两个参数,va_list变量和可变参数列表中下一个参数的类型(在本案中,参数都是整形。而在有些函数中,你需要通过前面的数据来判断下一个参数了的类型)va_arg返回这个参数的值,并是va_arg指向下一个可变参数,最后,当访问完最后一个可变参数后,需要调用va_end,使访问过程结束。

可变参数函数调用要满足三个基本条件

  1. 参数列表中至少有一个命名参数,如果第一个连一个命名参数都没有,你就无法使用va_start。只有知道第一个命名参数,才能找到函数形参创建的起始位置,便于后面参数的查找与提取。
  2. 参数的类型,只有知道参数的类型才能正确的找到第一个形参后面的形参,这些宏无法判断参数的类型
  3. 参数的个数,决定指针查找参数这个过程在哪停止,函数实参个数有限,所以查过程也是有终点的,这些宏无法判断参数的数量。
    当这三个基本条件满足后,函数调用就可以进行了。这里需要注意的是可变参数形参是从右往左依次创建的,具体过程参照我的博客《深度理解函数》中有详细介绍。创建的结果就是最左边的参数地址最低。如下图a的地址最低,其次是b,c的地址最高,可以参照下图帮助理解

这里写图片描述
当指针找到第一个形参地址时并取数,(数据是连续存储的)往后查找取数时需要依次查找,由于参数类型是int型,所以指针依次要越过四字节才能找到下一个地址取数,直到找到最后一个参数地址取数,(参数运算略)对可变参数全部操作即结束

原创粉丝点击