从汇编分析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主从复制是基于二进制日…...
RestClient
什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端,它允许HTTP与Elasticsearch 集群通信,而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级ÿ…...
铭豹扩展坞 USB转网口 突然无法识别解决方法
当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...
Ubuntu系统下交叉编译openssl
一、参考资料 OpenSSL&&libcurl库的交叉编译 - hesetone - 博客园 二、准备工作 1. 编译环境 宿主机:Ubuntu 20.04.6 LTSHost:ARM32位交叉编译器:arm-linux-gnueabihf-gcc-11.1.0 2. 设置交叉编译工具链 在交叉编译之前&#x…...
蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...
抖音增长新引擎:品融电商,一站式全案代运营领跑者
抖音增长新引擎:品融电商,一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中,品牌如何破浪前行?自建团队成本高、效果难控;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...
2021-03-15 iview一些问题
1.iview 在使用tree组件时,发现没有set类的方法,只有get,那么要改变tree值,只能遍历treeData,递归修改treeData的checked,发现无法更改,原因在于check模式下,子元素的勾选状态跟父节…...
Python爬虫(二):爬虫完整流程
爬虫完整流程详解(7大核心步骤实战技巧) 一、爬虫完整工作流程 以下是爬虫开发的完整流程,我将结合具体技术点和实战经验展开说明: 1. 目标分析与前期准备 网站技术分析: 使用浏览器开发者工具(F12&…...
Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)
笔记整理:刘治强,浙江大学硕士生,研究方向为知识图谱表示学习,大语言模型 论文链接:http://arxiv.org/abs/2407.16127 发表会议:ISWC 2024 1. 动机 传统的知识图谱补全(KGC)模型通过…...
【无标题】路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论
路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论 一、传统路径模型的根本缺陷 在经典正方形路径问题中(图1): mermaid graph LR A((A)) --- B((B)) B --- C((C)) C --- D((D)) D --- A A -.- C[无直接路径] B -…...
