当前位置: 首页 > news >正文

【C语言可变参数函数的使用与原理分析】

文章目录

  • 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 总结

本文将通过具体的实例程序,介绍了如何定义和使用变参数函数,并分析其原理。

参考链接:
va_start 用法
C 可变参数
【C语言】vsnprintf函数的使用

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 搭建VUE+VScode+elementUI环境遇到的问题
  • 【每日一题】【平衡树】【__gnu_pbds :: tree】小红的中位数 牛客周赛 Round 29 D题 C++
  • Rust: Web框架Axum和Rest Client协同测试
  • 常见概念 -- 非线性效应
  • FPGA随记——8B/10B编码
  • 倍福——ADS协议解析及C语言读写库
  • 2024年6月第2套英语四级真题PDF
  • 第二章 深信服超融合测试历程第二天
  • 【计算机视觉前沿研究 热点 顶会】ECCV 2024中Mamba有关的论文
  • macOS系统介绍与特点
  • Oracle(106)如何实现透明数据加密?
  • 93. UE5 GAS RPG 应用负面效果表现
  • 批处理常用指令与脚本的例子
  • web渗透:SSRF漏洞
  • 海康二次开发学习笔记7-流程相关操作
  • #Java异常处理
  • 「前端」从UglifyJSPlugin强制开启css压缩探究webpack插件运行机制
  • 【知识碎片】第三方登录弹窗效果
  • CentOS从零开始部署Nodejs项目
  • docker容器内的网络抓包
  • golang 发送GET和POST示例
  • Java的Interrupt与线程中断
  • Mocha测试初探
  • MySQL Access denied for user 'root'@'localhost' 解决方法
  • mysql_config not found
  • nfs客户端进程变D,延伸linux的lock
  • React-生命周期杂记
  • Spring框架之我见(三)——IOC、AOP
  • Travix是如何部署应用程序到Kubernetes上的
  • TypeScript迭代器
  • 测试如何在敏捷团队中工作?
  • 和 || 运算
  • 名企6年Java程序员的工作总结,写给在迷茫中的你!
  • 数据可视化之 Sankey 桑基图的实现
  • 学习使用ExpressJS 4.0中的新Router
  • 一个SAP顾问在美国的这些年
  • ​你们这样子,耽误我的工作进度怎么办?
  • ​如何使用ArcGIS Pro制作渐变河流效果
  • #HarmonyOS:基础语法
  • #ifdef 的技巧用法
  • (1)Android开发优化---------UI优化
  • (2)STL算法之元素计数
  • (27)4.8 习题课
  • (leetcode学习)236. 二叉树的最近公共祖先
  • (pytorch进阶之路)CLIP模型 实现图像多模态检索任务
  • (Redis使用系列) Springboot 使用redis实现接口Api限流 十
  • (补)B+树一些思想
  • (附源码)ssm考生评分系统 毕业设计 071114
  • (介绍与使用)物联网NodeMCUESP8266(ESP-12F)连接新版onenet mqtt协议实现上传数据(温湿度)和下发指令(控制LED灯)
  • (四)docker:为mysql和java jar运行环境创建同一网络,容器互联
  • (贪心 + 双指针) LeetCode 455. 分发饼干
  • (转) ns2/nam与nam实现相关的文件
  • (自用)仿写程序
  • ***详解账号泄露:全球约1亿用户已泄露
  • .Mobi域名介绍