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

C语言中的va_list

目录

1. 可变参数函数(Variadic Function)

2. va_list 及相关宏

3. va_list 的用途

4. 与 printf、vsnprintf 等函数的关系

5. 在实际场景中的示例

5.1 API_SendAtCommandParam 函数

5.2 va_arg 直接取参数

6. 常见问题

7. 结论


在 C 语言中,va_list 是一个专门用于处理可变参数函数(variadic function)的类型。可变参数函数指的是像 printf(const char *format, ...) 这样使用省略号(...)来接收不定数量或类型参数的函数。要想在函数内部获取这些“不定参数”,就要用到 va_list 及其相关的宏和函数。下面做详细介绍:


1. 可变参数函数(Variadic Function)

在 C 语言中,函数可以声明成类似下面的形式:

int exampleFunction(const char *format, ...);

这里的 ... 表示这个函数可以接收除固定参数 format 之外的任意数量任意类型的额外参数(C 语言本身不会自动帮你识别它们的类型,需手动解析)。

一个典型示例就是标准库的 printf 函数原型:

int printf(const char *format, ...);

它可以根据 format 字符串中的占位符(如 %d, %s, %f等)来解析后续传入的参数。


2. va_list 及相关宏

C 语言提供以下专门的机制用来处理可变参数:

  1. va_list

    • 它是一种类型,用来声明一个存储可变参数信息的变量。例如:
      va_list args;
      
  2. va_start(va_list, last_fixed_param)

    • 用来初始化这个 va_list 变量,让它从函数形参中“最后一个固定参数”后面开始读取不定参数。
    • 例如:
      void exampleFunction(const char *format, ...)
      {va_list args;va_start(args, format); // 初始化args,从format后面获取可变参数...
      }
      
  3. va_arg(va_list, type)

    • 用来按指定类型依次获取下一个可变参数值。例如:
      int i = va_arg(args, int);      // 取一个 int
      double d = va_arg(args, double);// 取一个 double
      char *s = va_arg(args, char*); // 取一个字符串指针
      
  4. va_end(va_list)

    • 用来清理可变参数列表。当函数对不定参数读取完毕后,应当调用 va_end(args)
    • 例如:
      va_end(args);
      
  5. va_copy(va_list dest, va_list src) (C99 引入)

    • 用来复制一个可变参数列表(在某些情形需要重复遍历参数时使用)。

3. va_list 的用途

在一个可变参数函数内部,当你想要处理形如 (const char *fmt, ...) 中的 “..." 部分,就必须:

  1. 声明一个 va_list 变量
  2. va_start(args, last_fixed_param) 初始化它
  3. 根据需要多次调用 va_arg(args, type) 获取后续的每个不定参数
  4. 在处理完后,调用 va_end(args) 进行清理

例如,在一个简化的“打印函数”中:

#include <stdarg.h>  // 包含va_list等定义
#include <stdio.h>void simplePrint(const char *format, ...)
{va_list args;va_start(args, format);    // 初始化args,从format后面取不定参数// 假设我们只想取一个 int 和一个 doubleint i = va_arg(args, int); double d = va_arg(args, double);// 这里可以使用 i, dprintf("format=%s, i=%d, d=%f\n", format, i, d);va_end(args);
}

当调用 simplePrint("test", 42, 3.14);时,va_arg(args, int) 会得到 42,va_arg(args, double) 会得到 3.14。


4. 与 printfvsnprintf 等函数的关系

标准库的 printffprintfvsnprintf 等都使用了可变参数机制:

  • printf(const char *format, ...) / snprintf(char *buf, size_t size, const char *format, ...)
    这类函数使用省略号接收不定参数,然后在函数内部完成对这些参数的解析与格式化输出。

  • vprintf(const char *format, va_list args) / vsnprintf(char *buf, size_t size, const char *format, va_list args)
    针对已有 va_list 的版本。如果你已经拿到了 va_list args,就可以使用 vprintf/vsnprintf 直接处理。
    也就是说,“v”系列函数(如 vprintfvsnprintf)正是可变参数 + va_list 相结合的结果。


5. 在实际场景中的示例

5.1 API_SendAtCommandParam 函数

就像我们之前给出的示例:

#include <stdarg.h>
#include <stdio.h>API_Status API_SendAtCommandParam(const char *at_template, ...)
{char cmd[128];va_list args;va_start(args, at_template);            // 初始化args,从at_template之后开始获取可变参int n = vsnprintf(cmd, sizeof(cmd), at_template, args); va_end(args);if (n < 0 || n >= (int)sizeof(cmd)) {return API_PARAM_ERROR;}if (comSendBuf(cmd, n) != 0) {return API_ERROR;}return API_OK;
}

在这里:

  1. 我们的函数定义了一个不定参数 (const char *at_template, ...)
  2. 函数内部声明 va_list args;
  3. 调用 va_start(args, at_template); 来初始化 args
  4. 然后把 args 交给 vsnprintf 来格式化到 cmd 中。
  5. 最后 va_end(args); 进行清理。

5.2 va_arg 直接取参数

如果我们不想使用 vsnprintf 之类函数来拼接字符串,而是自行解析每个参数,可以像这样:

void exampleFunc(const char *info, ...)
{va_list args;va_start(args, info);int a = va_arg(args, int);char c = (char) va_arg(args, int); // char在va_arg中传递时要按int取double d = va_arg(args, double);// ...va_end(args);
}

6. 常见问题

  1. 为什么要 va_end

    • 这是可变参数的协议要求,用于清理资源或使处理器栈保持一致。忽略可能导致移植性问题。
  2. 如果不知道参数数量咋办?

    • 一般通过format字符串或某个“终止标识”来知道需要获取多少参数,与 printf 同理。C 语言本身不自动帮你检测参数数量。
  3. va_arg(args, type) 中的 type 必须跟传入参数类型一致吗?

    • 必须一致,否则会导致解析错误、内存访问混乱。
  4. 变长宏(variadic macros)va_list 有何不同?**

    • 变长宏是一种特性(以 ... 结尾的宏定义)在编译期处理多余参数。
    • va_list函数层面的不定参数在运行期处理。

7. 结论

  • va_list 是 C 语言用来处理不定参数函数的一种类型,它与 va_start / va_arg / va_end 等宏配合使用,能够在函数里逐个获取传入的可变数量/类型参数。
  • 典型用法:在函数内部先 va_start(args, last_fixed_param),然后多次 va_arg(args, type) 依次读出每个参数,最后 va_end(args)
  • printfsnprintfvsnprintf 等函数就是使用可变参数(或 va_list)来实现的,在变长参数的解析格式化方面非常常见。

相关文章:

C语言中的va_list

目录 1. 可变参数函数&#xff08;Variadic Function&#xff09; 2. va_list 及相关宏 3. va_list 的用途 4. 与 printf、vsnprintf 等函数的关系 5. 在实际场景中的示例 5.1 API_SendAtCommandParam 函数 5.2 va_arg 直接取参数 6. 常见问题 7. 结论 在 C 语言中&am…...

idea无法安装插件

目录 修改工具配置 本地安装 无法下载很多时候就是延迟太高导致的&#xff0c;我们先打开插件官网看一下 Python - IntelliJ IDEs Plugin | Marketplace 修改工具配置 1、配置代理&#xff08;点击 setting-点击 plugins-在点击 http proxy Settings&#xff09; 输入&…...

智汇厦门:苏哒智能携其智能化产品亮相文心中国行现场

2025年1月2日&#xff0c;文心中国行再次踏足美丽的鹭岛厦门。 本次的文心中国行活动不仅有来自政府、高校及企业的精英专家将齐聚一堂&#xff0c;分享AI与大模型的最新研究成果&#xff0c;还正式揭牌百度飞桨&#xff08;厦门&#xff09;人工智能产业赋能中心&#xff0c;…...

C++函数模板的定义为何要和调用点放在一起

在C中&#xff0c;模板的声明最好和调用放在一起&#xff0c;或者确保编译器在进行模板实例化时能看到模板完整的定义&#xff0c;主要有以下几方面原因&#xff1a; 一、模板实例化机制的需求 编译时实例化特点 C模板是在编译阶段根据实际使用时传入的类型参数进行实例化&am…...

Nginx - 整合lua 实现对POST请求的参数拦截校验(不使用Openresty)

文章目录 概述步骤 1: 安装 Nginx 和 Lua 模块步骤 2: 创建 Lua 脚本用于参数校验步骤 3: 配置 Nginx 使用 Lua 脚本写法二&#xff1a; 状态码写法三 &#xff1a; 返回自定义JSON复杂的正则校验 步骤 4: 测试和验证ngx.HTTP_* 枚举值 概述 一个不使用 OpenResty 的 Nginx 集…...

互联网直播点播平台EasyDSS无人机视频推拉流技术实现工地远程监控巡检直播

在建筑行业&#xff0c;施工现场的安全管理和实时监控一直是项目管理中的重点。随着技术的进步&#xff0c;无人机工地直播技术成为了一种新兴的解决方案&#xff0c;它不仅能够提高施工透明度&#xff0c;还能够加强现场安全管理。EasyDSS作为一种先进的流媒体技术平台&#x…...

Unity3D 基于GraphView实现的节点编辑器框架详解

前言 在Unity3D游戏开发中&#xff0c;节点编辑器是一种强大的工具&#xff0c;它允许开发者以可视化的方式创建和编辑复杂的逻辑和流程。Unity提供了一个强大的UI工具包——GraphView&#xff0c;它使得创建自定义节点编辑器变得相对简单。本文将详细介绍如何使用GraphView实…...

【C++】开源:Armadillo数值计算库配置与使用

😏★,:.☆( ̄▽ ̄)/$:.★ 😏 这篇文章主要介绍Armadillo数值计算库配置与使用。 无专精则不能成,无涉猎则不能通。——梁启超 欢迎来到我的博客,一起学习,共同进步。 喜欢的朋友可以关注一下,下次更新不迷路🥞 文章目录 :smirk:1. Armadillo介绍:blush:2. 环境配置:s…...

HackMyVM-Airbind靶机的测试报告

目录 一、测试环境 1、系统环境 2、使用工具/软件 二、测试目的 三、操作过程 1、信息搜集 2、Getshell 3、提权 使用ipv6绕过iptables 四、结论 一、测试环境 1、系统环境 渗透机&#xff1a;kali2021.1(192.168.101.127) 靶 机&#xff1a;debian(192.168.101.11…...

C语言----函数

目录 1. 定义&#xff1a; 2.三要素 3.格式 4. 函数声明 5. 函数调用 6.函数传参 6.1. 值传递 6.2. 地址传递 6.3. 数组传递 string函数族 1.strcpy 2. strlen 3. strcat 4.strcmp 递归函数 1. 定义&#xff1a; 一个完成特定功能的代码模块 2.三要素 功能、…...

MySQL图形化界面工具--DataGrip

之前介绍了在命令行进行操作&#xff0c;但是不够直观&#xff0c;本次介绍图形化界面工具–DataGrip。 安装DataGrip 官网链接&#xff1a;官网下载链接 常规的软件安装流程。 参考链接&#xff1a;DataGrip安装 使用DataGrip 添加数据源&#xff1a; 第一次使用最下面会…...

PyTorch AMP 混合精度中grad_scaler.py的scale函数解析

PyTorch AMP 混合精度中的 scale 函数解析 混合精度训练&#xff08;AMP, Automatic Mixed Precision&#xff09;是深度学习中常用的技术&#xff0c;用于提升训练效率并减少显存占用。在 PyTorch 的 AMP 模块中&#xff0c;GradScaler 类负责动态调整和管理损失缩放因子&…...

【Ubuntu20.04】Apollo10.0 Docker容器部署+常见错误解决

官方参考文档【点击我】 Apollo 10.0 版本开始&#xff0c;支持本机和Docker容器两种部署方式。 如果您使用本机部署方式&#xff0c;建议使用x86_64架构的Ubuntu 22.04操作系统或者aarch64架构的Ubuntu 20.04操作系统。 如果您使用Docker容器部署方式&#xff0c;可以使用x…...

【文献精读笔记】Explainability for Large Language Models: A Survey (大语言模型的可解释性综述)(二)

****非斜体正文为原文献内容&#xff08;也包含笔者的补充&#xff09;&#xff0c;灰色块中是对文章细节的进一步详细解释&#xff01; 3.1.2 基于注意力的解释&#xff08;Attention-Based Explanation&#xff09; 注意力机制可以揭示输入数据中各个部分之间的关系&#…...

朱姆沃尔特隐身战舰:从失败到威慑

前言 "朱姆沃尔特"号驱逐舰是美国海军雄心勃勃的项目&#xff0c;旨在重塑未来海战。它融合了隐身、自动化和强大火力&#xff0c;然而由于技术问题和预算超支&#xff0c;原计划建造32艘的目标被大幅缩减&#xff0c;最终只建造了三艘。该舰的设计特点包括“穿浪逆船…...

免费分享 | 基于极光优化算法PLO优化宽度学习BLS实现光伏数据预测算法研究附Matlab代码

研究内容 宽度学习系统&#xff08;BLS&#xff09;简介&#xff1a; BLS是一种新型的神经网络结构&#xff0c;由增强节点&#xff08;Enhancement Nodes, ENs&#xff09;和特征节点&#xff08;Feature Nodes, FNs&#xff09;组成&#xff0c;具有结构简单、训练速度快、泛…...

logback日志文件多环境配置路径

项目中遇到问题&#xff0c;springboot项目 本地jar包部署到现场后&#xff0c;经常遇到现场的日志存放的路径会更改&#xff0c;经过查阅&#xff0c;有两种方式&#xff0c;下面简单说明一下。 一、第一种 启动jar包时 添加参数 --logging.configF:\hgtest\config\logback.x…...

面试高频:一致性hash算法

这两天看到技术群里&#xff0c;有小伙伴在讨论一致性hash算法的问题&#xff0c;正愁没啥写的题目就来了&#xff0c;那就简单介绍下它的原理。下边我们以分布式缓存中经典场景举例&#xff0c;面试中也是经常提及的一些话题&#xff0c;看看什么是一致性hash算法以及它有那些…...

docker部署项目

docker部署项目 &#xff08;加载tar包&#xff1a;docker image load -i mysql.tar&#xff09; 一、jdk环境配置 1.jdk下载地址 --Java Archive | Oracle 中国 --选择好版本进入 --下载Linux x64 Compressed Archive的链接 2.解压 --创建文件夹&#xff1a;mkdir /ro…...

每天40分玩转Django:Django Celery

Django Celery 一、知识要点概览表 模块知识点掌握程度要求Celery基础配置、任务定义、任务执行深入理解异步任务任务状态、结果存储、错误处理熟练应用周期任务定时任务、Crontab、任务调度熟练应用监控管理Flower、任务监控、性能优化理解应用 二、基础配置实现 1. 安装和…...

df.groupby(pd.Grouper(level=1)).sum()

df.groupby(pd.Grouper(level1)).sum() 在 Python 中的作用是根据 DataFrame 的某一索引级别进行分组&#xff0c;并计算每个分组的总和。具体来说&#xff1a; df.groupby(...)&#xff1a;这是 pandas 的分组操作&#xff0c;按照指定的规则将 DataFrame 分组。 pd.Grouper(…...

运动控制探针功能详细介绍(CODESYS+SV63N伺服)

汇川AM400PLC和禾川X3E伺服EtherCAT通信 汇川AM400PLC和禾川X3E伺服EtherCAT通信_汇川ethercat通信-CSDN博客文章浏览阅读1.2k次。本文详细介绍了如何使用汇川AM400PLC通过EtherCAT总线与禾川X3E伺服进行通信。包括XML硬件描述文件的下载与安装,EtherCAT总线的启用,从站添加…...

C语言基础18(GDB调试)

文章目录 GDBGDB概述什么是GDB**GDB**的主要功能 GDB的启动GDB常见的启动方式 GDB的退出GDB的常用命令GDB查看源代码指令———list(1)**GDB** 查看设置**------info****GDB** 查看内存**GDB** 设置断点**---break (b)****GDB** 设置观察点**---watch****GDB** 程序调试 GDB完整…...

《向量数据库指南》——应对ElasticSearch挑战,拥抱Mlivus Cloud的新时代

在当今数据驱动的商业环境中,向量数据库的应用正变得愈加重要。随着人工智能和机器学习的快速发展,尤其是在自然语言处理、图像识别及推荐系统等领域,向量数据库以其强大的存储和检索能力,迎来了广泛的应用机会。然而,在实际应用中,企业在选择和实施向量数据库方案时,常…...

c++的stl库中stack的解析和模拟实现

目录 1.stack的介绍和使用 1.1stack的介绍 1.2stack的使用 2.stack的模拟实现 1.stack的介绍和使用 1.1stack的介绍 1. stack 是一种容器适配器&#xff0c;专门用在具有后进先出操作的上下文环境中&#xff0c;其删除只能从容器的一端进行元素的插入与提取操作。 2. stac…...

C语言——字符函数和内存函数

目录 前言 字符函数 1strlen 模拟实现 2strcpy 模拟实现 3strcat 模拟实现 4strcmp 模拟实现 5strncpy 模拟实现 6strncat 模拟实现 7strncmp 模拟实现 8strstr 模拟实现 9strtok 10strerror 11大小写字符转换函数 内存函数 1memcpy 模拟实现 2…...

查询docker overlay2文件夹下的 c7ffc13c49xxx是哪一个容器使用的

问题背景 查询docker overlay2文件夹下的 c7ffc13c49xxx是哪一个容器使用的 [root@lnops overlay2]# du -sh * | grep G 1.7G 30046eca3e838e43d16d9febc63cc8f8bb3d327b4c9839ca791b3ddfa845e12e 435G c7ffc13c49a43f08ef9e234c6ef9fc5a3692deda3c5d42149d0070e9d8124f71 1.…...

Golang的容器编排实践

Golang的容器编排实践 一、Golang中的容器编排概述 作为一种高效的编程语言&#xff0c;其在容器编排领域也有着广泛的运用。容器编排是指利用自动化工具对容器化的应用进行部署、管理和扩展的过程&#xff0c;典型的容器编排工具包括Docker Swarm、Kubernetes等。在Golang中&a…...

【51项目】51单片机自制小霸王游戏机

视频演示效果: 纳新作品——小霸王游戏机 目录: 目录 视频演示效果: 目录: 前言:...

ArkTs之NAPI学习

1.Node-api组成架构 为了应对日常开发经的网络通信、串口访问、多媒体解码、传感器数据收集等模块&#xff0c;这些模块大多数是使用c接口实现的&#xff0c;arkts侧如果想使用这些能力&#xff0c;就需要使用node-api这样一套接口去桥接c代码。Node-api整体的架构图如下&…...