1、前提知识html
一、如何传递参数(主函数)编程
a、函数的参数是经过栈传递,并且是从右到左依次入栈ide
b、即便是char型变量,在传递参数时,也是占用两个字节,由于push操做是两个字节为单位的。 函数
c、showchar('a',2)这样的传入两个常数,也会在堆栈中开辟两个空间,也即对应两个实参变量。post
二、函数如何接收参数(子函数)测试
a、 函数接受形参是经过从栈中取的this
b、经过BP能够找到传入参数的值,BP+4是第一个参数,BP+6是第二个参数......取参数是从左到右取的 url
三、如何释放参数(主函数)spa
释放参数能够经过屡次pop来实现。有时是经过“add sp,+数值”来实现的。3d
2、研究一个简单的不定数量参数的函数
测试代码
void showchar(int,char,...); main() { showchar(8,2,'a','b','c','d','e','f','g','h'); } void showchar(int n,char color,...) { int i; char r; for (i = 0; i!=n; i++) { r = *(char *)(_BP+8+i+i); r = color; } }
一、main函数
main() { showchar(8,2,'a','b','c','d','e','f','g','h'); }
对应的反汇编代码
二、showchar函数
void showchar(int n,char color,...) { int i; char r; for (i = 0; i!=n; i++) { r = *(char *)(_BP+8+i+i); r = color; } }
对应的反汇编代码
三、分析
函数经过参数一来控制显示字符的循环次数,经过这种方式来接收多个参数。
3、printf函数肯定不定参数个数的方法
经过第一个参数所指向的字符串中%个数来肯定不定参数的个数。
4、实现一个printf函数
一、包含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',6553,'o',123); } /*************************************************************************** 函数功能:支持%c、%d功能的printf函数 输入参数:str以及不定参数 前提知识:不管char型,仍是int型,在传递参数时,都是入栈操做,并且都是以两个 字节入栈的。由于push指令只能以两个字节来操做。 实现思路:经过%来统计获取不定参数的个数,参数从堆栈中去获取 ****************************************************************************/ static void printf(const char * str,...) { char strnum = 0; /* 记录读出字符串str的位置 */ char paraaddr = 0; /* 记录读出不定参数的位置 */ while(str[strnum]){ /* 取出字符为NULL,结束 */ if(str[strnum] == '%'){ /* 取出字符为%,看下一个字符 */ strnum++; /* 读取字符串的位置后移 */ switch(str[strnum]){ /* 根据%后边字符的值,选择不一样的操做 */ case 'c': showchar(*(char*)(_BP+6+paraaddr));/* 为c,则将一个char型大小的参数取出显示 */ paraaddr += 2; /* 读取不定参数的位置后移 */ break; case 'd': showint(*(int*)(_BP+6+paraaddr)); /* 为d,则将一个int型大小的参数取出显示 */ paraaddr += 2; /* 读取不定参数的位置后移 */ 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[5]; /* 定义栈空间 */ char p; /* 栈顶 */ for(p = 0; p < 5; p++){ bufstk[p] = num % 10; /* 从低位到高位依次入栈 */ num /= 10; } while((p > 0)&&(bufstk[--p] == 0)); /* 舍去高位为0的数字,但不舍弃num=0的个位0 */ do{ showchar(bufstk[p]+'0'); /* 显示有效数字 */ } while(p--); /* 直到栈空 */ }
二、包含在光标位置显示一个字符和显示换行的汇编程序
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+10] ;字符 mov bl,07h ;颜色为黑底白字 mov bh,0 ;第0页 mov cx,1 ;重复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 ;页号为0 mov ah,3 ;取当前光标位置 int 10h ;返回参数。DH=行号,DL=列号 inc dh ;行号加1 mov dl,0 ;列号为0 mov ah,2 ;置光标位置 int 10h ;入口参数。DH=行号,DL=列号 pop dx pop ax pop bx ret _SHOWENTER ENDP _TEXT ENDS END
三、编译方法