研究试验5 接收不定数量的参数

 

2012-12-21

a.c

 1 void showchar(char a,int b);
 2 main()
 3 {
 4 showchar('a',2);
 5 }
 6 void showchar(char a,int b)
 7 {
 8 *(char far*)(0xb8000000+160*10+80)=a;
 9 *(char far*)(0xb8000000+160*10+81)=b;
10 }

使用TC2.0进行编译的,借用上个研究试验的c0s.obj,下面的汇编代码:

184D:0000 B84518 MOV AX,1845
184D:0003 8ED8 MOV DS,AX
184D:0005 8ED0 MOV SS,AX
184D:0007 BC8000 MOV SP,0080   
184D:000A E80500 CALL 0012 ;调用main函数  push ip(000D) jmp 0012 bp=0000

   
 sp->007d 0d
007f 00
0080  

 

 

 

 

 

 

 

 

;main function start
184D:0012 55 PUSH BP;暂存bp,实际上是保存调用main的函数时栈的初始状态

sp->007C 00
007D 00
007E 0d
007F 00
0080  

 

 

 

 

 

 

 

 

184D:0013 8BEC MOV BP,SP ;bp=007c 保存main函数的栈初始状态
184D:0015 B80200 MOV AX,0002
184D:0018 50 PUSH AX

 

 

sp->007A 02
007B 00
007c 00
007d 00
007e 0d
007f 00
0080  

 

 

 

 

 

 

 

 


184D:0019 B061 MOV AL,61
184D:001B 50 PUSH AX

 

 

sp->0078 61
0079 00
007A 02
007B 00
007C 00
007D 00
007E 0d
007F 00
0080  

 参数 'a'

 参数2

 

 

 

 

 

 

 

 


184D:001C E80400 CALL 0023;push ip(0017) jmp 0023

 

 

sp->0076 1f
0077 00
0078 61
0079 00
007A 02
007B 00
007C 00
007D 00
007E 0d
007F 00
0080  

 

 

 

 

 

 

 

 

 

 

 

 

 

 

;showchar function start
-u cs:0023
184D:0023 55 PUSH BP 

 

 

sp->0074 7C
0075 00
0076 1F
0077 00
0078 61
0079 00
007A 02
007B 00
007C 00
007D 00
007E 0d
007F 00
0080  

 

 

 

 

 

 

 

 

 

 

 

 

 


184D:0024 8BEC MOV BP,SP;栈的初始状态 bp=sp=0074
184D:0026 8A4604 MOV AL,[BP+04] ;ds=ss,ss:[0078] (AL)=61H
184D:0029 BB00B8 MOV BX,B800
184D:002C 8EC3 MOV ES,BX
184D:002E BB9006 MOV BX,0690
184D:0031 26 ES:
184D:0032 8807 MOV [BX],AL ; (B800:0690)=61H
184D:0034 8A4606 MOV AL,[BP+06]
184D:0037 BB00B8 MOV BX,B800
184D:003A 8EC3 MOV ES,BX
184D:003C BB9106 MOV BX,0691
184D:003F 26 ES:
184D:0040 8807 MOV [BX],AL
184D:0042 5D POP BP  ;释放栈中的内存

 

 

sp->0076 1F
0077 00
0078 61
0079 00
007A 02
007B 00
007C 00
007D 00
007E 0d
007F 00
0080  
   

 

 

 

 

 

 

 

 

 

 

 


184D:0043 C3 RET ;pop ip(ip=001F)
;showchar function end

 

 

sp->0078 61
0079 00
007A 02
007B 00
007C 00
007D 00
007E 0d
007F 00
0080  

 

 

 

 

 

 

 

 

 

184D:001F 59 POP CX ;释放内存
184D:0020 59 POP CX ;释放内存
184D:0021 5D POP BP
184D:0022 C3 RET
;main function end


184D:000D B8004C MOV AX,4C00
184D:0010 CD21 INT 21

总结:1)参数使用栈进行传递,参数入栈的顺序是从右到左,最左边的参数是在栈顶

         2)char虽然是占用一个字节,但是仍然采用是用16位存储入栈,存在低位

         3)bp寄存器存放进入函数之前的栈顶地址,通过入栈的次数可以定位参数在栈中的位置。

         4)参数占用内存采用出栈的方式释放,不是采用直接移动栈顶地址,区别于局部变量

         5)注意在利用栈顶定位参数的时候不要落下call和ret指令,call会调用push指令,ret会调用pop指令

         6)*(char far*)(0xb8000000+160*10+80)=a;表示地址为B800:0690H的内存单元是放入a变量的内容。

----------------------------------------------------------------------

利用BP寄存器定位参数的修改程序如下:

 1 void showchar(char a,int b);
 2 main()
 3 {
 4 showchar('a',2);
 5 }
 6 void showchar(char a,int b)
 7 {
 8 *(char far*)(0xb8000000+160*10+80)=*(char *)(_BP+4);
 9 *(char far*)(0xb8000000+160*10+81)=*(char *)(_BP+6);
10 }

 -----------------------------------------------------------------

数量不定参数的接收

B.C

 1 void showchar(int,int,...);
 2 main()
 3 {
 4 showchar(8,2,'a','b','c','d','e','f','g','h');
 5 }
 6 
 7 void showchar(int n,int color,...)
 8 {
 9 int a;
10 for(a=0;a!=n;a++)
11 {
12 *(char far *)(0xb8000000+160*10+80+a+a)=*(int *)(_BP+8+a+a);
13 *(char far *)(0xb8000000+160*10+81+a+a)=color;
14 }
15 }

反汇编

 1 -u cs:1fa
 2 ;main 函数 start
 3 1847:01FA 55            PUSH    BP
 4 1847:01FB 8BEC          MOV     BP,SP
 5 
 6 ;10个参数 用栈传递 占用20个内存单元
 7 1847:01FD B86800        MOV     AX,0068
 8 1847:0200 50            PUSH    AX
 9 1847:0201 B86700        MOV     AX,0067
10 1847:0204 50            PUSH    AX
11 1847:0205 B86600        MOV     AX,0066
12 1847:0208 50            PUSH    AX
13 1847:0209 B86500        MOV     AX,0065
14 1847:020C 50            PUSH    AX
15 1847:020D B86400        MOV     AX,0064
16 1847:0210 50            PUSH    AX
17 1847:0211 B86300        MOV     AX,0063
18 1847:0214 50            PUSH    AX
19 1847:0215 B86200        MOV     AX,0062
20 1847:0218 50            PUSH    AX
21 1847:0219 B86100        MOV     AX,0061
22 1847:021C 50            PUSH    AX
23 1847:021D B80200        MOV     AX,0002
24 1847:0220 50            PUSH    AX
25 1847:0221 B80800        MOV     AX,0008
26 1847:0224 50            PUSH    AX
27 
28 1847:0225 E80500        CALL    022D
29 
30 ;调用showchar函数
31 1847:022D 55            PUSH    BP
32 1847:022E 8BEC          MOV     BP,SP 
33 1847:0230 56            PUSH    SI
34 1847:0231 33F6          XOR     SI,SI ;si清零,比mov si,0的效率高,si相当于计数器。
35 1847:0233 EB49          JMP     027E
36 ;*(int *)(_BP+8+a+a)
37 1847:0235 8BDD          MOV     BX,BP ;_BP+8+a+a
38 1847:0237 03DE          ADD     BX,SI ; 
39 1847:0239 03DE          ADD     BX,SI ;
40 1847:023B 83C308        ADD     BX,+08 ;移动到存放61H内存单元的
41 1847:023E 8A07          MOV     AL,[BX] ;将'a'赋值给al
42 1847:0240 50            PUSH    AX      ;字母入栈
43 
44 ;*(char far *)(0xb8000000+160*10+80+a+a)
45 1847:0241 8BC6          MOV     AX,SI 
46 1847:0243 99            CWD       ;扩展ax的符号位,即将ax的15位扩展到dx中。如果ax的15位是0,表面ax内存储负数,dx变为0000H;若ax的15位是1,dx变为1111H
47 1847:0244 52            PUSH    DX
48 1847:0245 50            PUSH    AX
49 1847:0246 8BC6          MOV     AX,SI
50 1847:0248 99            CWD      
51 1847:0249 5B            POP     BX ;(BX)=(AX)=(SI)
52 1847:024A 59            POP     CX ;(CX)=(DX)=AX 15位
53 1847:024B 03D8          ADD     BX,AX ;add si,2 移动2位
54 1847:024D 13CA          ADC     CX,DX   ;这么多行实现a+a,有何特殊之处?
55 
56 
57 1847:024F 81C39006      ADD     BX,0690 ;160*10+80
58 1847:0253 81D100B8      ADC     CX,B800
59 1847:0257 8EC1          MOV     ES,CX
60 1847:0259 58            POP     AX
61 1847:025A 26            ES:
62 1847:025B 8807          MOV     [BX],AL ;
63 
64 *(char far *)(0xb8000000+160*10+81+a+a)=color
65 1847:025D 8A4606        MOV     AL,[BP+06]
66 1847:0260 50            PUSH    AX
67 1847:0261 8BC6          MOV     AX,SI
68 1847:0263 99            CWD
69 1847:0264 52            PUSH    DX
70 1847:0265 50            PUSH    AX
71 1847:0266 8BC6          MOV     AX,SI
72 1847:0268 99            CWD
73 1847:0269 5B            POP     BX
74 1847:026A 59            POP     CX
75 1847:026B 03D8          ADD     BX,AX
76 1847:026D 13CA          ADC     CX,DX
77 
78 1847:026F 81C39106      ADD     BX,0691
79 1847:0273 81D100B8      ADC     CX,B800
80 1847:0277 8EC1          MOV     ES,CX
81 1847:0279 58            POP     AX
82 1847:027A 26            ES:
83 1847:027B 8807          MOV     [BX],AL
84 1847:027D 46            INC     SI
85 
86 1847:027E 3B7604        CMP     SI,[BP+04] ;a与n比较
87 1847:0281 75B2          JNZ     0235  ;不相等 不等于0就跳到0235
88 
89 1847:0283 5E            POP     SI
90 1847:0284 5D            POP     BP
91 1847:0285 C3            RET
92 
93 
94 1847:0228 83C414        ADD     SP,+14
95 1847:022B 5D            POP     BP
96 1847:022C C3            RET
内容 指令   CS:IP
FFC8 00 push ax    
FFC9 00      
FFCA 00 push dx    
FFCB 00      
FFCC 61 push ax    
FFCD 00      
FFCE B0 push si   1847:0230
FFCF 05      
FFD0 E8 push BP BP=FFD0 1847:022D
FFD1 FF      
FFD2 28 push ip 0228   call 022D
FFD3 02      
FFD4 08 push 0008 字符个数参数 1847:0224
FFD5 00      
FFD6 02 push 0002 颜色参数 1847:0220
FFD7 00      
FFD8 61 push 0061   1847:021C
FFD9 00      
FFDA 62 push 0062   1847:0218
FFDB 00      
FFDC 63 push 0063   1847:0214
FFDD 00      
FFDE 64 push 0064   1847:0210
FFDF 00      
FFE0 65 push 0065   1847:020C
FFE1 00      
FFE2 66 push 0066   1847:0208
FFE3 00      
FFE4 67 push 0067   1847:0204
FFE5 00      
FFE6 68 push 0068   1847:0200
FFE7 00      
FFE8 F4 push bp   1847:01FA
FFE9 FF      
FFEA 83 call 1FA   1847:011A
FFEB 02      

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

------------------------------

对比上面两个showchar函数,可以发现showchar都是通过栈来传递参数的。

push bp

mov bp,sp  ;001

mov ax,参数1

push ax

mov ax,参数2

push ax

mov ax,参数3

push ax

.........

call showchar 函数

push bp

mov bp,sp ;002 ;

001和002的两个bp数值差额是参数个数*2+2(call showchar->push ip)+2(push bp)=2*N+2+2(N是参数的个数)

 

上面三张图分别是a.exe的反汇编指令、main函数将实参传递、调用showchar函数。

对比就可以发现实参传递和调用showchar之后bp的数值差是8。

可以再通过其他的函数来验证。

当然上述规律针对的是没有返回值的函数,如果有返回值了,那可能就不适应了,不过按照这个思路进行探讨。

--------------------------------------------

printf函数

 pt1.c  pt2.c  pt3.c

1 main()
2 {
3 printf("%c%c",'a','b');
4 }
1 main()
2 {
3 printf("%c%d",'a',12);
4 }
1 main()
2 {
3 printf("%c is the first letter!",'a');
4 }

以上三段代码是三个关于printf应用的例子。

根据上面得到的结论,参数都是利用栈进行传递的。三个程序中分别传递了2、2、1个参数。共同之处就是都使用了mov ax,0194进行参数传递,那么0194是什么呢?

 三个程序中0194都是对应着一段字符串,而且字符串的结尾处是0。

所以可以断定,printf函数将"..."中的内容由编译器存放到一个地址,根据格式符号%及其后面所跟的字符进行判定,如果是c或者是d,就将栈的内容取出进行替换,再将未替换部分直接拷到到屏幕。

----------------------------------------

实现%c %d功能的函数print

 1 void print(int,char *str,...);
 2 main()
 3 {
 4 int row=8;    //显示行号
 5 print(row,"%c%c",'a','b');  //注意"%c%c",放入内存的部分是%c%c0,详见C基础
 6 row++;
 7 print(row,"%c%d",'a',123);
 8 row++;
 9 print(row,"%c is the first letter",'a');
10 
11 }
12 
13 void print(int row,char *str,...)
14 {
15   char ch;   //判断字符
16   int ch_count=0; //字符数目统计
17   
18   
19   int stackstep=0;  //栈移动参数
20   int showstep=0;   //显示移动参数
21   
22   int q;  //数值除10的商
23   int pushcount;  //模拟入栈的数目统计
24   
25   ch=str[ch_count++];
26   
27   while(ch)
28   {
29   //先判断字符
30   if ('%' == ch)
31   {
32   ch=str[ch_count++];  //下一个字符判断
33   switch(ch)
34   {
35   //%c形式,直接将字符参数放入显存
36   case 'c':
37     *(char far *)(0xb8000000+160*row+20+(showstep++))=*(int *)(_BP+8+stackstep);
38     *(char far *)(0xb8000000+160*row+20+(showstep++))=2;//绿色显示
39     break;
40  case 'd':
41     pushcount=0;  
42     q = *(int *)(_BP+8+stackstep);  //将栈中的整数参数赋值给q
43     stackstep+=2;
44     
45     //特殊整数的处理
46     if(!q)
47     {
48       *(char far *)(0xb8000000+160*row+20+(showstep++))='0';
49       *(char far *)(0xb8000000+160*row+20+(showstep++))=2;
50     }
51 
52     while(q)
53     {
54     //模拟入栈
55     _SP-=2;
56     *(int *)(_SP)=(q%10)+48;
57     
58     pushcount++;
59     q/=10;
60     }
61 
62     while(pushcount)
63     {
64      //模拟出栈
65     *(char far *)(0xb8000000+160*row+20+(showstep++))=*(int *)(_SP);
66     *(char far *)(0xb8000000+160*row+20+(showstep++))=2;
67     _SP+=2;
68     
69     pushcount--;
70     }
71     break;
72     }
73     }
74 
75 
76     else
77     {
78     *(char far *)(0xb8000000+160*row+20+(showstep++))=ch;
79     *(char far *)(0xb8000000+160*row+20+(showstep++))=2;
80     }
81     ch=str[ch_count++];
82     }
83 
84 
85 }

 

posted @ 2012-12-21 12:03  Black Man's Note  阅读(232)  评论(0编辑  收藏  举报