一、前提知识

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. VMware虚拟机里Ubuntu14.04下安装及配置MySQL

    更新源列表 快捷键"Ctrl+Alt+t"打开"Terminal终端窗口",输入"sudo apt-get update"-->回车- ...

  2. 2的32次方 分类: C#小技巧 2014-08-05 18:18 406人阅读 评论(0) 收藏

    版权声明:本文为博主原创文章,未经博主允许不得转载.

  3. Python基础知识--列表和集合

    列表:有序性,可以存放任意类型的对象,通过索引访问,可以分片操作 >>> L = ['id', 1000, 'scd', 1000, 'scd'] >>> L [' ...

  4. JDK6 下载地址

    http://www.oracle.com/technetwork/java/javasebusiness/downloads/java-archive-downloads-javase6-41940 ...

  5. 构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(39)-在线人数统计探讨

    原文:构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(39)-在线人数统计探讨 系列目录 基于web的网站在线统计一直处于不是很精准的状态!基本上没有一种方法可 ...

  6. HDU2699+Easy

    简单题. /* */ #include<stdio.h> #include<string.h> #include<stdlib.h> #include<alg ...

  7. IOS-NSDateFormatter使用介绍

    IOS-NSDateFormatter使用介绍 NSDateFormatter的使用: NSDate *nowDate = [[NSDate alloc] init]; NSDateFormatter ...

  8. AS【常用插件】

    安装插件,Settings -->[Plugins]-->搜索-->点击install-->重启AS 禁用插件,右侧面板会显示出已经安装的插件列表,取消勾选即可禁用插件 AS插 ...

  9. weex-toolkit打包

    需要2.15.1及以上的npm支持 运行终端,查看版本 npm --version 如果版本低于2.15.1,需要更新 sudo npm install -g npm 安装weex-toolkit n ...

  10. jquery-动态设置ul li a链接目标

    概述: 先上我的布局图: ul中的每一个li里面包含着一个'a'超链接,可以猜想到,我想没点击ul->li里面每个a,链接到不同的页面,在这里,我用iframe完成. html主要代码为: &l ...