在阅读别人的代码时遇到va_list这个符号,一时之间想不起来怎么理解,只依稀记得这是初学c时遇到的知识点
翻开教材书才知道这叫做可变参数,由stdarg.h头文件包含
《C Primer Plus》中这样描述
stdarg.h头文件为函数提供了一个类似的(前文提到的变参宏)功能, 但是 用法 比较 复杂。 必须 按 如下 步骤 进行:
1. 提供 一个 使用 省略 号的 函数 原型;
2. 在 函数 定义 中 创建 一个 va_ list 类型 的 变量;
3. 用 宏 把 该 变量 初始 化为 一个 参数 列表;
4. 用 宏 访问 参数 列表;
5. 用 宏 完成 清理 工作。
例如:
void f1( int n, ...); // 有效
int f2( const char * s, int k, ...); // 有效
char f3( char c1, ..., char c2); // 无效, 省略号 不在 最后
double f3(...); // 无效,没 有形 参 最 右边 的 形 参( 即 省略 号的 前 一个 形 参) 起着 特殊 的 作用, 标准 中用 parmN 这个 术语 来 描述 该 形 参。
在上 面的 例子 中, 第 1 行 f1() 中 parmN 为 n, 第 2 行 f2() 中 parmN 为 k。
传 递给 该 形 参 的 实际 参数 是 省略号 部分 代表 的 参数 数量。
例如, 可以 这样 使用 前面 声明 的 f1() 函数:
f1( 2, 200, 400); // 2 个 额外 的 参数
f1( 4, 13, 117, 18, 23); // 4 个 额外 的 参数
接下来,声明 在 stdarg. h 中的 va_ list 类型 代表 一种 用于 储存 形 参 对应 的 形 参 列表 中 省略号 部分 的 数据 对象。 变参 函数 的 定义 起始 部分 类似 下面 这样:
double sum( int lim,...)
{
va_ list ap; /*声明 一个 储存 参数 的 对象 在 该 例 中, lim 是 parmN 形 参, 它 表明 变参 列表 中 参数 的 数量。*/
...
}
宏: va_start va_start va_end va_copy
这是网上的某些文章写的解释
va_start
首先在函数中定义一个具有va_list型的变量,这个变量是指向参数的指针。
然后用va_start宏初始化变量刚定义的va_list变量,使其指向第一个可变参数的地址。
然后va_arg返回可变参数,va_arg的第二个参数是你要返回的参数的类型(如果多个可变参数,依次调用va_arg获取各个参数)。
对ap进行了一系列的初始化,通过va_start初始化ap,之后ap会被va_arg和va_end使用到。lim是可变参数列表的前一个参数,指代参数列表的元素数量。而在fun(char *fmt, …) 中 的fmt。如果fmt=“%s%c”,则参数列表元素也应该是两个(前一个是string类型后一个是char类型)。
例如:
接着 上面 的 例子 讨论, va_ list 类型 的 变量 是 ap, parmN 形 参 是 lim。 所以, 应 这样 调用 它:
va_ start( ap, lim); // 把 ap 初始 化为 参数 列表
下一步 是 访问 参数 列表 的 内容, 这 涉及 使用 另一个 宏 va_ arg()。 该 宏 接受 两个 参数: 一个 va_ list 类型 的 变量 和 一个 类型 名。 第 1 次 调用 va_ arg() 时, 它 返回 参数 列表 的 第 1 项; 第 2 次 调用 时 返回 第 2 项, 以此类推。 表示 类型 的 参数 指定 了 返回 值 的 类型。
如果 参数 列表 中的 第 1 个 参数 是 double 类型, 第 2 个 参数 是 int 类型, 可以 这样做:
double tic;
int toc;
...
tic = va_ arg( ap, double); // 检索 第 1 个 参数
toc = va_ arg( ap, int); //检索 第 2 个 参数
注意, 传入 的 参数 类型 必须 与 宏 参数 的 类型 相 匹配。 如果 第 1 个 参数 是 10. 0, 上面 tic 那 行 代码 可以 正常 工作。 但是 如果 参数 是 10, 这 行 代码 可能 会 出错。 这里 不会 像 赋值 那样 把 double 类型 自动 转换 成 int 类型。
最后, 要 使用 va_ end() 宏 完成 清理 工作。 例如, 释放 动态 分配 用于 储存 参数 的 内存。 该 宏 接受 一个 va_ list 类型 的 变量: va_ end( ap); 清理 工作 调用 va_ end( ap) 后, 只有 用 va_ start 重新 初始化 ap 后, 才能 使用 变量 ap。 因为 va_ arg() 不提 供 退回 之前 参数 的 方法, 所以 有 必要 保存 va_ list 类型 变量 的 副本。所以 有 必要 保存 va_ list 类型 变量 的 副本。 C99 新增 了 一个 宏 用于 处理 这种 情况: va_ copy()。 该 宏 接受 两个 va_ list 类型 的 变量 作为 参数, 它 把 第 2 个 参数 拷贝 给 第 1 个 参数
实例:
程序 清单 16. 21
varargs. c 程序 //varargs. c -- use variable number of arguments
#include < stdio. h>
#include < stdarg. h>
double sum( int, ...);
int main( void)
{
double s, t;
s = sum( 3, 1. 1, 2. 5, 13. 3);
t = sum( 6, 1. 1, 2. 1, 13. 1, 4. 1, 5. 1, 6. 1);
printf(" return value for " "sum( 3, 1. 1, 2. 5, 13. 3): %g\ n", s);
printf(" return value for " "sum( 6, 1. 1, 2. 1, 13. 1, 4. 1, 5. 1, 6. 1): %g\ n", t);
return 0;
}
double sum( int lim, ...)
{
va_ list ap; // 声明 一个 对象 储存 参数
double tot = 0;
int i;
va_ start( ap, lim); // 把 ap 初始 化为 参数 列表
for (i = 0; i < lim; i++)
tot += va_ arg( ap, double); // 访问 参数 列表 中的 每 一项
va_ end( ap); // 清理 工作
return tot;
}
下面 是 该 程序 的 输出:
return value for sum( 3, 1. 1, 2. 5, 13. 3): 16. 9
return value for sum( 6, 1. 1, 2. 1, 13. 1, 4. 1, 5. 1, 6. 1): 31. 6
查看 程序 中的 运算 可以 发现, 第 1 次 调用 sum() 时 对 3 个数 求和, 第 2 次 调用 时 对 6 个数 求和。
最后觉得我写的博客有什么需要提升的地方,欢迎评论指出,我一定斟酌更改,谢谢!