研究不定数量参数的函数并实现一个printf函数
一、前提知识
1、如何传递参数(主函数)
a、函数的参数是通过栈传递,而且是从右到左依次入栈
b、即使是char型变量,在传递参数时,也是占用两个字节,因为push操作是两个字节为单位的。
c、showchar('a',2)这样的传入两个常数,也会在堆栈中开辟两个空间,也即对应两个实参变量。
2、函数如何接收参数(子函数)
a、 函数接受形参是通过从栈中取的
b、通过BP可以找到传入参数的值,BP+4是第一个参数,BP+6是第二个参数......取参数是从左到右取的
3、如何释放参数(主函数)
释放参数可以通过多次pop来实现。有时是通过“add sp,+数值”来实现的。
二、研究一个简单的不定数量参数的函数
测试代码
void showchar(int,char,...); main()
{
showchar(,,'a','b','c','d','e','f','g','h');
} void showchar(int n,char color,...)
{
int i;
char r;
for (i = ; i!=n; i++)
{
r = *(char *)(_BP++i+i);
r = color;
}
}
1、main函数
main()
{
showchar(,,'a','b','c','d','e','f','g','h');
}
对应的反汇编代码
2、showchar函数
void showchar(int n,char color,...)
{
int i;
char r;
for (i = ; i!=n; i++)
{
r = *(char *)(_BP++i+i);
r = color;
}
}
对应的反汇编代码
3、分析
函数通过参数一来控制显示字符的循环次数,通过这种方式来接收多个参数。
三、printf函数确定不定参数个数的方法
通过第一个参数所指向的字符串中%个数来确定不定参数的个数。
四、实现一个printf函数
1、包含printf函数、测试函数的C程序
extern void showenter(void); /* 在模块showchar.obj中定义 */
extern void showchar(char); /* 在模块showchar.obj中定义 */
/* 在光标位置显示字符,同时光标后移 */
static void printf(const char * str,...); /* 使用自己定义的printf函数 */
static void showint(int num); main()
{
printf("\nhello %c%c%cld!\n",'w','o','r');
printf("\n%c %d %c %d\n",'l',,'o',);
} /***************************************************************************
函数功能:支持%c、%d功能的printf函数
输入参数:str以及不定参数
前提知识:无论char型,还是int型,在传递参数时,都是入栈操作,而且都是以两个
字节入栈的。因为push指令只能以两个字节来操作。
实现思路:通过%来统计获取不定参数的个数,参数从堆栈中去获取
****************************************************************************/
static void printf(const char * str,...)
{
char strnum = ; /* 记录读出字符串str的位置 */
char paraaddr = ; /* 记录读出不定参数的位置 */ while(str[strnum]){ /* 取出字符为NULL,结束 */
if(str[strnum] == '%'){ /* 取出字符为%,看下一个字符 */
strnum++; /* 读取字符串的位置后移 */
switch(str[strnum]){ /* 根据%后边字符的值,选择不同的操作 */
case 'c':
showchar(*(char*)(_BP++paraaddr));/* 为c,则将一个char型大小的参数取出显示 */
paraaddr += ; /* 读取不定参数的位置后移 */
break;
case 'd':
showint(*(int*)(_BP++paraaddr)); /* 为d,则将一个int型大小的参数取出显示 */
paraaddr += ; /* 读取不定参数的位置后移 */
break;
default:
showchar('%'); /* 不是d,也不是c,就将之前的%显示 */
showchar(str[strnum]); /* 还要把当前字符显示 */
}
}
else /* 取出字符非%,直接显示 */
{
if(str[strnum] == '\n') /* 换行符 */
{
showenter(); /* 用函数showenter显示 */
}
else{
showchar(str[strnum]); /* 其他情况,直接显示 */
}
}
strnum++; /* 读取字符串的位置后移 */
}
}
/***************************************************************************
函数功能:显示整型数字
输入参数:num
实现思路:先将整型数字从个位依次向高位取数,存在堆栈中。显示的时候,从堆栈的
高位第一个非零数字开始显示。
存在问题:函数在VC6.0上能正确运行,但是在TC2.0上不能,比如说不能显示65535
猜测原因:VC6.0和TC2.0对整型的定义是不同的,前者是4个byte的存储空间,而后者只
有两个
****************************************************************************/
static void showint(int num)
{
char bufstk[]; /* 定义栈空间 */
char p; /* 栈顶 */ for(p = ; p < ; p++){
bufstk[p] = num % ; /* 从低位到高位依次入栈 */
num /= ;
} while((p > )&&(bufstk[--p] == )); /* 舍去高位为0的数字,但不舍弃num=0的个位0 */ do{
showchar(bufstk[p]+''); /* 显示有效数字 */
}
while(p--); /* 直到栈空 */
}
2、包含在光标位置显示一个字符和显示换行的汇编程序
PUBLIC _SHOWCHAR
PUBLIC _SHOWENTER
_TEXT SEGMENT BYTE PUBLIC 'CODE'
ASSUME CS: _TEXT
;==================================================================
;函数名称:showchar
;函数功能:显示一个字符
;输入参数:在堆栈中,具体说来是(返回地址+2),目的是为了和C程序无缝对接
;==================================================================
_SHOWCHAR PROC NEAR push ax ;用到的中间寄存器入栈保存
push cx
push bx
push bp mov bp,sp ;模拟C程序的反汇编程序
mov ah,0eh ;调用"int 10h"的第0e号功能,显示字符且光标后移
mov al,[bp+] ;字符
mov bl,07h ;颜色为黑底白字
mov bh, ;第0页
mov cx, ;重复1次
int 10h pop bp
pop bx
pop cx
pop ax
ret _SHOWCHAR ENDP ;==================================================================
;函数名称:showenter
;函数功能:显示'\n'
;输入参数:无
;==================================================================
_SHOWENTER PROC NEAR push bx
push ax
push dx mov bh, ;页号为0
mov ah, ;取当前光标位置
int 10h ;返回参数。DH=行号,DL=列号 inc dh ;行号加1
mov dl, ;列号为0
mov ah, ;置光标位置
int 10h ;入口参数。DH=行号,DL=列号 pop dx
pop ax
pop bx
ret _SHOWENTER ENDP
_TEXT ENDS
END
3、编译方法
研究不定数量参数的函数并实现一个printf函数的更多相关文章
- js闭包(函数内部嵌套一个匿名函数:这个匿名函数可将所在函数的局部变量常驻内存)
js闭包(函数内部嵌套一个匿名函数:这个匿名函数可将所在函数的局部变量常驻内存) 一.总结 1.闭包:就是在一个函数内部嵌套一个匿名函数,这个匿名函数可以访问这个函数的变量. 二.要点 闭包 闭包的相 ...
- 详解Python函数参数定义及传参(必备参数、关键字参数、默认可省略参数、可变不定长参数、*args、**kwargs)
详解Python函数参数定义及传参(必备参数.关键字参数.默认可省略参数.可变不定长参数.*args.**kwargs) Python函数参数传参的种类 Python中函数参数定义及调用函数时传参 ...
- java 不定长参数
一,不定长参数的规定 一个方法只能有一个不定长参数,并且这个不定长参数必须是该方法的最后一个参数. 示例: public class VariArgs { public static void mai ...
- JS基础语法---函数---介绍、定义、函数参数、返回值
函数: 把一坨重复的代码封装,在需要的时候直接调用即可 函数的作用: 代码的重用 函数需要先定义,然后才能使用 函数名字:要遵循驼峰命名法 函数一旦重名,后面的会把前面的函数覆盖 Ctrl +鼠标左键 ...
- c语言中,既然不支持函数重载,那么printf算怎么回事?在c语言中,它不就是被重载了吗?
这个问题问的不错.其实printf不是重载,c语言不支持函数重载 这句话是对的.printf函数是通过变长参数表实现的.你可以查看一下printf的函数原型声明.printf函数的实现在不同的机器上是 ...
- [转]printf 函数实现的深入剖析
研究printf的实现,首先来看看printf函数的函数体 int printf(const char *fmt, ...) { int i; char buf[256]; va_l ...
- s3c2440——实现裸机的简易printf函数
在单片机开发中,我们借助于vsprintf函数,可以自己实现一个printf函数,但是,那是IDE帮我们做了一些事情. 刚开始在ARM9裸机上自己写printf的实现的时候,包含对应头文件也会提示vs ...
- js中的匿名函数和匿名自执行函数
1.匿名函数的常见场景 js中的匿名函数是一种很常见的函数类型,比较常见的场景: <input type="button" value="点击" id ...
- STM32 printf()函数和scanf()函数重定向到串口
STM32 printf()函数和scanf()函数重定向到串口 printf()函数和scanf()函数重定向 在学习STM32的时候,常常需要用串口来测试代码的正确与否,这时候就要要用到print ...
随机推荐
- 403. Frog Jump
做完了终于可以吃饭了,万岁~ 假设从stone[i]无法跳到stone[i+1]: 可能是,他们之间的距离超过了stone[i]所能跳的最远距离,0 1 3 7, 从3怎么都调不到7: 也可能是,他们 ...
- Pencil-一个开源免费的UI原型工具,自带ios和android模板
Pencil是一个开源免费的UI原型工具,自带ios和android模板,可以很方便的做mockup. 下图是一个官方展示的模板:
- jquery 的小角落
最近换了工作,在这家公司里,使用了大量的jQuery,闲来无事看看锋利的jQuery,发现好多边边角角的选择器,却能省去一大堆逻辑上的的代码,废话不多说直接上代码. #### jquery 对象与do ...
- javascript数组基本方法
一.数组方法 1)concat 该方法用于连接两个或多个数组,返回连接成的新数组的副本,不会改变现有数组 [1,2,3].concat(5,6);//返回[1,2,3,5,6] 2)join 用于把数 ...
- 弹出窗口zDialog的使用
因为没有元素可以显示到Frameset上面去,所以重新定义了,一个index.htm,对其的操作是: Index.htm <script language="javascript ...
- JavaCodeTra 猴子选猴王 约瑟夫循环
之前用的是循环链表,java刚学,不知道怎么用链表.用个小算法吧 代码: import java.util.Scanner; /** * */ /** * @author john * @约瑟夫循环/ ...
- QStandardItemModel角色控制及QTreeView加入不同的右键菜单
1.概述 QTreeView最长用的一个功能就是作为导航栏,像vs里的项目结构树,word的文档结构图,资源管理器的文档结构,等等都是利用树形结构组织的,在前面已经讲述了Qt中使用标准化项目模型QSt ...
- 使用blktrace统计磁盘块I/O访问频率 + IO调度CFQ
http://blog.chinaunix.net/uid-24774106-id-4096470.html http://blog.csdn.net/wyzxg/article/details/74 ...
- web.xml常用标签整理(不定期更新)
<?xml version="1.0" encoding="UTF-8"?><!-- 标明使用的XML版本和文档编码,此项必须位于第一行,之前 ...
- [转] webpack debug in webstorm
先run build,然后用node server.js来做 WebStorm 11 adds support for debugging client-side apps built with We ...