多参的实现原理

相信大家都使用过C语言的库函数:printf("%d%d", 1, 2)的吧,使用确实很方便功能也很强大。

但是为什么它可以接受多个参数呢?

现在我们来解析一下多参的实现原理,网上也找了一些文章。发现解析得都不全面。并且有BUG。

先看如下源码:

#include <windows.h>
#include <stdio.h>
#include <winnt.h>void MySprintf(char* szBuffer, const char* szFormat, ...)
{va_list pa;    // 定义一个指针va_start(pa, szFormat);  // 把指针赋值为第一个参数的值vsprintf(szBuffer, szFormat, pa);// va_end(pa); // 清空
}
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{char szBuffer[128] = {0};MySprintf(szBuffer, "%d%d%d", 1,2,3);return 0 ;
}

首先,我们函数压参顺序是重右往左,对应的栈空间内存地址从高到低。

MySprintf(szBuffer, "%d%d%d", 1,2,3);

这行代码,分别想栈中压入3, 2, 1, 然后再是szFormat的内存空间,在是szBuffer的内存空间.

内存结构如下:

 

熟悉函数调用的几步过程,压入参数,保存返回地址和栈顶,开辟局部变量空间.

现在保存了返回地址,然后继续执行的话,就是保存栈顶和开辟va_list pa所需的内存空间.

va_list其实也就是char* 类型。占4个字节。继续执行后如下:

看到了吗,va_start(pa, szFormat); 这条语句计算出了参数的起始地址,

它是如何计算出的呢?我们既然知道了内存布局, 那szFormat取地址+sizeof(va_list)。

说简单点也就是:szFormat的内存地址 + 4个字节. 不就刚好偏移到第一个参数的地址处了吗?

并且, 以上的MySprintf函数等同于下面这种写法:

void MySprintf(char* szBuffer, const char* szFormat, ...)
{char* pCh = NULL;pCh = (char*)&szFormat + sizeof(pCh);vsprintf(szBuffer, szFormat, pCh);pCh = NULL;
}

现在,我们得到了参数的内存首地址,但是还缺少信息。缺什么信息?

当前地址处有几个参数,每个参数什么类型(占用字节数)?

关键的地方就在这里了。szFormat中有类型信息信息,并且有类型信息的个数,我们可以通过遍历字符串,

找出类型信息的顺序和个数。然后根据遍历找到的信息。来解析参数的内存首地址。

具体的做法.在vsprintf中有实现,下面是拷贝vsprintf的实现代码,

#ifndef _COUNT_int __cdecl vsprintf (char *string,const char *format,va_list ap)
#else  /* _COUNT_ */int __cdecl _vsnprintf (char *string,size_t count,const char *format,va_list ap)
#endif  /* _COUNT_ */{FILE str;REG1 FILE *outfile = &str;REG2 int retval;_ASSERTE(string != NULL);_ASSERTE(format != NULL);outfile->_flag = _IOWRT|_IOSTRG;outfile->_ptr = outfile->_base = string;
#ifndef _COUNT_outfile->_cnt = MAXSTR;
#else  /* _COUNT_ */outfile->_cnt = count;
#endif  /* _COUNT_ *//*简单说明:关键代码处,大家直接跟进去即可,先是做些判断,然后设置一些标志位,最后把数字根据设置的标志转为字符串.*/retval = _output(outfile,format,ap );_putc_lk('\0',outfile);return(retval);
}

本人菜鸟,水平有限,望各路大牛指点!

转载于:https://www.cnblogs.com/ziolo/archive/2013/04/22/3036346.html

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

学生成绩统计c语言课程设计,学生成绩管理系统-C语言课程设计

记一次课程设计作业 学生成绩管理系统#include#include#include#include/*学生成绩管理系统介绍2017C语言课程设计 2021609361.支持将数据保存到文件并从文件中读取已有数据2.支持添加、删改数据3.支持按学号或姓名查询、删除、修改数据4.支持简单统计&#xff0c;包括 不及格…

LeetCode 439. 三元表达式解析器

文章目录1. 题目2. 解题2.1 递归2.2 循环1. 题目 给定一个以字符串表示的任意嵌套的三元表达式&#xff0c;计算表达式的值。 你可以假定给定的表达式始终都是有效的并且只包含数字 0-9, ?, :, T 和 F (T 和 F 分别表示真和假&#xff09;。 注意&#xff1a; 给定的字符串…

LeetCode 1216. 验证回文字符串 III(DP)

文章目录1. 题目2. 解题1. 题目 给出一个字符串 s 和一个整数 k&#xff0c;请你帮忙判断这个字符串是不是一个「K 回文」。 所谓「K 回文」&#xff1a;如果可以通过从字符串中删去最多 k 个字符将其转换为回文&#xff0c;那么这个字符串就是一个「K 回文」。 示例&#x…

c语言表达式10 amp 6等于多少,C语言基础——表达式

目录回顾&#xff1a;一、定义概念&#xff1a;二、表达式的分类举例说明&#xff1a;(一)首先是加、减、乘、除、取余四种表达式&#xff1a;(二)一元表达式(三)强制转换表达式(四)判断表达式(五)逻辑表达式(六)移位表达式小结写在最后&#xff1a;回顾&#xff1a;在上一篇博…

LeetCode 1274. 矩形内船只的数目(分治)

文章目录1. 题目2. 解题1. 题目 (此题是 交互式问题 ) 在用笛卡尔坐标系表示的二维海平面上&#xff0c;有一些船。 每一艘船都在一个整数点上&#xff0c;且每一个整数点最多只有 1 艘船。 有一个函数 Sea.hasShips(topRight, bottomLeft) &#xff0c;输入参数为右上角和…

图的定义与术语 - 数据结构和算法54

图的定义与术语 让编程改变世界 Change the world by program 在前边讲解的线性表中&#xff0c;每个元素之间只有一个直接前驱和一个直接后继&#xff0c;在树形结构中&#xff0c;数据元素之间是层次关系&#xff0c;并且每一层上的数据元素可能和下一层中多个元素相关&…

LeetCode 1152. 用户网站访问行为分析

文章目录1. 题目2. 解题1. 题目 为了评估某网站的用户转化率&#xff0c;我们需要对用户的访问行为进行分析&#xff0c;并建立用户行为模型。 日志文件中已经记录了用户名、访问时间 以及 页面路径。 为了方便分析&#xff0c;日志文件中的 N 条记录已经被解析成三个长度相…

python缺省参数与多个函数返回值

缺省参数 调用函数时&#xff0c;缺省参数的值如果没有传入&#xff0c;则被认为是默认值。 下例会打印默认的age&#xff0c;如果age没有被传入. def printinfo( name,age 35 ): # 打印任何传入的字符串 print "Name: ", name print "Age ", age #调…

如何用c语言制作飞机订票系统,C语言编程飞机订票系统如何设计?

题目&#xff1a;编制一个航空客运订票系统&#xff0c;实现简单的机票操作班级&#xff1a;计0702 姓名&#xff1a;学号&#xff1a; 完成日期&#xff1a;2008年12月20日一、 实验内容&#xff1a;1、问题描述&#xff1a;航空客运订票的业务包括&#xff1a;查询航班、客票…

挖掘有价值的搜索关键词

挖掘有价值的搜索关键词 在对搜索引擎流量及关键词的标记后&#xff0c;还需要从中挖掘最有价值的那部分关键词。以下是关键词挖掘思路和详细的操作方法。 图1 挖掘有价值的搜索关键词 找到一个有价值的关键词&#xff0c;再加上好的搜索排名&#xff0c;就可以给网站带来大量…

LeetCode 1197. 进击的骑士(BFS)

文章目录1. 题目2. 解题1. 题目 一个坐标可以从 -infinity 延伸到 infinity 的 无限大的 棋盘上&#xff0c;你的 骑士 驻扎在坐标为 [0, 0] 的方格里。 骑士的走法和中国象棋中的马相似&#xff0c;走 “日” 字&#xff1a;即先向左&#xff08;或右&#xff09;走 1 格&am…

LeetCode 1167. 连接棒材的最低费用(优先队列+贪心)

文章目录1. 题目2. 解题1. 题目 为了装修新房&#xff0c;你需要加工一些长度为正整数的棒材 sticks。 如果要将长度分别为 X 和 Y 的两根棒材连接在一起&#xff0c;你需要支付 X Y 的费用。 由于施工需要&#xff0c;你必须将所有棒材连接成一根。 返回你把所有棒材 sti…

flyme8会更新Android版本吗,魅族17系列升级Flyme 8.1操作系统:终于到Android 10

原标题&#xff1a;魅族17系列升级Flyme 8.1操作系统&#xff1a;终于到Android 10玩懂手机网资讯&#xff0c;根据魅族官方的消息&#xff0c;魅族17系列终于升级至Android 10&#xff0c;将会搭载 Flyme 8.1 操作系统&#xff0c;魅族官方发布消息表示让欢喜的&#xff0c;更…

python中常见的几种错误

python中常见的几种错误&#xff1a; 1、end前面一定加逗号 2、命令输入错误 3、冒号中英文切换 4、命令缩进错误 5、等于号要双等于&#xff0c;否则一个等于号是赋值 6、命令之间正确搭配

LeetCode 1181. 前后拼接(哈希map)

文章目录1. 题目2. 解题1. 题目 给你一个「短语」列表 phrases&#xff0c;请你帮忙按规则生成拼接后的「新短语」列表。 「短语」&#xff08;phrase&#xff09;是仅由小写英文字母和空格组成的字符串。「短语」的开头和结尾都不会出现空格&#xff0c;「短语」中的空格不会…

Android设置text按钮,安卓基础控件使用(TextView、Button、ImageView、EditText)

一、文本控件TextView1.布局文件android:text"string/content"android:layout_width"wrap_content"android:layout_height"wrap_content"android:textColor"color/green"android:textSize"dimen/title"android:lines"1…