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.mzph.cn/web/67435.shtml

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

相关文章

RoboMaster- RDK X5能量机关实现案例(一)识别

作者&#xff1a;SkyXZ CSDN&#xff1a;https://blog.csdn.net/xiongqi123123 博客园&#xff1a;https://www.cnblogs.com/SkyXZ 在RoboMaster的25赛季&#xff0c;我主要负责了能量机关的视觉方案开发&#xff0c;目前整体算法已经搭建完成&#xff0c;实际方案上我使用的上…

shell脚本批量修改文件名之方法(The Method of Batch Modifying File Names in Shell Scripts)

shell脚本批量修改文件名方法 我们可以使用Shell脚本来实现这个功能。Shell脚本是一种用于自动化任务的编程语言&#xff0c;它可以在Unix/Linux操作系统上运行。在这个脚本中&#xff0c;我们将使用一个for循环来遍历目标目录下的所有文件&#xff0c;并使用mv命令将每个文件…

MySQL误删数据怎么办?

文章目录 1. 从备份恢复数据2. 通过二进制日志恢复数据3. 使用数据恢复工具4. 利用事务回滚恢复数据5. 预防误删数据的策略总结 在使用MySQL进行数据管理时&#xff0c;误删数据是一个常见且具有高风险的操作。无论是因为操作失误、系统故障&#xff0c;还是不小心执行了删除命…

RDK X5运行DeepSeek-R1-Distill-Qwen-1.5B,体验长思维链的语言大模型!

简介 本文介绍了在RDK X5上&#xff0c;如何从HuggingFace的原始模型权重&#xff08;safetensors&#xff09;经过量化和编译&#xff0c;的到llama.cpp推理框架所需要的GGUF格式的模型&#xff0c;然后演示了如何使用llama.cpp运行量化后的DeepSeek-R1-Distill-Qwen-1.5B模型…

SQL UCASE() 函数详解

SQL UCASE() 函数详解 在SQL中&#xff0c;UCASE() 函数是一个非常有用的字符串处理函数&#xff0c;它可以将字符串中的所有小写字母转换为大写字母。本文将详细介绍UCASE() 函数的用法、语法、示例以及其在实际应用中的优势。 一、UCASE() 函数简介 UCASE() 函数是SQL标准…

【Proteus仿真】【51单片机】简易计算器系统设计

目录 一、主要功能 二、使用步骤 三、硬件资源 四、软件设计 五、实验现象 联系作者 一、主要功能 1、LCD1602液晶显示 2、矩阵按键​ 3、可以进行简单的加减乘除运算 4、最大 9999*9999 二、使用步骤 系统运行后&#xff0c;LCD1602显示数据&#xff0c;通过矩阵按键…

留学毕业论文如何利用不同问题设计问卷

在留学毕业论文的写作中&#xff0c;我们经常会遇到各种问题&#xff0c;例如选择合适的问题&#xff0c;选择合适的研究方法&#xff0c;以及设计合理的研究过程。然而在完成留学毕业论文的过程中&#xff0c;我们往往会在研究设计这里卡住。即使我们选准了研究问题和研究方法…

深度学习在金融风控中的应用:突破传统模型的瓶颈

深度学习在金融风控中的应用:突破传统模型的瓶颈 金融风险控制(简称“风控”)是现代金融体系中至关重要的一环,关系到金融机构的稳定性、客户的安全以及整体经济的健康运行。近年来,随着深度学习的迅猛发展,传统的风控模型正面临被颠覆的挑战,新的技术手段和思维方式正…

Python中的函数(上)

Python中的函数是非常重要的编程概念&#xff0c;以下是详细的介绍&#xff1a; 函数定义基础 在Python中&#xff0c;函数是组织好的、可重复使用的代码块&#xff0c;用于执行特定任务。通过函数&#xff0c;我们可以将复杂的程序分解为较小的、更易管理的部分&#xff0c…

图漾相机搭配VisionPro使用简易教程

文章目录 1.下载并安装VisionPro软件2.下载PercipioCameraForVisionPro软件包3.软件部署4.测试流程4.1 遍历VisionPro SDK支持的参数4.2 设置示例4.2.1_cameraSingle.SetTriggerMode4.2.2 _cameraSingle.SetRegistration4.2.3_cameraSingle.SetInt4.2.4 _cameraSingle.GetInt4.…

新版IDEA创建数据库表

这是老版本的IDEA创建数据库表&#xff0c;下面可以自己勾选Not null&#xff08;非空),Auto inc&#xff08;自增长),Unique(唯一标识)和Primary key&#xff08;主键) 这是新版的IDEA创建数据库表&#xff0c;Not null和Auto inc可以看得到&#xff0c;但Unique和Primary key…

(非技术)从一公里到半程马拉松:我的一年跑步经历

在24年初&#xff0c;从来不运动的我&#xff0c;连跑步一公里都不能完成。而在一年之后的2025年的1月1日&#xff0c;我参加了上海的蒸蒸日上迎新跑&#xff0c;完成了半程马拉松。虽然速度不快&#xff0c;也并不是什么特别难完成的事情&#xff0c;但对我来说还是挺有意义的…

论“0是不存在的”

你看这又是一个悖论的例子。 你会说&#xff0c;既然你都写出来了“0”&#xff0c;咋还能说它不存在&#xff1f; 总是刷到谢尔顿说零不存在那个视频。可能有些小伙伴不解其意&#xff0c;为啥那小谢尔顿坚持说0不存在。我这说一个最简单的视角&#xff0c;怎么理解这句话。…

电商系统-用户认证(三)基于公钥解析JWT令牌

一、 基于私钥生成jwt令牌 步骤&#xff1a; 导入认证服务 将shangcheng_user_auth工程导入到项目中去&#xff0c;如下图 启动eureka&#xff0c;再启动认证服务 3&#xff09; 认证服务中创建测试类 public class CreateJwtTest { ​ /**** 创建令牌测试*/Testpublic voi…

单路由及双路由端口映射指南

远程登录总会遇到登陆不上的情况&#xff0c;可能是访问的大门没有打开哦&#xff0c;下面我们来看看具体是怎么回事&#xff1f; 当软件远程访问时&#xff0c;主机需要两个条件&#xff0c;一是有一个唯一的公网IP地址&#xff08;运营商提供&#xff09;&#xff0c;二是开…

Spring AI 在微服务中的应用:支持分布式 AI 推理

1. 引言 在现代企业中&#xff0c;微服务架构 已成为开发复杂系统的主流方式&#xff0c;而 AI 模型推理 也越来越多地被集成到业务流程中。如何在分布式微服务架构下高效地集成 Spring AI&#xff0c;使多个服务可以协同完成 AI 任务&#xff0c;并支持分布式 AI 推理&#x…

Kafak 单例生产者实现-C#操作

前面写了一篇入门操作的文章,因为工作需要,简单修改了下如何实现单例生产者。 Kafka入门-C#操作_c# kafka-CSDN博客文章浏览阅读1.6k次,点赞20次,收藏9次。2).报错:“kafka.zookeeper.ZooKeeperClientTimeoutException: Timed out waiting for connection while in state…

2024年记 | 凛冬将至

放弃幻想&#xff0c;准备斗争&#xff01; 考研or就业&#xff1f; 上大学以来&#xff0c;考研上名校在我的心里一直是一颗种子&#xff0c;2024年初&#xff0c;当时的想法是考研和就业两手抓。买了张宇的高数现代&#xff0c;想要死磕&#xff01; 也记了挺多笔记... 如果…

基于 Jenkins 的测试报告获取与处理并写入 Jira Wiki 的技术总结

title: 基于 Jenkins 的测试报告获取与处理并写入 Jira Wiki 的技术总结 tags: - jenkins - python categories: - jenkins在软件开发的持续集成与持续交付&#xff08;CI/CD&#xff09;流程里&#xff0c;及时、准确地获取并分析测试报告对保障软件质量至关重要。本文将详细…

C# 中 default 使用详解

总目录 前言 在C#中&#xff0c;default 关键字用于表示类型默认值。它可以根据上下文推断出适用的类型&#xff0c;并返回该类型的默认值。随着C#版本的发展&#xff0c;default 的用法也变得更加丰富和灵活。本文将详细介绍 default 在不同场景下的使用方法及其最佳实践。 一…