从汇编分析C语言可变参数的原理,并实现一个简单的sprintf函数
C语言可变参数
使用printf等函数的时候函数原型是printf(const char* fmt, ...), 这一类参数的个数不限的函数是可变参数
使用
使用一个头文件stdarg.h, 主要使用以下的宏
typedef char * va_list;// 把 n 圆整到 sizeof(int) 的倍数
#define _INTSIZEOF(n) ( (sizeof(n)+sizeof(int)-1) & ~(sizeof(int)-1) )// 初始化 ap 指针,使其指向第一个可变参数。v 是变参列表的前一个参数
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )// 使用type进行一个类型的转换,
#define va_arg(ap, type) ( *(type *)((ap += _INTSIZEOF(type)) - _INTSIZEOF(type)) )// /将指针 ap 置为无效,结束变参的获取
#define va_end(ap) ( ap = (va_list)0 )
C语言可变参数详解_c语音 可变参数-CSDN博客
在函数定义中创建一个 va_list 类型变量,该类型是在 stdarg.h 头文件中定义的。
使用 int 参数和 va_start() 宏来初始化 va_list 变量为一个参数列表。宏 va_start() 是在 stdarg.h 头文件中定义的。
使用 va_arg() 宏和 va_list 变量来访问参数列表中的每个项。
使用宏 va_end() 来清理赋予 va_list 变量的内存。
va_start(ap, last_arg):初始化可变参数列表。
ap是一个va_list类型的变量,last_arg是最后一个固定参数的名称(也就是可变参数列表之前的参数)。该宏将ap指向可变参数列表中的第一个参数。va_arg(ap, type):获取可变参数列表中的下一个参数。
ap是一个va_list类型的变量,type是下一个参数的类型。该宏返回类型为type的值,并将ap指向下一个参数。va_end(ap):结束可变参数列表的访问。
ap是一个va_list类型的变量。该宏将ap置为NULL。
原理
实际是从栈里面依次获取下一个字符串的指针地址

这里分析这两个函数的汇编代码

可以看出多出来的参数是依次被压入栈中进行传递的, 所以使用的时候可以使用最后一个参数的地址以及大小推导出来下一个参数的位置, 这里之后的参数实际是按照字符串指针的格式存储的
这里使用的栈是向下增长的, 但是压入的顺序是从右向左
如果使用fun(a, b)实际的栈里面是
再来看前面库函数
typedef char * va_list;// 把 n 调整到 sizeof(int) 的倍数(这一个是用来计数对齐的, 栈的存储是对齐的)
#define _INTSIZEOF(n) ( (sizeof(n)+sizeof(int)-1) & ~(sizeof(int)-1) )// 可以通过(va_list)&v获取已知最后一个参数的地址加上它的大小就是第一个参数的位置
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )// 使用type进行一个类型的转换, 转换的是现在的参数的地址, ap+=以后会指向下一个的位置
#define va_arg(ap, type) ( *(type *)((ap += _INTSIZEOF(type)) - _INTSIZEOF(type)) )// /将指针 ap 置为无效,结束变参的获取
#define va_end(ap) ( ap = (va_list)0 )
实现一个简单的sprintf(只处理%s)
void kernel_vsprintf(char * buf, const char *fmt, ...){va_list args;//记录第一个可变参数的位置va_start(args, fmt);//一个状态机enum {NORMAL, READ_FMT} state = NORMAL;char * curr = buf;char ch;while((ch = *fmt++)){switch(state) {case NORMAL:if(ch == '%'){//需要处理这一个格式字符state = READ_FMT;}else{//普通字符直接复制*curr++ = ch;}break;case READ_FMT:if(ch == 's'){//需要填入一个字符串//获取这一个字符串的地址const char * str = va_arg(args, char *);int len = strlen(str);while(len--){*curr++ = *str++;}}state = NORMAL;break;}}
}
相关文章:
从汇编分析C语言可变参数的原理,并实现一个简单的sprintf函数
C语言可变参数 使用printf等函数的时候函数原型是printf(const char* fmt, ...), 这一类参数的个数不限的函数是可变参数 使用 使用一个头文件stdarg.h, 主要使用以下的宏 typedef char * va_list;// 把 n 圆整到 sizeof(int) 的倍数 #define _INTSIZEOF(n) ( (sizeo…...
Word docx文件重命名为zip文件,解压后直接查看和编辑
一个不知道算不算冷的知识[doge]: docx格式的文件本质上是一个ZIP文件 当把一个.docx文件重命名为.zip文件并解压后,你会发现里面包含了一些XML文件和媒体文件,它们共同构成了Word文档的内容和格式。 例如,word/document.xml文件…...
SpringBoot中公共字段的自动填充
目录 1 前言 2 使用方法 2.1 自定义枚举类 2.2 自定义注解AutoFill 2.3 自定义切面类并设定切入点 2.4 切面类中设置前置通知,对公共字段赋值 2.5 在方法上添加自定义注解 3 最后 1 前言 在我们的项目中,项目表可能会有一些公共的字段需要我们的…...
【天衍系列 03】深入理解Flink的Watermark:实时流处理的时间概念与乱序处理
文章目录 01 基本概念02 工作原理03 优势与劣势04 核心组件05 Watermark 生成器 使用06 应用场景07 注意事项08 案例分析8.1 窗口统计数据不准8.2 水印是如何解决延迟与乱序问题?8.3 详细分析 09 项目实战demo9.1 pom依赖9.2 log4j2.properties配置9.3 Watermark水印…...
day07.C++类与对象
一.类与对象的思想 1.1面向对象的特点 封装、继承、多态 1.2类的概念 创建对象的过程也叫类的实例化。每个对象都是类的一个具体实例(Instance),拥有类的成员变量和成员函数。由{ }包围,由;结束。 class name{ //类的…...
String讲解
文章目录 String类的重要性常用的方法常用的构造方法String类的比较字符串的查找转化数字转化为字符串字符串转数字 字符串替换字符串的不可变性 字符串拆分字符串截取字符串修改 StringBuilder和StringBuffer String类的重要性 在c/c的学习中我们接触到了字符串,但…...
人群异常聚集监测系统-聚众行为检测与识别算法---豌豆云
聚众识别系统对指定区域进行实时监测,当监测到人群大量聚集、达到设置上限时,立即告警及时疏散。 旅游业作为国民经济战略性支柱产业,随着客流量不断增加,旅游景区和一些旅游城市的管理和服务面临着前所未有的挑战: …...
多模态基础---BERT
1. BERT简介 BERT用于将一个输入的句子转换为word_embedding,本质上是多个Transformer的Encoder堆叠在一起。 其中单个Transformer Encoder结构如下: BERT-Base采用了12个Transformer Encoder。 BERT-large采用了24个Transformer Encoder。 2. BERT的…...
图表示学习 Graph Representation Learning chapter2 背景知识和传统方法
图表示学习 Graph Representation Learning chapter2 背景知识和传统方法 2.1 图统计和核方法2.1.1 节点层次的统计和特征节点的度 节点中心度聚类系数Closed Triangles, Ego Graphs, and Motifs 图层次的特征和图的核节点袋Weisfieler–Lehman核Graphlets和基于路径的方法 邻域…...
OpenMVG(计算两个球形图像之间的相对姿态、细化重建效果)
目录 1 Bundle Adjustment(细化重建效果) 2 计算两个球形图像之间的相对姿态 1 Bundle Adjustment(细化重建效果) 数...
【QT+QGIS跨平台编译】之三十四:【Pixman+Qt跨平台编译】(一套代码、一套框架,跨平台编译)
文章目录 一、Pixman介绍二、文件下载三、文件分析四、pro文件五、编译实践一、Pixman介绍 Pixman是一款开源的软件库,提供了高质量的像素级图形处理功能。它主要用于在图形渲染、合成和转换方面进行优化,可以帮助开发人员在应用程序中实现高效的图形处理。 Pixman的主要特…...
2.17学习总结
tarjan 【模板】缩点https://www.luogu.com.cn/problem/P3387 题目描述 给定一个 �n 个点 �m 条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。 允许多次经过一条边或者…...
Unity类银河恶魔城学习记录7-7 P73 Setting sword type源代码
Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释,可供学习Alex教程的人参考 此代码仅为较上一P有所改变的代码 【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili Sword_Skill_Controller.cs using System.Collections; using System.Col…...
安卓版本与鸿蒙不再兼容,鸿蒙开发工程师招疯抢
最近,互联网大厂纷纷开始急招华为鸿蒙开发工程师。这是一个新的信号。在Android和iOS长期霸占市场的今天,鸿蒙的崛起无疑为整个行业带来了巨大的震动。 2023年11月10日,网易更新了高级/资深Android开发工程师岗位,职位要求参与云音…...
《白话C++》第9章 泛型,Page842~844 9.4.2 AutoPtr
源起: C编程中,最容易出的问题之一,就是内存泄露,而new一个对象,却忘了delete它,则是造成内存泄露的主要原因之一 例子一: void foo() {XXXObject* xo new XXXObject;if(!xo->DoSomethin…...
服务流控(Sentinel)
引入依赖 <!-- 必须的 --> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId> </dependency><!-- sentinel 核心库 --> <dependency><groupId>com.ali…...
点亮代码之灯,程序员的夜与电脑
在科技的海洋里,程序员是那些驾驶着代码船只,穿梭于虚拟世界的探险家。他们手中的键盘是航行的舵,而那台始终不愿关闭的电脑,便是他们眼中永不熄灭的灯塔。有人说,程序员不喜欢关电脑,这究竟是为什么呢&…...
ClickHouse--07--Integration 系列表引擎
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 Integration 系列表引擎1 HDFS1.1 语法1.2 示例: 2 MySQL2.1 语法2.2 示例: 3 Kafka3.1 语法3.2 示例:3.3 数据持久化方法 Integ…...
前端架构: 脚手架框架之yargs的11种基础核心特性的应用教程
脚手架框架之yargs的基础核心特性与应用 1 )概述 yargs 是脚手架当中使用量非常大的一个框架进入它的npm官网: https://www.npmjs.com/package/yargs 目前版本: 17.7.2Weekly Downloads: 71,574,188 (动态数据)最近更新:last month (github)说明这是一个…...
MySQL性能调优篇(6)-主从复制的配置与管理
MySQL数据库主从复制是一种常用的数据复制和高可用性解决方案。它允许将一个MySQL主服务器上的数据自动复制到多个从服务器上,从而提供了数据冗余备份、读写分离等优势。本文将详细介绍MySQL数据库主从复制的配置与管理。 1. 原理概述 MySQL主从复制是基于二进制日…...
【Oracle APEX开发小技巧12】
有如下需求: 有一个问题反馈页面,要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据,方便管理员及时处理反馈。 我的方法:直接将逻辑写在SQL中,这样可以直接在页面展示 完整代码: SELECTSF.FE…...
云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地
借阿里云中企出海大会的东风,以**「云启出海,智联未来|打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办,现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...
解锁数据库简洁之道:FastAPI与SQLModel实战指南
在构建现代Web应用程序时,与数据库的交互无疑是核心环节。虽然传统的数据库操作方式(如直接编写SQL语句与psycopg2交互)赋予了我们精细的控制权,但在面对日益复杂的业务逻辑和快速迭代的需求时,这种方式的开发效率和可…...
蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...
《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》
在注意力分散、内容高度同质化的时代,情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现,消费者对内容的“有感”程度,正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中࿰…...
从零实现STL哈希容器:unordered_map/unordered_set封装详解
本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说,直接开始吧! 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...
Axios请求超时重发机制
Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式: 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...
vue3+vite项目中使用.env文件环境变量方法
vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量,这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...
音视频——I2S 协议详解
I2S 协议详解 I2S (Inter-IC Sound) 协议是一种串行总线协议,专门用于在数字音频设备之间传输数字音频数据。它由飞利浦(Philips)公司开发,以其简单、高效和广泛的兼容性而闻名。 1. 信号线 I2S 协议通常使用三根或四根信号线&a…...
GO协程(Goroutine)问题总结
在使用Go语言来编写代码时,遇到的一些问题总结一下 [参考文档]:https://www.topgoer.com/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/goroutine.html 1. main()函数默认的Goroutine 场景再现: 今天在看到这个教程的时候,在自己的电…...
