C语言可变参数(stdarg.h包含)详解

这篇文章介绍了C语言中处理可变参数的机制,包括使用stdarg.h头文件,va_list类型,以及va_start、va_arg和va_end宏的用法。通过示例函数说明如何在函数中接收和处理不同数量的参数,并强调了正确匹配参数类型的重要性。最后给出一个求和函数的示例来演示这些概念的应用。
摘要由CSDN通过智能技术生成

可变参数(包含头文件stdarg.h)

在阅读别人的代码时遇到va_list这个符号,一时之间想不起来怎么理解,只依稀记得这是初学c时遇到的知识点

翻开教材书才知道这叫做可变参数,由stdarg.h头文件包含

《C Primer Plus》中这样描述

	stdarg.h头文件为函数提供了一个类似的(前文提到的变参宏)功能, 但是 用法 比较 复杂。 必须 按 如下 步骤 进行: 
	提供 一个 使用 省略 号的 函数 原型; 
	在 函数 定义 中 创建 一个 va_ list 类型 的 变量; 
	用 宏 把 该 变量 初始 化为 一个 参数 列表; 
	用 宏 访问 参数 列表; 
	用 宏 完成 清理 工作。

例如:

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 个数 求和。

最后觉得有什么需要更改的地方,欢迎评论指出,谢谢!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值