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
三、編譯方法