文章目录
- 1 前言
- 2 实例
- 2.1实例程序
- 2.2程序执行结果
- 2.3 程序分析
- 3 补充
- 4 总结
1 前言
在编程过程中,有时会遇到需要定义参数数量不固定的函数的情况。
C语言提供了一种灵活的解决方案:变参函数。这种函数能够根据实际调用时的需求,接受任意数量的参数。
本文将通过具体的实例程序,介绍如何定义和使用变参数函数,并分析其原理。
2 实例
2.1实例程序
下面这段代码实现了一个名为 average 的可变参数函数,用于计算平均值。该函数接受一个固定参数 num,指示将要计算平均的数值个数,随后跟随着省略号 …,表示其后跟随的是不定数量的数值参数。
#include <stdio.h>
#include <stdarg.h>
double average(int num,...)
{
va_list valist;
double sum = 0.0;
va_start(valist, num); //为 num 个参数初始化 valist
/* 访问所有赋给 valist 的参数 */
for (int i = 0; i < num; i++)
{
sum += va_arg(valist, int);
}
va_end(valist); //清理为 valist 保留的内存
return sum/num;
}
int main()
{
printf("Average of 2, 3, 4, 5 = %f\n", average(4, 2,3,4,5));
printf("Average of 5, 10, 15 = %f\n", average(3, 5,10,15));
}
2.2程序执行结果
2.3 程序分析
1.函数参数传递的原理
为更好的理解变参数函数,首先介绍下函数函数参数传递的原理。传入参数是以栈的形式存取,举个例子,声明一个函数如下:
void fun(int x, float y, char z);
在调用函数 fun 时,参数按照相反的顺序入栈:首先是 int x,接着是 float y,最后是 char z,即在内存中的存储顺序是 z->y->x。
知道这些参数在内存中是连续存储的,从理论上讲,如果我们能够探测到这些参数中的任意一个变量的内存地址,并且了解其类型以及相关类型的内存布局,我们可以使用指针算术来计算并访问其他参数的地址。
2.变参函数 average() 的执行遵循以下符合参数传递原理的步骤:
- 创建一个va_list 类型 变量valist,用于存储变参函数的参数列表
- 使用 宏 va_start用于初始化 va_list类型的变量,确保它指向变参函数的第一个命名参数 num,该参数地址紧邻可变参数区域
...
- 利用 宏 va_arg 来访问参数列表valist中的每个int类型项,每次调用后 valist 将自动更新以指向下一个参数
- 使用宏 va_end 来清理赋予valist变量的内存
通过上面对变参函数的分析可知,变参函数并不是所有的参数都可以省略(即函数不能定义成
fun(...)
这种形式),至少需要一个固定参数(如实例程序中的num)来作为变参列表的开始标记
3 补充
下面再介绍一个实例,拓展一下变参函数的使用,它通过变参函数列表和vsnprintf函数格式化字符串,输出整数、浮点数等类型的变量。
1.程序:
#include "stdio.h"
#include "stdarg.h"
int i=1;
double j = 45.67;
char message[50];
void fun(const char *format, ...)
{
va_list args;
va_start(args, format);
vsnprintf(message, sizeof(message), format, args);
va_end(args);
// 打印格式化后的字符串
printf("%s\n", message);
}
int main(void)
{
fun("var1: %d", i);
fun("var1: %d var2: %f ", i, j);
return 0;
}
2.程序执行结果
3.函数vsnprintf
介绍
vsnprintf
函数是一个C语言标准库函数,用于将格式化的数据写入到一个字符串缓冲区中,并且可以指定最大写入的字符数。
函数原型:
int vsnprintf(char *str, size_t size, const char *format, va_list arg);
参数说明:
- str:指向用于存储格式化后的输出的字符数组的指针。
- size:缓冲区的大小(以字符为单位),包括空字符(‘\0’)的空间。如果size为0,vsnprintf将不写入任何字符,但会返回需要的缓冲区大小(不包括空字符)。
- format:格式化字符串,指定了如何格式化后续参数。
- arg:va_list类型的参数列表,包含了要格式化的参数。
返回值:
- vsnprintf返回写入到str缓冲区中的字符数(不包括终止的空字符’\0’),如果发生错误或者缓冲区大小不足以容纳所有字符,则返回负值。
实例程序中,main函数中调用fun("var1: %d var2: %f ", i, j);
时,其内部vsnprintf函数的调用相当于直接使用vsnprintf(message, sizeof(message), "var1: %d var2: %f ", i, j);
进行格式化输出。
4 总结
本文将通过具体的实例程序,介绍了如何定义和使用变参数函数,并分析其原理。
参考链接:
https://blog.csdn.net/lijian2017/article/details/109597068
https://www.runoob.com/cprogramming/c-variable-arguments.html
https://blog.csdn.net/Rue_lcy/article/details/129689951