29. C语言 可变参数详解

news2025/1/30 14:02:49

本章目录:

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


前言

在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); // 获取下一个整数并累加
    }

    // 清理valist
    va_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_list
    va_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_list
    va_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.coloradmin.cn/o/2286451.html

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

相关文章

node 爬虫开发内存处理 zp_stoken 作为案例分析

声明: 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01; 前言 主要说3种我们补环境过后如果用…

Hive:struct数据类型,内置函数(日期,字符串,类型转换,数学)

struct STRUCT&#xff08;结构体&#xff09;是一种复合数据类型&#xff0c;它允许你将多个字段组合成一个单一的值, 常用于处理嵌套数据&#xff0c;例如当你需要在一个表中存储有关另一个实体的信息时。你可以使用 STRUCT 函数来创建一个结构体。STRUCT 函数接受多个参数&…

冯诺依曼系统及操作系统

目录 一.冯诺依曼体系结构 二.操作系统 三.系统调用和库函数概念 一.冯诺依曼体系结构 我们常见的计算机&#xff0c;如笔记本。我们不常见的计算机&#xff0c;如服务器&#xff0c;大部分都遵守冯诺依曼体系 截至目前&#xff0c;我们所认识的计算机&#xff0c;都是由一…

Scrapy如何设置iP,并实现IP重用, IP代理池重用

前置知识 1/3乐观锁 2/3 Scrapy流程(非全部) 3/3 关于付费代理 我用的"快代理", 1000个ip, 每个ip1min的有效期, 你用的时候, 把你的链接, 用户名填上去就行 设置代理IP &#x1f512; & 帮助文档: ①meta ②meta#proxy$ 语法: ①proxy的设置: Request对象中…

HTML<kbd>标签

例子 在文档中将一些文本定义为键盘输入&#xff1a; <p>Press <kbd>Ctrl</kbd> <kbd>C</kbd> to copy text (Windows).</p> <p>Press <kbd>Cmd</kbd> <kbd>C</kbd> to copy text (Mac OS).</p>…

RocketMQ原理—5.高可用+高并发+高性能架构

大纲 1.RocketMQ的整体架构与运行流程 2.基于NameServer管理Broker集群的架构 3.Broker集群的主从复制架构 4.基于Topic和Queue实现的数据分片架构 5.Broker基于Pull模式的主从复制原理 6.Broker层面到底如何做到数据0丢失 7.数据0丢失与写入高并发的取舍 8.RocketMQ读…

Golang Gin系列-9:Gin 集成Swagger生成文档

文档一直是一项乏味的工作&#xff08;以我个人的拙见&#xff09;&#xff0c;但也是编码过程中最重要的任务之一。在本文中&#xff0c;我们将学习如何将Swagger规范与Gin框架集成。我们将实现JWT认证&#xff0c;请求体作为表单数据和JSON。这里唯一的先决条件是Gin服务器。…

技术发展视域下中西方技术研发思维方式的比较与启示

一、引言 1.1 研究背景与意义 在当今全球化的时代&#xff0c;科技发展日新月异&#xff0c;深刻地改变着人类的生活与社会的面貌。从人工智能的飞速发展&#xff0c;到生物科技的重大突破&#xff1b;从信息技术的广泛应用&#xff0c;到新能源技术的不断革新&#xff0c;技术…

第4章 神经网络【1】——损失函数

4.1.从数据中学习 实际的神经网络中&#xff0c;参数的数量成千上万&#xff0c;因此&#xff0c;需要由数据自动决定权重参数的值。 4.1.1.数据驱动 数据是机器学习的核心。 我们的目标是要提取出特征量&#xff0c;特征量指的是从输入数据/图像中提取出的本质的数 …

Go的内存逃逸

Go的内存逃逸 内存逃逸是 Go 语言中一个重要的概念&#xff0c;指的是本应分配在栈上的变量被分配到了堆上。栈上的变量在函数结束后会自动回收&#xff0c;而堆上的变量需要通过垃圾回收&#xff08;GC&#xff09;来管理&#xff0c;因此内存逃逸会增加 GC 的压力&#xff0…

StarRocks BE源码编译、CLion高亮跳转方法

阅读SR BE源码时&#xff0c;很多类的引用位置爆红找不到&#xff0c;或无法跳转过去&#xff0c;而自己的Linux机器往往缺乏各种C依赖库&#xff0c;配置安装比较麻烦&#xff0c;因此总体的思路是通过CLion远程连接SR社区已经安装完各种依赖库的Docker容器&#xff0c;进行编…

Vue 响应式渲染 - 待办事项简单实现

Vue 渐进式JavaScript 框架 基于Vue2的学习笔记 - Vue 响应式渲染 - 待办事项简单实现 目录 待办事项简单实现 页面初始化 双向绑定的指令 增加留言列表设置 增加删除按钮 最后优化 总结 待办事项简单实现 页面初始化 对页面进行vue的引入、创建输入框和按钮及实例化V…

SpringBoot基础概念介绍-数据源与数据库连接池

&#x1f64b;大家好&#xff01;我是毛毛张! &#x1f308;个人首页&#xff1a; 神马都会亿点点的毛毛张 毛毛张今天介绍的SpringBoot中的基础概念-数据源与数据库连接池&#xff0c;同时介绍SpringBoot整合两种连接池的教程 文章目录 1 数据库与数据库管理系统2 JDBC与数…

Microsoft Visual Studio 2022 主题修改(补充)

Microsoft Visual Studio 2022 透明背景修改这方面已经有很多佬介绍过了&#xff0c;今天闲来无事就补充几点细节。 具体的修改可以参考&#xff1a;Microsoft Visual Studio 2022 透明背景修改&#xff08;快捷方法&#xff09;_material studio怎么把背景弄成透明-CSDN博客文…

(done) ABI 相关知识补充:内核线程切换、用户线程切换、用户内核切换需要保存哪些寄存器?

由于操作系统和编译器约定了 ABI&#xff0c;如下&#xff1a; 编译器在对 C 语言编译时&#xff0c;会自动 caller 标注的寄存器进行保存恢复。保存的步骤通常发生在进入函数的时候&#xff0c;恢复的步骤通常发生在从函数返回的时候。 内核线程切换需要保存的寄存器&#…

Linux 多路转接select

Linux 多路转接select 1. select select() 是一种较老的多路转接IO接口&#xff0c;它有一定的缺陷导致它不是实现多路转接IO的最优选择&#xff0c;但 poll() 和 epoll() 都是较新版的Linux系统提供的&#xff0c;一些小型嵌入式设备的存储很小&#xff0c;只能使用老版本的…

【实践案例】使用Dify构建文章生成工作流【在线搜索+封面图片生成+内容标题生成】

文章目录 概述开始节点图片封面生成关键词实时搜索主题参考生成文章详情和生成文章标题测试完整工作流运行测试结果 概述 使用Dify构建文章生成工作流&#xff0c;使用工具包括&#xff1a;使用 Tavily 执行的搜索查询&#xff0c;使用Flux生成封面图片&#xff0c;使用Stable…

Web3 如何赋能元宇宙,实现虚实融合的无缝对接

随着技术的飞速发展&#xff0c;元宇宙作为一个未来数字世界的概念&#xff0c;正在吸引全球范围内的关注。而 Web3 技术的兴起&#xff0c;为元宇宙的实现提供了强大的支撑。Web3 是基于区块链技术的去中心化网络&#xff0c;它在改变互联网的同时&#xff0c;也推动着虚拟世界…

LangChain的开发流程

文章目录 LangChain的开发流程开发密钥指南3种使用密钥的方法编写一个取名程序 LangChain表达式 LangChain的开发流程 为了更深人地理解LangChain的开发流程&#xff0c;本文将以构建聊天机器人为实际案例进行详细演示。下图展示了一个设计聊天机器人的LLM应用程序。 除了Wb服务…

电商系统-用户认证(四)Oauth2授权模式和资源服务授权

本文章介绍&#xff1a;Oauth2.0 常见授权模式&#xff0c;资源服务授权 。 准备工作 搭建认证服务器之前&#xff0c;先在用户系统表结构中增加如下表结构&#xff1a; CREATE TABLE oauth_client_details (client_id varchar(48) NOT NULL COMMENT 客户端ID&#xff0c;主…