一、前提知识

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、编译方法

 
   《汇编语言》326页 研究实验5 “函数如何接收不定数量的参数”
 

研究不定数量参数的函数并实现一个printf函数的更多相关文章

  1. js闭包(函数内部嵌套一个匿名函数:这个匿名函数可将所在函数的局部变量常驻内存)

    js闭包(函数内部嵌套一个匿名函数:这个匿名函数可将所在函数的局部变量常驻内存) 一.总结 1.闭包:就是在一个函数内部嵌套一个匿名函数,这个匿名函数可以访问这个函数的变量. 二.要点 闭包 闭包的相 ...

  2. 详解Python函数参数定义及传参(必备参数、关键字参数、默认可省略参数、可变不定长参数、*args、**kwargs)

    详解Python函数参数定义及传参(必备参数.关键字参数.默认可省略参数.可变不定长参数.*args.**kwargs) Python函数参数传参的种类   Python中函数参数定义及调用函数时传参 ...

  3. java 不定长参数

    一,不定长参数的规定 一个方法只能有一个不定长参数,并且这个不定长参数必须是该方法的最后一个参数. 示例: public class VariArgs { public static void mai ...

  4. JS基础语法---函数---介绍、定义、函数参数、返回值

    函数: 把一坨重复的代码封装,在需要的时候直接调用即可 函数的作用: 代码的重用 函数需要先定义,然后才能使用 函数名字:要遵循驼峰命名法 函数一旦重名,后面的会把前面的函数覆盖 Ctrl +鼠标左键 ...

  5. c语言中,既然不支持函数重载,那么printf算怎么回事?在c语言中,它不就是被重载了吗?

    这个问题问的不错.其实printf不是重载,c语言不支持函数重载 这句话是对的.printf函数是通过变长参数表实现的.你可以查看一下printf的函数原型声明.printf函数的实现在不同的机器上是 ...

  6. [转]printf 函数实现的深入剖析

    研究printf的实现,首先来看看printf函数的函数体 int printf(const char *fmt, ...) { int i; char buf[256];          va_l ...

  7. s3c2440——实现裸机的简易printf函数

    在单片机开发中,我们借助于vsprintf函数,可以自己实现一个printf函数,但是,那是IDE帮我们做了一些事情. 刚开始在ARM9裸机上自己写printf的实现的时候,包含对应头文件也会提示vs ...

  8. js中的匿名函数和匿名自执行函数

    1.匿名函数的常见场景 js中的匿名函数是一种很常见的函数类型,比较常见的场景:   <input type="button" value="点击" id ...

  9. STM32 printf()函数和scanf()函数重定向到串口

    STM32 printf()函数和scanf()函数重定向到串口 printf()函数和scanf()函数重定向 在学习STM32的时候,常常需要用串口来测试代码的正确与否,这时候就要要用到print ...

随机推荐

  1. Java菜鸟学习笔记--数组篇(二):数组实例&args实例

    基本类型实例 //1.定义一个一维数组,先声明,在分配空间 int []number;//生命,没有初始化,number=null number=new int[5];//初始化为默认值,int默认值 ...

  2. python_list和tuple互转

    Python中,tuple和list均为内置类型, 以list作为参数将tuple类初始化,将返回tuple类型 tuple([1,2,3,4]) list->tuple 以tuple做为参数, ...

  3. js事件的方法

    事件的方法:onclick         鼠标单击ondblclick       鼠标双击onkeyup         按下并释放键盘上的一个键时触发 onchange        文本内容或 ...

  4. java格式处理工具类

    import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOExceptio ...

  5. Java web 文件下载

    /** * 下载文件 * @param msg */ public boolean printOutFile(String fileFullName,String fileName) { if (fi ...

  6. Java基础知识强化之集合框架笔记24:ArrayList存储自定义对象并遍历

    1. ArrayList存储自定义对象并遍历 2. 代码示例: Student.java,如下: package cn.itcast_01; public class Student { privat ...

  7. 10.7 noip模拟试题

    楼[问题背景]zhx 为他的妹子造了一幢摩天楼.[问题描述]zhx 有一幢摩天楼. 摩天楼上面有 M 个观光电梯,每个观光电梯被两个整数

  8. Jquery 替换全部花括号

    js:var str="{aaa}123{aaa}";str.replace(/\{aaa\}/g, "111");

  9. java开发规范总结_命名规范

    规范需要平时编码过程中注意,是一个慢慢养成的好习惯 1.文件 1.属性文件后缀为properties,并且符合java中i18n的规范:   2.对于各产品模块自己的配置文件必须放置在自己模块的con ...

  10. 在xcode6.1和ios10.10.1环境下实现app发布

    之前写过在xcode6.1和ios10.10.1环境下实现真机测试,以及最近提交的app一直在审核当中,所以木有发布如何实现app发布来分享给大家.刚好昨天app审核通过了,所以就分享一篇如何实现ap ...