. : : Assembly Language : : .  |  首页  |  我提出的问题  |  我参与的问题  |  我的收藏  |  消息中心   |  游客  登录  | 
刷新 | 提问 | 未解决 | 已解决 | 精华区 | 搜索 |
  《汇编语言》论坛 ->综合研究
  管理员: assembly   [回复本贴] [收藏本贴] [管理本贴] [关闭窗口]
主题 : :  【砖头】综合研究试验3---使用内存空间(2)  [待解决] 回复[ 6次 ]   点击[ 1468次 ]  
mywiil
[帖 主]   [ 发表时间:2009-03-20 15:00 ]   [引用]   [回复]   [ top ] 
荣誉值:61
信誉值:4
注册日期:2008-10-14 16:29
上次由于时间问题,只学习了研究试验3的一部分内容,现在就把后面的三个题目的学习和思考过程继续贴出来交流。 

  实践学习题目(3),思考问题:C语言的全局变量、局部变量存放在哪里?每个函数开头的“push bp mov bp,sp”有什么作用。 
我们编译连接书上的代码,然后反汇编进行研究,反汇编代码及分析如下: 
;main函数部分 
1413:01FA 55            PUSH    BP  
;这里不用说,是保存BP的原始值(作用还得往下看) 
1413:01FB 8BEC          MOV     BP,SP  
;这里是将sp的值赋值给BP,那么SS:BP和SS:SP此时指向了同一位置。() 

1413:01FD 83EC06        SUB     SP,+06 
;将栈空间空出3个字的内存空间来,干什么?看看下面的局部变量赋值就知道了。 

1413:0200 C706A601A100  MOV     WORD PTR [01A6],00A1 
1413:0206 C706A801A200  MOV     WORD PTR [01A8],00A2 
1413:020C C706AA01A300  MOV     WORD PTR [01AA],00A3 
;以上三条指令是给全局变量a1,a2,a3赋值,根据汇编的源代码可以看出,这种寻址方式默认的段寄存器是ds,全局变量是存放在段地址为ds的内存空间中的。 

1413:0212 C746FAB100    MOV     WORD PTR [BP-06],00B1 
1413:0217 C746FCB200    MOV     WORD PTR [BP-04],00B2 
1413:021C C746FEB300    MOV     WORD PTR [BP-02],00B3 
;以上三条指令是给局部变量b1,b2,b3赋值,根据汇编代码,用BP寻址默认的段寄存器是ss,而ss是程序运行时候的栈的段地址,所以,可以推断,局部变量是存放在了程序的栈空间。(这里也就知道了sub sp,6的指令的作用了,空出来三个字单元内存,然后,将三个局部变量的值依次存放到这段栈空间内,存放的时候就用到了BP来定位,也就是,那三个子单元空间是在sp和bp之间的。可以细细体会一下bp的用处了吧,临时的定位。还有作用呢,别急,向下看) 

1413:0221 E80400        CALL    0228   
;为了准确的定位f函数的位置,我在源代码中加了一个f()来调用f函数。 

1413:0224 8BE5          MOV     SP,BP 
;程序运行完毕了,局部变量的生命周期也该结束了,BP的另一个作用又出现了:恢复SP的值。因为局部变量已经结束了自己的作用范围了,所以,就可以释放了,怎么释放呢?我们知道sp和bp之间是存储的局部变量,要想把这段栈空间空闲出来,那么就可以pop掉那几个局部变量,当然,还有一个更简单的办法,我们既然在存出局部变量之前,用BP记录了当时sp的值,那么,我们直接再来个mov sp,bp就OK。所以,这里这条的指令就是释放局部变量占用的栈空间。 

1413:0226 5D            POP     BP 
;恢复BP为函数进入前的原始值,这是汇编子程序的处理方式。 
1413:0227 C3            RET 

;f函数部分 
1413:0228 55            PUSH    BP 
1413:0229 8BEC          MOV     BP,SP 
1413:022B 83EC06        SUB     SP,+06 
;以上三条指令的作用以及跟下面指令的关联和main函数一样,就不在复述。 

1413:022E C706A601A10F  MOV     WORD PTR [01A6],0FA1 
1413:0234 C706A801A20F  MOV     WORD PTR [01A8],0FA2 
1413:023A C706AA01A30F  MOV     WORD PTR [01AA],0FA3 
;这里是给全局变量a1,a2,a3赋值,我们可以看出,跟main函数的一样,这些数据放到了段地址默认为ds的内存空间内。说明,全局变量都是存放到这段内存空间的。 

1413:0240 C746FAC100    MOV     WORD PTR [BP-06],00C1 
1413:0245 C746FCC200    MOV     WORD PTR [BP-04],00C2 
1413:024A C746FEC300    MOV     WORD PTR [BP-02],00C3 
;f函数中局部变量c1,c2,c3进行赋值,跟main函数的一样,这里的局部变量也是存放到了栈中间中,存放的方式方法也完全一样。 

1413:024F 8BE5          MOV     SP,BP 
1413:0251 5D            POP     BP 
1413:0252 C3            RET 

  上面对函数的汇编代码进行了分析,知道了局部变量是存放到了当前栈空间中,但是,全局变量是什么段呢?我们知道其段地址是ds,那么我们就看看ds指向的是哪里。在汇编的学习过程中,我们知道,一般ds指向的是我们数据段,但是到底是不是呢?前段时间大概的学习了一下汇编网训练营的C教程,预备知识里有一个自己实现的启动代码,通过对那个启动代码的了解,可以知道,ds在程序的初始化时,指向的就是该程序的数据段(至少现在的C程序应该是这样的),所以,我们可以说全局变量是存放在数据段的。 

  下面,我们通过实践程序(4)来了解一下,C语言中的返回值是怎么处理的。 
将书中的实践代码进行编译、连接,debug下查看其汇编代码如下: 
1413:01FA 55            PUSH    BP 
1413:01FB 8BEC          MOV     BP,SP 
1413:01FD 83EC02        SUB     SP,+02 
1413:0200 E80700        CALL    020A 
1413:0203 8946FE        MOV     [BP-02],AX;将ax中的值赋值到局部变量c对应的内存单元。 
1413:0206 8BE5          MOV     SP,BP 
1413:0208 5D            POP     BP 
1413:0209 C3            RET 
1413:020A 55            PUSH    BP 
1413:020B 8BEC          MOV     BP,SP 
1413:020D A1A601        MOV     AX,[01A6];将a变量的值放入ax 
1413:0210 0306A801      ADD     AX,[01A8];ax+b变量的值 
1413:0214 A3AA01        MOV     [01AA],AX;将ax放入ab变量的内存地址 
1413:0217 A1AA01        MOV     AX,[01AA];将ab变量的内存中的值放入ax 
1413:021A EB00          JMP     021C 
1413:021C 5D            POP     BP 
1413:021D C3            RET 

  从上面的分析可以看出,在f函数中,ax寄存器贯穿于函数内外,函数内没有对其进行先保存在恢复的操作,所以,ax被用作了一个能够将函数中的操作结果带到函数外部的存储器,也就是我们用来保存返回值的寄存器,而我们从main函数中的指令call 020A后面的那个给c赋值的指令 mov [bp-02], ax也很明显的可以看出,寄存器ax是c程序中用来存放函数返回值的地方。 

  最后的第(5)个实践程序,是让我们对之前的知识进行理解。 
首先我们理解一下第一行代码: 
#define Buffer ((char *)*(int far *)0x0200000) 
首先这是一条宏指令,程序中的Buffer,都能用后面的字符串((char *)*(int far *)0x0200000)进行物理替换。关键是((char *)*(int far *)0x0200000)怎么理解。我就说一下我的理解。 

从右向左,(int far *)0x0200000将一个16进制的数据强制转换成一个长指针;*(int far *)0x0200000相当于取这个长指针变量对应的地址处的值;然后((char *)*(int far *)0x0200000)),将长指针指向的内存空间中的值转换成一个char型指针。一句话概括一下:地址为0x02000000处的内存空间中存放着一个char型指针。再往明白了说就是,Buffer代表的就是一个char型指针。#define Buffer ((char *)*(int far *)0x0200000)相当于 char * Buffer。 

接下来的程序就不难理解了 

main(){ 
Buffer=(char *)malloc(20); 
;有了上面的结论,这里不难理解了,就是申请了20个字节的内存空间。malloc是申请空间的,然后返回空间的首地址。 

Buffer[10]=0; 
;这里就奇怪了,这是数组的使用形式,而我们没有定义数组。哈哈。牛就牛在这了。以前的学C的理解太死板,太肤浅了。其实,数组的实现是基于指针的,数组名就是代表了数组的首地址。“地址+[下标]” 这个寻址形式很类似我们的汇编中的基址变址寻址的形式。“地址+[下标]”代表的意义:操作“地址+下标值”这个地址处的数据。所以,这行代码的作用就是将申请到的内存中的第10号(游标号从0开始)的char型数据直接赋值为0。 

while(Buffer[10]!=8){ 
  ;我们知道,不论字符还是什么,在内存中都是以数据的行是存放,所以,这里可以用字符指针指向的内存中的数据值与循环控制次数做比较,进行判断。 

  Buffer[Buffer[10]] = 'a'+Buffer[10]; 
  ;因为Buffer[10]从0开始变化,所以,可以将其作为游标,然后以字符'a'的值与Buffer[10]当前的值来使字符从a变到h。 

  Buffer[10]++; 
  ;Buffer[10]处的内存中的值进行子增变化。 



  上面的这段程序非常的巧妙,而且,理解起来有点绕,我也不知道我说的清楚不清楚,总之,通过这个实践任务,我知道了指针可以跟数组一样,用下标来控制和变化,这个真是长见识了,那个C语言教程上也没见过这个啊。呵呵。还得细细品味品味。如果那个兄弟品的更有味道,指教小弟一二。再次做谢啦! 

================================= 
研究试验一个比一个有味道了。还得多体会一下啊。 
下个就是研究试验4了。 

准备中。。。
crazyman
[第1楼]   [ 回复时间:2009-03-20 15:02 ]   [引用]   [回复]   [ top ] 
荣誉值:152
信誉值:3
注册日期:2008-01-24 21:26
看来mywiil还真下了不少功夫。呵呵
linwangfeng
[第2楼]   [ 回复时间:2009-04-19 14:59 ]   [引用]   [回复]   [ top ] 
荣誉值:0
信誉值:0
注册日期:2009-04-15 18:50
楼主能不能再把(5)讲详细点啊,就 malloc 函数部分,我t了半天,最后是t得晕头转向啊,还是没明白是怎么弄的
zjxiaogaoren
[第3楼]   [ 回复时间:2009-04-27 20:23 ]   [引用]   [回复]   [ top ] 
荣誉值:0
信誉值:1
注册日期:2009-04-20 20:16
研究试验3.(5)个人分析,希望大家能指出批评
C:\minic>debug um6.exe
-r
AX=0000  BX=0000  CX=0F2A  DX=0000  SP=0080  BP=0000  SI=0000  DI=0000
DS=1406  ES=1406  SS=1492  CS=1416  IP=0000   NV UP EI PL NZ NA PO NC
1416:0000 BA7314        MOV     DX,1473
-u 1fa
1416:01FA 55            PUSH    BP
1416:01FB 8BEC          MOV     BP,SP
1416:01FD B81400        MOV     AX,0014
1416:0200 50            PUSH    AX
-------------------------------------------------------------
1416:0201 E8D902        CALL    04DD                ;这里是调用函数malloc有待跟踪,但是现在我们只要根据研究试验3(4),知道返回值

在AX中就可以了
1416:0204 59            POP     CX
1416:0205 BB0002        MOV     BX,0200
1416:0208 8EC3          MOV     ES,BX
1416:020A 33DB          XOR     BX,BX
1416:020C 26            ES:
1416:020D 8907          MOV     [BX],AX                ;根据研究试验3(4),我们知道了返回值是通过AX传递的,所以,这里的AX寄存器中存

放了是由函数malloc(CALL    04DD)返回的Buffer所指向首地址的数据段偏移地址,注意这说是一个偏移地址值,这里把Buffer所指向的首个

单元0200:0000的内容改成了一个偏移地址值

1416:020F BB0002        MOV     BX,0200
1416:0212 8EC3          MOV     ES,BX
1416:0214 33DB          XOR     BX,BX
1416:0216 26            ES:
1416:0217 8B1F          MOV     BX,[BX]                ;这里用了段前缀,Buffer所指向首地址0200:0000存放的数据段偏移地址放入BX寄存器


1416:0219 C6470A00      MOV     BYTE PTR [BX+0A],00        ;注意这里没有段前缀了,会用默认DS作为段前缀的,Buffer所指向首地址的偏

移地址+0A表示了Buffer[10],以下同理
-u
------------------------------------------------------------------
1416:021D EB3C          JMP     025B

1416:021F BB0002        MOV     BX,0200
1416:0222 8EC3          MOV     ES,BX
1416:0224 33DB          XOR     BX,BX
1416:0226 26            ES:
1416:0227 8B1F          MOV     BX,[BX]
1416:0229 8A470A        MOV     AL,[BX+0A]
1416:022C 0461          ADD     AL,61

1416:022E BB0002        MOV     BX,0200
1416:0231 8EC3          MOV     ES,BX
1416:0233 33DB          XOR     BX,BX
1416:0235 26            ES:
1416:0236 8B1F          MOV     BX,[BX]
1416:0238 50            PUSH    AX
1416:0239 53            PUSH    BX

1416:023A BB0002        MOV     BX,0200
-u
1416:023D 8EC3          MOV     ES,BX
1416:023F 33DB          XOR     BX,BX
1416:0241 26            ES:
1416:0242 8B1F          MOV     BX,[BX]
1416:0244 8A470A        MOV     AL,[BX+0A]
1416:0247 98            CBW                        ;字节转换为字,执行的操作:AL的内容符号扩展到AH,形成AX中的字。即如果(AL)的最高

有效位为0,则(AH)=0;如(AL)的最高有效位为1,则(AH)=0FFH。
1416:0248 5B            POP     BX
1416:0249 03D8          ADD     BX,AX
1416:024B 58            POP     AX
1416:024C 8807          MOV     [BX],AL
1416:024E BB0002        MOV     BX,0200
1416:0251 8EC3          MOV     ES,BX
1416:0253 33DB          XOR     BX,BX
1416:0255 26            ES:
1416:0256 8B1F          MOV     BX,[BX]
1416:0258 FE470A        INC     BYTE PTR [BX+0A]        ;对应Buffer[10]++
-------------------------------------------------------------------
1416:025B BB0002        MOV     BX,0200
-u
1416:025E 8EC3          MOV     ES,BX
1416:0260 33DB          XOR     BX,BX
1416:0262 26            ES:
1416:0263 8B1F          MOV     BX,[BX]
1416:0265 807F0A08      CMP     BYTE PTR [BX+0A],08        ;Buffer[10]!=8是一个判断比较
------------------------------------------------------------------
1416:0269 75B4          JNZ     021F
1416:026B 5D            POP     BP
1416:026C C3            RET
1416:026D C3            RET
1416:026E 55            PUSH    BP
1416:026F 8BEC          MOV     BP,SP
1416:0271 EB0A          JMP     027D
1416:0273 8B1E9E01      MOV     BX,[019E]
1416:0277 D1E3          SHL     BX,1
1416:0279 FF97A601      CALL    [BX+01A6]
1416:027D A19E01        MOV     AX,[019E]
yxlovemoney
[第4楼]   [ 回复时间:2009-05-17 21:33 ]   [引用]   [回复]   [ top ] 
荣誉值:0
信誉值:0
注册日期:2009-04-21 15:20
自己作了一些小分析,对没对 CALL 04DD 作分析,只对程序主体作了粗略分析
0B76:01FA 55            PUSH    BP
0B76:01FB 8BEC          MOV     BP,SP
0B76:01FD B81400        MOV     AX,0014 ;AX存放20即所需分配空间大小
0B76:0200 50            PUSH    AX
0B76:0201 E8D902        CALL    04DD    ;返回后AX存放分配的首地址
0B76:0204 59            POP     CX
0B76:0205 BB0020        MOV     BX,2000
0B76:0208 8EC3          MOV     ES,BX
0B76:020A 33DB          XOR     BX,BX   ;XOR异或,即使BX为0
0B76:020C 26            ES:
0B76:020D 8907          MOV     [BX],AX  ;2000:0得到malloc分配的地址空间
0B76:020F BB0020        MOV     BX,2000
0B76:0212 8EC3          MOV     ES,BX
0B76:0214 33DB          XOR     BX,BX
0B76:0216 26            ES:
0B76:0217 8B1F          MOV     BX,[BX]   ;es:bx 即2000:0中的内容送入bx
0B76:0219 C6470A00      MOV     BYTE PTR [BX+0A],00 ;ds:(bx+10)将malloc分配的首地址+A的地方赋0

0B76:021D EB3C          JMP     025B     ;IP改为025B,跳转到该地址,相当于循环开始
0B76:021F BB0020        MOV     BX,2000
0B76:0222 8EC3          MOV     ES,BX
0B76:0224 33DB          XOR     BX,BX
0B76:0226 26            ES:
0B76:0227 8B1F          MOV     BX,[BX]  ;malloc分配的地址送到BX
0B76:0229 8A470A        MOV     AL,[BX+0A] ;分配地址的偏移A的地址上的数据送入AL
0B76:022C 0461          ADD     AL,61      ;AL加上'a',根据[BX+0A]的值不同,AL得到不同字符
0B76:022E BB0020        MOV     BX,2000
0B76:0231 8EC3          MOV     ES,BX
0B76:0233 33DB          XOR     BX,BX
0B76:0235 26            ES:
0B76:0236 8B1F          MOV     BX,[BX]  ;malloc分配的地址送到BX
0B76:0238 50            PUSH    AX
0B76:0239 53            PUSH    BX      ;AX='a',BX入栈
0B76:023A BB0020        MOV     BX,2000

0B76:023D 8EC3          MOV     ES,BX
0B76:023F 33DB          XOR     BX,BX
0B76:0241 26            ES:
0B76:0242 8B1F          MOV     BX,[BX]  ;malloc分配的地址送到BX
0B76:0244 8A470A        MOV     AL,[BX+0A] ;分配地址的偏移A的地址上的数据送入AL
0B76:0247 98            CBW             ;字节转为字如果AL的最高有效位是0,则AH = 00;AL的最高有效位为1(应该指符号位),则AH = FFH。AL不变。
0B76:0248 5B            POP     BX     ;malloc分配的地址出栈到BX
0B76:0249 03D8          ADD     BX,AX  ;第一次AX=0,加到BX时分配的地址在第一位,第二次时BX中的地址就是第二位。
0B76:024B 58            POP     AX
0B76:024C 8807          MOV     [BX],AL ;第一次循环时AL='a'送入相应偏移地址
0B76:024E BB0020        MOV     BX,2000
0B76:0251 8EC3          MOV     ES,BX
0B76:0253 33DB          XOR     BX,BX
0B76:0255 26            ES:
0B76:0256 8B1F          MOV     BX,[BX] ;malloc分配的地址送到BX
0B76:0258 FE470A        INC     BYTE PTR [BX+0A]  ;分配地址+10的位置上,增加1
0B76:025B BB0020        MOV     BX,2000

0B76:025E 8EC3          MOV     ES,BX
0B76:0260 33DB          XOR     BX,BX
0B76:0262 26            ES:
0B76:0263 8B1F          MOV     BX,[BX]   ;BX得到malloc分配的地址
0B76:0265 807F0A08      CMP     BYTE PTR [BX+0A],08  ;该地址里的值与8比较
0B76:0269 75B4          JNZ     021F  ;jnz如不相等则跳转,相等于继续循环
0B76:026B 5D            POP     BP
0B76:026C C3            RET
0B76:026D C3            RET
jiych
[第5楼]   [ 回复时间:2012-02-04 12:04 ]   [引用]   [回复]   [ top ] 
荣誉值:15
信誉值:0
注册日期:2010-06-02 22:04
函数的返回值不一定存放在ax中,比如返回值为结构体。
za111456
[第6楼]   [ 回复时间:2012-03-29 19:52 ]   [引用]   [回复]   [ top ] 
荣誉值:0
信誉值:0
注册日期:2012-03-26 19:52
顶楼主,好人啊,,
需要登录后才能回帖 -->> 请单击此处登录
    Copyright © 2006-2024   ASMEDU.NET  All Rights Reserved