29. C语言 可变参数详解

本章目录:

    • 前言
    • 可变参数的基本概念
      • 可变参数的工作原理
      • 如何使用可变参数
    • 示例:计算多个整数的平均值
      • 解析:
    • 更复杂的可变参数示例:打印可变数量的字符串
      • 解析:
    • 总结


前言

在C语言中,函数参数的数量通常是固定的,但在某些情况下,我们可能需要一个函数能够接受不同数量的参数。为了应对这一需求,C语言提供了可变参数机制,这使得函数的参数个数能够灵活调整。这在处理不确定参数个数的场景中非常有用,比如打印调试信息、构建日志记录函数等。


可变参数的基本概念

C语言允许函数定义具有可变数量的参数,形式如下所示:

int func_name(int arg1, ...);

其中,...表示该函数的可变参数列表。与固定参数不同,C语言并不直接提供有关可变参数的类型和个数的直接信息,因此我们需要一些宏和类型来处理这些参数。

可变参数的工作原理

要在C语言中使用可变参数,我们必须引入<stdarg.h>头文件。该头文件定义了一些用于处理可变参数的宏。这些宏使得程序能够访问可变参数列表,并从中获取不同类型的参数。

常用的宏有:

  • va_list:该类型用于保存可变参数的相关信息。
  • va_start(ap, last_arg):初始化va_list变量,指向第一个可变参数。
  • va_arg(ap, type):访问可变参数列表中的下一个参数,并将ap指向下一个参数。
  • va_end(ap):结束可变参数的访问。

如何使用可变参数

  1. 定义一个va_list变量:该变量用于保存参数信息。
  2. 使用va_start初始化:该宏初始化va_list变量,使其指向可变参数的第一个元素。
  3. 使用va_arg获取每一个参数:每次调用va_arg获取一个参数,并使指针指向下一个参数。
  4. 使用va_end结束访问:访问结束后,需要使用va_end来清理资源。

让我们通过一个示例来详细说明这一过程。

示例:计算多个整数的平均值

下面是一个函数average,它接受多个整数作为参数,并计算这些整数的平均值。

#include <stdio.h>
#include <stdarg.h>// 计算平均值的函数
double average(int num, ...) {va_list valist;     // 声明一个va_list变量,用于保存可变参数的信息double sum = 0.0;   // 用于累加所有参数的和int i;// 初始化valist,num是可变参数前的固定参数va_start(valist, num);// 逐一获取并累加参数for (i = 0; i < num; i++) {sum += va_arg(valist, int); // 获取下一个整数并累加}// 清理valistva_end(valist);return sum / num;  // 返回平均值
}int main() {// 调用average函数,传入不同数量的参数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));return 0;
}

解析:

  1. average函数:该函数接受一个固定的参数num,表示后面可变参数的个数。通过va_start宏初始化valist,然后通过va_arg宏逐个访问参数并进行累加。
  2. va_start(valist, num):初始化valistnum是最后一个固定参数的名字,告诉va_start从哪里开始读取可变参数。
  3. va_arg(valist, int):每次调用时,va_arg都会返回一个参数的值,并使valist指向下一个参数。
  4. va_end(valist):结束对valist的访问,释放资源。

运行此程序的输出结果是:

Average of 2, 3, 4, 5 = 3.500000
Average of 5, 10, 15 = 10.000000

更复杂的可变参数示例:打印可变数量的字符串

我们可以进一步扩展可变参数的应用,例如设计一个函数,它能够接受任意数量的字符串并逐一打印它们。以下是实现的代码:

#include <stdio.h>
#include <stdarg.h>
#include <string.h>// 打印多个字符串
void print_strings(const char* msg, ...) {va_list argp;int argno = 1; // 参数编号char *str;// 初始化va_listva_start(argp, msg);// 输出第一个固定参数printf("%s: ", msg);// 逐个取出字符串并打印while (1) {str = va_arg(argp, char*);if (strcmp(str, "/0") == 0) break;  // "/0"表示参数输入结束printf("Parameter #%d is: %s\n", argno, str);argno++;}// 清理va_listva_end(argp);
}int main() {print_strings("Demo", "This", "is", "a", "test", "/0");return 0;
}

解析:

  1. print_strings函数:此函数接受一个固定参数msg,表示信息的前缀。接下来的可变参数是字符串,直到遇到特殊字符串/0为止,表示参数输入的结束。
  2. va_list的使用:函数通过va_start初始化参数列表,然后使用va_arg逐一获取并打印每个字符串,直到遇到/0
  3. va_end:结束对可变参数的访问,释放资源。

运行此代码的输出如下:

Demo: Parameter #1 is: This
Parameter #2 is: is
Parameter #3 is: a
Parameter #4 is: test

总结

可变参数在C语言中提供了极大的灵活性,使得函数能够接受不同数量的参数。通过使用stdarg.h中的宏,我们可以安全地访问和处理这些参数。在实际应用中,理解和掌握可变参数的使用对于编写更加通用的库和函数非常重要。

在实际编程中,我们应注意以下几点:

  1. 可变参数的类型安全:C语言并不进行类型检查,因此在使用va_arg时需要确保获取的参数类型正确。
  2. 参数个数的传递:可变参数列表并不自带参数个数,因此通常需要通过固定参数来传递参数的数量,或者通过特定的标志符(如/0)来终止参数的读取。

通过掌握这些知识,您可以更灵活地处理函数参数,编写更加高效和可复用的代码。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/9732.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微服务入门(go)

微服务入门&#xff08;go&#xff09; 和单体服务对比&#xff1a;里面的服务仅仅用于某个特定的业务 一、领域驱动设计&#xff08;DDD&#xff09; 基本概念 领域和子域 领域&#xff1a;有范围的界限&#xff08;边界&#xff09; 子域&#xff1a;划分的小范围 核心域…

js小游戏---2048(附源代码)

一、游戏页面展示 开始游戏&#xff1a; 游戏结束&#xff1a; 二、游戏如何操作 通过监听键盘的操作&#xff0c;进行移动变化 键盘上下左右键控制页面中所有模块同时向键入的方向移动&#xff0c;如果有两块一样的方块&#xff0c;就进行合并&#xff0c;并且在键盘每操作…

ASP.NET代码审计 SQL注入篇(简单记录)

sql注入&#xff0c;全局搜索 Request QueryString ToString() select select * aspx是设计页面&#xff0c;而aspx.cs是类页面&#xff0c;也就是说设计页面用到的类信息在这个页面里面&#xff0c;其实就是把设计和实现分离开来。 源码 using System; using System.Collect…

【Rust自学】14.6. 安装二进制crate

喜欢的话别忘了点赞、收藏加关注哦&#xff0c;对接下来的教程有兴趣的可以关注专栏。谢谢喵&#xff01;(&#xff65;ω&#xff65;) 14.6.1. 从cratea.io安装二进制crate 通过cargo_install命令可以从crates.io安装二进制crate。 这并不是为了替换系统包&#xff0c;它应…

C++中的类与对象(中)

在上一节中&#xff0c;我们初步了解了一下&#xff0c;C中的类&#xff0c;这一概念&#xff0c;这一节让我们进一步深入了解一下。 文章目录 目录 前言 一、类中的默认成员函数 1.1 构造函数 构造函数的特点&#xff1a; 1.2 析构函数 析构函数的特点&#xff1a; 1.3 …

机器人抓取与操作概述(深蓝)——1

工业机器人&#xff1a;① “臂”的形态 ② “手”的形态 ③ 视觉&#xff0c;力和触觉 1 机器人的不同形态 “臂”的形态 “手”的形态 2 常见的操作任务 操作&#xff1a;插入、推和滑 抓取&#xff1a;两指&#xff08;平行夹爪&#xff09;抓取、灵巧手抓取 落地-产…

指针的介绍3后

1.函数指针变量 1.1函数的地址 void test(int (*arr)[2]) {printf("zl_dfq\n"); } int main() {printf("%p\n", test);printf("%p\n", &test);return 0; } 由上面的程序运行可知&#xff1a; 函数名就是函数的地址 &函数名也可以拿到函…

基于Springboot的智能学习平台系统【附源码】

基于Springboot的智能学习平台系统 效果如下&#xff1a; 系统登陆页面 系统主页面 课程详情页面 论坛页面 考试页面 试卷管理页面 考试记录页面 错题本页面 研究背景 随着互联网技术的普及&#xff0c;人们探索知识的方式逐渐转向数字化平台。传统的教学方法通常局限于固定…

shiro学习五:使用springboot整合shiro。在前面学习四的基础上,增加shiro的缓存机制,源码讲解:认证缓存、授权缓存。

文章目录 前言1. 直接上代码最后在讲解1.1 新增的pom依赖1.2 RedisCache.java1.3 RedisCacheManager.java1.4 jwt的三个类1.5 ShiroConfig.java新增Bean 2. 源码讲解。2.1 shiro 缓存的代码流程。2.2 缓存流程2.2.1 认证和授权简述2.2.2 AuthenticatingRealm.getAuthentication…

网关登录校验

网关登录校验 单体架构时我们只需要完成一次用户登录、身份校验&#xff0c;就可以在所有业务中获取到用户信息。而微服务拆分后&#xff0c;每个微服务都独立部署&#xff0c;不再共享数据。也就意味着每个微服务都需要做登录校验&#xff0c;这显然不可取。 鉴权思路分析 …

【单细胞第二节:单细胞示例数据分析-GSE218208】

GSE218208 1.创建Seurat对象 #untar(“GSE218208_RAW.tar”) rm(list ls()) a data.table::fread("GSM6736629_10x-PBMC-1_ds0.1974_CountMatrix.tsv.gz",data.table F) a[1:4,1:4] library(tidyverse) a$alias:gene str_split(a$alias:gene,":",si…

【已解决】黑马点评项目Redis版本替换过程的数据迁移

黑马点评项目Redis版本替换过程的数据迁移 【哭哭哭】附近商户中需要用到的GEO功能只在Redis 6.2以上版本生效 如果用的是老版本&#xff0c;美食/KTV的主页能正常返回&#xff0c;但无法显示内容 上次好不容易升到了5.0以上版本&#xff0c;现在又用不了了 Redis 6.2的windo…

本地部署deepseek模型步骤

文章目录 0.deepseek简介1.安装ollama软件2.配置合适的deepseek模型3.安装chatbox可视化 0.deepseek简介 DeepSeek 是一家专注于人工智能技术研发的公司&#xff0c;致力于打造高性能、低成本的 AI 模型&#xff0c;其目标是让 AI 技术更加普惠&#xff0c;让更多人能够用上强…

[论文总结] 深度学习在农业领域应用论文笔记14

当下&#xff0c;深度学习在农业领域的研究热度持续攀升&#xff0c;相关论文发表量呈现出迅猛增长的态势。但繁荣背后&#xff0c;质量却不尽人意。相当一部分论文内容空洞无物&#xff0c;缺乏能够落地转化的实际价值&#xff0c;“凑数” 的痕迹十分明显。在农业信息化领域的…

快速分析LabVIEW主要特征进行判断

在LabVIEW中&#xff0c;快速分析程序特征进行判断是提升开发效率和减少调试时间的重要技巧。本文将介绍如何高效地识别和分析程序的关键特征&#xff0c;从而帮助开发者在编写和优化程序时做出及时的判断&#xff0c;避免不必要的错误。 ​ 数据流和并行性分析 LabVIEW的图形…

展示统计信息收集情况

看看最近是否收集失败 SET LINES 200 PAGES 0 SET LONG 100000 longc 100000 COLUMN REPORT FORMAT A200VARIABLE stat_report CLOB; BEGIN:stat_report : DBMS_STATS.REPORT_STATS_OPERATIONS (since > SYSDATE-3 , until > SYSDATE , detail_lev…

STM32 TIM输入捕获 测量频率

输入捕获简介&#xff1a; IC&#xff08;Input Capture&#xff09;输入捕获 输入捕获模式下&#xff0c;当通道输入引脚出现指定电平跳变时&#xff0c;当前CNT的值将被锁存到CCR中&#xff0c;可用于测量PWM波形的频率、占空比、脉冲间隔、电平持续时间等参数 每个高级定时器…

如何将 Windows 上的文件传递到 Mac 上

文章目录 效果需求Windows 上设置共享磁盘【可选】新建一个带有密码的账户查看 Windows 的 IP 地址Mac 上链接 Windows 共享的磁盘 效果 需求 Windows 上有一个有密码的账户 Windows 上设置共享磁盘 windows 这边需要用 Administrator 权限的账号&#xff0c;把要共享的磁盘设…

NLP模型大对比:Transformer > RNN > n-gram

结论 Transformer 大于 RNN 大于 传统的n-gram n-gram VS Transformer 我们可以用一个 图书馆查询 的类比来解释它们的差异&#xff1a; 一、核心差异对比 维度n-gram 模型Transformer工作方式固定窗口的"近视观察员"全局关联的"侦探"依赖距离只能看前…

ODP(OBProxy)路由初探

OBProxy路由策略 Primary Zone 路由 官方声明默认情况&#xff0c;会将租户请求发送到租户的 primary zone 所在的机器上&#xff0c;通过 Primary Zone 路由可以尽量发往主副本&#xff0c;方便快速寻找 Leader 副本。另外&#xff0c;设置primary zone 也会在一定成都上减少…