可变参数列表

来源:互联网 发布:数据存储 信息安全 编辑:程序博客网 时间:2024/06/05 04:52

可变参数列表

C语言中,我们经常使用的printf函数,是可以输入任意多个参数的,而且当我们要写一个函数去求平均值或者其它,我们有时候要求两个数的平均值,有时候要求三个数的平均值,如果每次都封装一个函数,就很麻烦,所以C语言提供了可变参数

下面是一段求平均值的代码:

#include <stdio.h>#include <stdarg.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);}                                             return sum/n;va_end(arg);   }
可以看到其中有几个特别的地方,va_list,va_start,va_arg,va_end

打开头文件stdarg.h


#pragma once#endif#ifndef _INC_STDARG#define _INC_STDARG#if     !defined(_WIN32)#error ERROR: Only Win32 target supported!#endif#include <vadefs.h>#define va_start _crt_va_start#define va_arg _crt_va_arg#define va_end _crt_va_end


可以看到我们使用的va_start,va_arg等都是封装好的宏,再打开<vadefs.h>查看宏具体的实现
typedef char *  va_list;

va_list就是char*,代码中我们声明一个va_list类型的变量arg,用来访问参数列表的未确定部分

#define _INTSIZEOF(n)   ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )#define _crt_va_start(ap,v)  ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )#define _crt_va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )#define _crt_va_end(ap)      ( ap = (va_list)0 )

va_start是用来初始化arg变量,把arg变量初始化为可变参数部分的最后一个参数,从源码中可以看到ap也就是arg,直接跳到可变参数列表的第一个参数.具体什么意思,学识有限

va_arg的第一个参数是arg,第二个参数是下一个参数的类型,所以每次让ap向后跳一个类型的位置

va_end 感觉没什么用,可能只是结束标志


既然是个函数,在调用过程中就要形成栈帧结构,既然要形成栈帧结构,就要知道变量的位置,如果只有 average( ... ),你传入参数,临时参数该怎么形成呢?所以,可变参数列表就必须有一个参数,这样的话根据栈帧结构,我们就可以找到后面所有参数的地址


参数列表的限制

可以看出,可变参数列表是一个参数一个参数的访问,如果你想直接访问后面的元素是做不到的,但是你想访问到中途终止,这是可行的。

因为要找到其它的参数,所以参数列表中至少有一个参数,如果连一个参数都没有,就没法初始化

宏是无法直接判断实际参数的数量的

宏无法判断每个参数的类型

如果va_arg中指定了错误的类型,那么其后果是不可预测的



函数调用:

int main(){printf("%d\n",average(3, 10, 15, 20));return 0;}

运行结果:


原创粉丝点击