当前位置: 首页 > 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. 安装和…...

stm32G473的flash模式是单bank还是双bank?

今天突然有人stm32G473的flash模式是单bank还是双bank&#xff1f;由于时间太久&#xff0c;我真忘记了。搜搜发现&#xff0c;还真有人和我一样。见下面的链接&#xff1a;https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...

以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:

一、属性动画概述NETX 作用&#xff1a;实现组件通用属性的渐变过渡效果&#xff0c;提升用户体验。支持属性&#xff1a;width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项&#xff1a; 布局类属性&#xff08;如宽高&#xff09;变化时&#…...

Cesium1.95中高性能加载1500个点

一、基本方式&#xff1a; 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...

macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用

文章目录 问题现象问题原因解决办法 问题现象 macOS启动台&#xff08;Launchpad&#xff09;多出来了&#xff1a;Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显&#xff0c;都是Google家的办公全家桶。这些应用并不是通过独立安装的…...

spring:实例工厂方法获取bean

spring处理使用静态工厂方法获取bean实例&#xff0c;也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下&#xff1a; 定义实例工厂类&#xff08;Java代码&#xff09;&#xff0c;定义实例工厂&#xff08;xml&#xff09;&#xff0c;定义调用实例工厂&#xff…...

解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错

出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上&#xff0c;所以报错&#xff0c;到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本&#xff0c;cu、torch、cp 的版本一定要对…...

C++中string流知识详解和示例

一、概览与类体系 C 提供三种基于内存字符串的流&#xff0c;定义在 <sstream> 中&#xff1a; std::istringstream&#xff1a;输入流&#xff0c;从已有字符串中读取并解析。std::ostringstream&#xff1a;输出流&#xff0c;向内部缓冲区写入内容&#xff0c;最终取…...

WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成

厌倦手动写WordPress文章&#xff1f;AI自动生成&#xff0c;效率提升10倍&#xff01; 支持多语言、自动配图、定时发布&#xff0c;让内容创作更轻松&#xff01; AI内容生成 → 不想每天写文章&#xff1f;AI一键生成高质量内容&#xff01;多语言支持 → 跨境电商必备&am…...

JDK 17 新特性

#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持&#xff0c;不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的&#xff…...