C++之va_start、vasprintf、va_end应用总结(二百二十六)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长!

优质专栏:Audio工程师进阶系列原创干货持续更新中……】🚀

人生格言: 人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药.

更多原创,欢迎关注:Android系统攻城狮

欢迎关注Android系统攻城狮

1.前言

本篇目的:C++之va_start、vasprintf、va_end应用总结。

在C/C++语言中,va_startvasprintfva_end是与可变参数相关的函数。它们通常与stdarg.h头文件一起使用。

  1. va_start函数:

    • va_start函数用于初始化可变参数列表。它接受两个参数:第一个参数是一个va_list类型的变量,第二个参数是最后一个固定参数的前一个参数的名称。
    • 这个函数将va_list类型变量初始化为指向可变参数列表的起始位置。
  2. vasprintf函数:

    • vasprintf函数用于将格式化的可变参数输出到动态分配的字符串中。
    • 它接受两个参数:第一个参数是一个指向char指针的指针,第二个参数是一个格式化字符串。
    • 这个函数会根据格式化字符串和可变参数列表生成一个动态分配的字符串,并将指针保存在第一个参数所指向的位置。
  3. va_end函数:

    • va_end函数用于结束可变参数的获取。
    • 它接受一个参数,即一个va_list类型的变量。
    • 这个函数对可变参数列表进行清理,使其无法再被访问。

通常在需要处理可变参数的情况下,使用实现Printf风格的函数或者处理命令行参数时。

2.应用实例

<1>.v1.0

#include <stdio.h>
#include <stdarg.h>void sum(int count, ...)
{va_list args;int total = 0;// 初始化可变参数列表va_start(args, count);// 遍历可变参数列表并求和for (int i = 0; i < count; i++){int num = va_arg(args, int);total += num;}// 结束可变参数列表va_end(args);printf("Sum: %d\n", total);
}int main()
{sum(3, 1, 2, 3);  // 输出 Sum: 6return 0;
}

va_start是一个宏,用于处理可变参数列表。它的原理是基于C语言的可变参数机制。

va_start宏的工作原理如下:

  1. 首先,通过传入的参数列表的类型信息和格式化字符串来确定参数列表的起始位置。

  2. 然后,通过指针运算和类型大小来计算出参数列表中各个参数的位置。

  3. 最后,将参数列表位置信息保存在一个va_list类型的变量中,供后续函数进行处理。

va_start宏的实现通常依赖于编译器和操作系统的底层支持。它使用了一些机制来获取参数列表的信息,如栈帧结构、类型信息、指针运算等。

具体实现的细节可能因编译器和操作系统而异,但通常会涉及以下步骤:

  1. 获取栈帧指针:va_start宏需要通过访问栈来获取参数列表的位置信息。它首先获取当前函数的栈帧指针,可以通过编译器提供的相关函数或底层汇编指令来实现。

  2. 计算参数列表位置:通过栈帧指针和参数类型大小,va_start宏可以计算出参数列表中各个参数的位置。这通常涉及指针运算,根据参数类型大小来移动指针位置。

  3. 保存参数列表信息:va_start宏将参数列表位置信息保存在一个va_list类型的变量中,以便后续的函数进行处理。可以将栈帧指针和计算得到的参数列表位置信息存储在va_list变量中。

va_start宏使用一些编译器和操作系统提供的底层机制来实现可变参数的处理。具体实现可能会因编译器和操作系统的不同而异,也可能受到编译器的优化策略的影响。

<2>.v2.0

#include <stdio.h>
#include <stdarg.h>char* format_string(const char* format, ...)
{va_list args;char* str;// 初始化可变参数列表va_start(args, format);// 格式化字符串并存储到动态分配的字符串中int length = vasprintf(&str, format, args);// 结束可变参数列表va_end(args);printf("Formatted String: %s\n", str);printf("String Length: %d\n", length);return str;
}int main()
{char* result = format_string("Hello %s, your age is %d.", "John", 25);// 输出 Formatted String: Hello John, your age is 25.// 输出 String Length: 30free(result);  // 释放动态分配的字符串return 0;
}

va_end是一个宏,用于结束对可变参数列表的访问。它的原理是基于C语言的可变参数机制。

va_end宏的工作原理如下:

  1. 首先,它接收一个va_list类型的变量作为参数,该变量保存了参数列表的位置信息。

  2. 接着,通过一系列机制(如编译器和操作系统的底层支持)实现将参数列表进行清理和释放的操作。

  3. 最后,将va_list类型的变量标记为无效,以确保之后对它的访问是无效的。

具体实现的细节可能因编译器和操作系统而异,但通常会涉及以下步骤:

  1. 清理参数列表:va_end宏根据参数列表的位置信息,使用特定的机制将参数列表进行清理。它可能涉及释放资源、清理栈上的参数值等操作。具体的清理操作由编译器和操作系统提供的底层支持来实现。

  2. 使va_list变量失效:为了确保在之后对va_list变量的访问是无效的,va_end宏将该变量标记为无效。这样可以避免误用已经结束的参数列表。

va_end宏的实现依赖于编译器和操作系统的底层支持。具体实现的细节可能会因编译器和操作系统的不同而异。但无论实现细节如何,va_end宏的目标都是确保正确清理和释放可变参数列表所占用的资源,并将va_list变量标记为无效。

<3>.v3.0

#include <iostream>
#include <cstdarg>
#include <cstdio>
#include <cstdlib>
#include <string>using namespace std;string AStringPrintf(const char *format, ...) {va_list ap;va_start(ap, format);char *buffer;int bufferSize = vasprintf(&buffer, format, ap);va_end(ap);if(bufferSize < 0) {return string();}string result(buffer);free(buffer);buffer = NULL;return result;
}int main(){char str[64]={1,2,3,5,6,7,8,9};string buf =  AStringPrintf(str);printf("buf = %s\n",buf.c_str());
}
  1. vsnprintf函数接收一个格式化字符串和一个va_list类型的参数列表。利用格式化字符串,它将参数列表中的数据转换为相应的字符串格式。

  2. 首先,vsnprintf函数根据格式化字符串中的格式指示符,逐个读取参数列表的值,并根据指示符的要求对其进行格式化。

  3. 然后,vsnprintf函数根据格式化的结果,将其写入到指定的缓冲区中。它会确保写入的数据不超过指定的缓冲区大小。

  4. 最后,vsnprintf函数会在写入完成后,返回实际写入的字符数(不包括结尾的’\0’)。如果缓冲区大小不足以容纳格式化后的字符串,vsnprintf函数会截断字符串,但仍然会返回实际要写入的字符数。

需要注意的是,由于vsnprintf函数是经过格式化而得到的字符串的长度是不确定的,因此在使用之前需要分配足够的缓冲区来存储格式化后的字符串,以避免缓冲区溢出的情况发生。

vsnprintf函数是一个用于以固定格式将可变参数格式化为字符串的函数。它将格式化的结果写入指定的缓冲区,并返回实际写入的字符数。这个函数在C++中可用于处理可变参数,并将其转换为字符串格式。

<4>.v4.0

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>void dynamic_print(const char* format, ...)
{va_list args;va_start(args, format);char* dyn_str = NULL;size_t str_length = 0;// 计算动态生成字符串所需的长度str_length = vsnprintf(NULL, 0, format, args);// 为字符串动态分配内存dyn_str = (char*)malloc(str_length + 1);// 动态生成字符串vsnprintf(dyn_str, str_length + 1, format, args);printf("%s", dyn_str);free(dyn_str);va_end(args);
}int main(){int num1 = 10;int num2 = 20;dynamic_print("Sum: %d + %d = %d\n", num1, num2, num1 + num2);return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/141208.html

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

相关文章

【算法】排序——插入排序及希尔排序

目录 前言 一、排序的概念及其应用 1.1排序的概念 1.2排序的应用 1.3常见的排序算法 二、插入排序的实现 基于插入排序的优化——希尔排序&#xff08;缩小增量排序 个人主页 代码仓库 C语言专栏 初阶数据结构专栏 Linux专栏 LeetCode刷题 算法专栏 前言 这…

Neo4j图数据库_web页面关闭登录实现免登陆访问_常用的cypher语句_删除_查询_创建关系图谱---Neo4j图数据库工作笔记0013

由于除了安装,那么真实使用的时候,就是导入数据了,有了关系和节点的csv文件以后如果用 cypher进行导入数据和创建关系图谱,还有进行查询,以及如果导入错误如何清空,大概是这些 用的最多的,单独把这些拿进来,总结一下,用的会比较方便. 1.实现免登陆访问: /data/module/neo4j-…

基于微信小程序的在线小说阅读系统,附数据库、教程

1 功能简介 Java基于微信小程序的在线小说阅读系统 微信小程序的在线小说阅读系统&#xff0c;系统的整体功能需求分为两部分&#xff0c;第一部分主要是后台的功能&#xff0c;后台功能主要有小说信息管理、注册用户管理、系统系统等功能。微信小程序主要分为首页、分类和我的…

【数据结构--排序】堆排序

&#x1f490; &#x1f338; &#x1f337; &#x1f340; &#x1f339; &#x1f33b; &#x1f33a; &#x1f341; &#x1f343; &#x1f342; &#x1f33f; &#x1f344;&#x1f35d; &#x1f35b; &#x1f364; &#x1f4c3;个人主页 &#xff1a;阿然成长日记 …

Linux的socket通信

关于套接字通信定义如下&#xff1a; 套接字对应程序猿来说就是一套网络通信的接口&#xff0c;使用这套接口就可以完成网络通信。网络通信的主体主要分为两部分&#xff1a;客户端和服务器端。在客户端和服务器通信的时候需要频繁提到三个概念&#xff1a;IP、端口、通信数据&…

测试C#图像文本识别模块Tesseract的基本用法

微信公众号“dotNET跨平台”的文章《c#实现图片文体提取》&#xff08;参考文献3&#xff09;介绍了C#图像文本识别模块Tesseract&#xff0c;后者是tesseract-ocr&#xff08;参考文献2&#xff09; 的C#封装版本&#xff0c;目前版本为5.2&#xff0c;关于Tesseract的详细介绍…

马尔可夫链预测举例——钢琴销售的存贮策略

问题概述 一家钢琴专卖店&#xff0c;根据以往的销售经验&#xff0c;平均每周只能售出一架钢琴&#xff0c;现在经理指定的存贮策略是&#xff0c;每周末检查库存存量&#xff0c;仅当库存量为零时&#xff0c;才订购3架供下周销售&#xff1b;否则就不订购。试估计这种策略下…

pytest一些常见的插件

Pytest拥有丰富的插件架构&#xff0c;超过800个以上的外部插件和活跃的社区&#xff0c;在PyPI项目中以“ pytest- *”为标识。 本篇将列举github标星超过两百的一些插件进行实战演示。 插件库地址&#xff1a;http://plugincompat.herokuapp.com/ 1、pytest-html&#xff1…

数据库——理论基础

目录 1.1 什么是数据库 1.2 数据库管理系统&#xff08;DBMS&#xff09; 1.3 数据库和文件系统的区别 1.4 数据库的发展史 1.5常见的数据库 1.5.1关系型数据库 1.5.2 非关系型数据库 1.6 DBMS支持的数据模型 1.1 什么是数据库 数据&#xff1a;描述事物的符号记录 数…

opencv实现仿射变换和透射变换

##1&#xff0c; 什么是仿射变换&#xff1f; 代码实现 import numpy as np import cv2 as cv import matplotlib.pyplot as plt#设置字体 from pylab import mpl mpl.rcParams[font.sans-serif] [SimHei]#图像的读取 img cv.imread("lena.png")#仿射变换 row…

clickhouse简单安装部署

目录 前言(来源于官方文档)&#xff1a; 一.下载并上传 1.下载地址&#xff1a;点我跳转下载 2.上传至Linux 二.解压和配置 1.解压顺序 注意&#xff1a;必须按照以下顺序解压&#xff0c;并且每解压一个都要执行该解压后文件的install/doinst.sh文件 解压步骤&#xff…

Mycat管理及监控

Mycat管理 -h 是你自己的ip地址 相关命令及含义 Mycat-eye(图形化界面监控) 仅限于Linux系统

基于Xml方式Bean的配置-初始化方法和销毁方法

SpringBean的配置详解 Bean的初始化和销毁方法配置 Bean在被实例化后&#xff0c;可以执行指定的初始化方法完成一些初始化的操作&#xff0c;Bean在销毁之前也可以执行指定的销毁方法完成一些操作&#xff0c;初始化方法名称和销毁方法名称通过 <bean id"userService…

Spring Boot 日志文件

前言 本篇博客主要介绍自定义的日志打印、日志的级别高低、如何保存日志等等..... 一、日志是什么&#xff1f;日志有什么用&#xff1f; 日志就是我们控制台上输出的内容&#xff0c;控制台上的输出的信息就是日志信息&#xff0c;如下所示&#xff1a; 日志有什么用&#x…

【Vue2.0源码学习】生命周期篇-挂载阶段

文章目录 1. 前言2. 挂载阶段分析3. 总结 1. 前言 模板编译阶段完成之后&#xff0c;接下来就进入了挂载阶段&#xff0c;从官方文档给出的生命周期流程图中可以看到&#xff0c;挂载阶段所做的主要工作是创建Vue实例并用其替换el选项对应的DOM元素&#xff0c;同时还要开启对…

c语言基础知识+OS+数据结构

c语言&#xff1a; memory section&#xff1a; .bss&#xff1a; uninitialized or zero-initialized global and static variables .data: initialized global and static variables .text: Read only, code and const C语言编译流程&#xff1a; pre-compiler: …

在创业公司,我靠它续命 ...

不知不觉就在新公司工作了一周&#xff0c;没有想象中那么难受。创业公司里没有复杂的人际关系&#xff0c;也没有无聊的会议&#xff0c;更没有复杂的流程。每天上班第一件事就是开个小会&#xff0c;可能是站着开&#xff0c;也可能是连麦开。大家简单过一下前一天的进度&…

vue-cli创建项目、vue项目目录结(运行vue项目)、ES6导入导出语法、vue项目编写规范

vue-cli创建项目、vue项目目录结构、 ES6导入导出语法、vue项目编写规范 1 vue-cli创建项目 1.1 vue-cli 命令行创建项目 1.2 使用vue-cli-ui创建 2 vue项目目录结构 2.1 运行vue项目 2.2 vue项目的目录结构 3 es6导入导出语法 4 vue项目编写规范 4.1 修改项目 4.2 以后…

LabVIEW崩溃后所产生的错误日志文件的位置

LabVIEW崩溃后所产生的错误日志文件的位置 LabVIEW开发环境刚刚崩溃&#xff0c;请问我如何访问崩溃后自动生成的日志文件&#xff1f; LabVIEW崩溃后产生的转储文件位于何处&#xff1f; 代码导致了LabVIEW崩溃&#xff0c;请问哪些文件可以帮助NI技术支持了解具体原因&…