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 语言提供以下专门的机制用来处理可变参数:
-
va_list
- 它是一种类型,用来声明一个存储可变参数信息的变量。例如:
va_list args;
- 它是一种类型,用来声明一个存储可变参数信息的变量。例如:
-
va_start(va_list, last_fixed_param)
- 用来初始化这个
va_list
变量,让它从函数形参中“最后一个固定参数”后面开始读取不定参数。 - 例如:
void exampleFunction(const char *format, ...) {va_list args;va_start(args, format); // 初始化args,从format后面获取可变参数... }
- 用来初始化这个
-
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*); // 取一个字符串指针
- 用来按指定类型依次获取下一个可变参数值。例如:
-
va_end(va_list)
- 用来清理可变参数列表。当函数对不定参数读取完毕后,应当调用
va_end(args)
。 - 例如:
va_end(args);
- 用来清理可变参数列表。当函数对不定参数读取完毕后,应当调用
-
va_copy(va_list dest, va_list src)
(C99 引入)- 用来复制一个可变参数列表(在某些情形需要重复遍历参数时使用)。
3. va_list
的用途
在一个可变参数函数内部,当你想要处理形如 (const char *fmt, ...)
中的 “..." 部分,就必须:
- 声明一个
va_list
变量 - 用
va_start(args, last_fixed_param)
初始化它 - 根据需要多次调用
va_arg(args, type)
获取后续的每个不定参数 - 在处理完后,调用
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. 与 printf
、vsnprintf
等函数的关系
标准库的 printf
、fprintf
、vsnprintf
等都使用了可变参数机制:
-
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”系列函数(如vprintf
、vsnprintf
)正是可变参数 +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;
}
在这里:
- 我们的函数定义了一个不定参数
(const char *at_template, ...)
。 - 函数内部声明
va_list args;
。 - 调用
va_start(args, at_template);
来初始化args
。 - 然后把
args
交给vsnprintf
来格式化到cmd
中。 - 最后
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. 常见问题
-
为什么要
va_end
?- 这是可变参数的协议要求,用于清理资源或使处理器栈保持一致。忽略可能导致移植性问题。
-
如果不知道参数数量咋办?
- 一般通过format字符串或某个“终止标识”来知道需要获取多少参数,与
printf
同理。C 语言本身不自动帮你检测参数数量。
- 一般通过format字符串或某个“终止标识”来知道需要获取多少参数,与
-
va_arg(args, type)
中的type
必须跟传入参数类型一致吗?- 必须一致,否则会导致解析错误、内存访问混乱。
-
变长宏(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)
。 printf
、snprintf
、vsnprintf
等函数就是使用可变参数(或va_list
)来实现的,在变长参数的解析或格式化方面非常常见。
相关文章:
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 语言中&am…...

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

智汇厦门:苏哒智能携其智能化产品亮相文心中国行现场
2025年1月2日,文心中国行再次踏足美丽的鹭岛厦门。 本次的文心中国行活动不仅有来自政府、高校及企业的精英专家将齐聚一堂,分享AI与大模型的最新研究成果,还正式揭牌百度飞桨(厦门)人工智能产业赋能中心,…...
C++函数模板的定义为何要和调用点放在一起
在C中,模板的声明最好和调用放在一起,或者确保编译器在进行模板实例化时能看到模板完整的定义,主要有以下几方面原因: 一、模板实例化机制的需求 编译时实例化特点 C模板是在编译阶段根据实际使用时传入的类型参数进行实例化&am…...

Nginx - 整合lua 实现对POST请求的参数拦截校验(不使用Openresty)
文章目录 概述步骤 1: 安装 Nginx 和 Lua 模块步骤 2: 创建 Lua 脚本用于参数校验步骤 3: 配置 Nginx 使用 Lua 脚本写法二: 状态码写法三 : 返回自定义JSON复杂的正则校验 步骤 4: 测试和验证ngx.HTTP_* 枚举值 概述 一个不使用 OpenResty 的 Nginx 集…...

互联网直播点播平台EasyDSS无人机视频推拉流技术实现工地远程监控巡检直播
在建筑行业,施工现场的安全管理和实时监控一直是项目管理中的重点。随着技术的进步,无人机工地直播技术成为了一种新兴的解决方案,它不仅能够提高施工透明度,还能够加强现场安全管理。EasyDSS作为一种先进的流媒体技术平台&#x…...
Unity3D 基于GraphView实现的节点编辑器框架详解
前言 在Unity3D游戏开发中,节点编辑器是一种强大的工具,它允许开发者以可视化的方式创建和编辑复杂的逻辑和流程。Unity提供了一个强大的UI工具包——GraphView,它使得创建自定义节点编辑器变得相对简单。本文将详细介绍如何使用GraphView实…...
【C++】开源:Armadillo数值计算库配置与使用
😏★,:.☆( ̄▽ ̄)/$:.★ 😏 这篇文章主要介绍Armadillo数值计算库配置与使用。 无专精则不能成,无涉猎则不能通。——梁启超 欢迎来到我的博客,一起学习,共同进步。 喜欢的朋友可以关注一下,下次更新不迷路🥞 文章目录 :smirk:1. Armadillo介绍:blush:2. 环境配置:s…...

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

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

MySQL图形化界面工具--DataGrip
之前介绍了在命令行进行操作,但是不够直观,本次介绍图形化界面工具–DataGrip。 安装DataGrip 官网链接:官网下载链接 常规的软件安装流程。 参考链接:DataGrip安装 使用DataGrip 添加数据源: 第一次使用最下面会…...
PyTorch AMP 混合精度中grad_scaler.py的scale函数解析
PyTorch AMP 混合精度中的 scale 函数解析 混合精度训练(AMP, Automatic Mixed Precision)是深度学习中常用的技术,用于提升训练效率并减少显存占用。在 PyTorch 的 AMP 模块中,GradScaler 类负责动态调整和管理损失缩放因子&…...

【Ubuntu20.04】Apollo10.0 Docker容器部署+常见错误解决
官方参考文档【点击我】 Apollo 10.0 版本开始,支持本机和Docker容器两种部署方式。 如果您使用本机部署方式,建议使用x86_64架构的Ubuntu 22.04操作系统或者aarch64架构的Ubuntu 20.04操作系统。 如果您使用Docker容器部署方式,可以使用x…...

【文献精读笔记】Explainability for Large Language Models: A Survey (大语言模型的可解释性综述)(二)
****非斜体正文为原文献内容(也包含笔者的补充),灰色块中是对文章细节的进一步详细解释! 3.1.2 基于注意力的解释(Attention-Based Explanation) 注意力机制可以揭示输入数据中各个部分之间的关系&#…...

朱姆沃尔特隐身战舰:从失败到威慑
前言 "朱姆沃尔特"号驱逐舰是美国海军雄心勃勃的项目,旨在重塑未来海战。它融合了隐身、自动化和强大火力,然而由于技术问题和预算超支,原计划建造32艘的目标被大幅缩减,最终只建造了三艘。该舰的设计特点包括“穿浪逆船…...
免费分享 | 基于极光优化算法PLO优化宽度学习BLS实现光伏数据预测算法研究附Matlab代码
研究内容 宽度学习系统(BLS)简介: BLS是一种新型的神经网络结构,由增强节点(Enhancement Nodes, ENs)和特征节点(Feature Nodes, FNs)组成,具有结构简单、训练速度快、泛…...
logback日志文件多环境配置路径
项目中遇到问题,springboot项目 本地jar包部署到现场后,经常遇到现场的日志存放的路径会更改,经过查阅,有两种方式,下面简单说明一下。 一、第一种 启动jar包时 添加参数 --logging.configF:\hgtest\config\logback.x…...

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

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

每天40分玩转Django:Django Celery
Django Celery 一、知识要点概览表 模块知识点掌握程度要求Celery基础配置、任务定义、任务执行深入理解异步任务任务状态、结果存储、错误处理熟练应用周期任务定时任务、Crontab、任务调度熟练应用监控管理Flower、任务监控、性能优化理解应用 二、基础配置实现 1. 安装和…...
DockerHub与私有镜像仓库在容器化中的应用与管理
哈喽,大家好,我是左手python! Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库,用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...

Python:操作 Excel 折叠
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...

如何在看板中体现优先级变化
在看板中有效体现优先级变化的关键措施包括:采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中,设置任务排序规则尤其重要,因为它让看板视觉上直观地体…...

(二)原型模式
原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...

MySQL 8.0 OCP 英文题库解析(十三)
Oracle 为庆祝 MySQL 30 周年,截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始,将英文题库免费公布出来,并进行解析,帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...
鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/
使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题:docker pull 失败 网络不同,需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...

IT供电系统绝缘监测及故障定位解决方案
随着新能源的快速发展,光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域,IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选,但在长期运行中,例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...
MySQL账号权限管理指南:安全创建账户与精细授权技巧
在MySQL数据库管理中,合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号? 最小权限原则…...
Android第十三次面试总结(四大 组件基础)
Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成,用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机: onCreate() 调用时机:Activity 首次创建时调用。…...
Go 语言并发编程基础:无缓冲与有缓冲通道
在上一章节中,我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道,它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好࿰…...