|
主题 : : 【砖头】综合研究试验3---使用内存空间(2) [待解决] |
回复[ 6次 ]
点击[ 1468次 ] | |
|
|
|
|
[帖 主]
[ 发表时间: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了。
准备中。。。 | | |
|
|
|
|
[第1楼]
[ 回复时间:2009-03-20 15:02 ]
[引用]
[回复]
[ top ] | |
荣誉值:152
信誉值:3
注册日期:2008-01-24 21:26 |
|
|
|
|
|
[第2楼]
[ 回复时间:2009-04-19 14:59 ]
[引用]
[回复]
[ top ] | |
荣誉值:0
信誉值:0
注册日期:2009-04-15 18:50 |
楼主能不能再把(5)讲详细点啊,就 malloc 函数部分,我t了半天,最后是t得晕头转向啊,还是没明白是怎么弄的 | | |
|
|
|
|
[第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] | | |
|
|
|
|
[第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 | | |
|
|
|
|
[第5楼]
[ 回复时间:2012-02-04 12:04 ]
[引用]
[回复]
[ top ] | |
荣誉值:15
信誉值:0
注册日期:2010-06-02 22:04 |
函数的返回值不一定存放在ax中,比如返回值为结构体。 | | |
|
|
|
|
[第6楼]
[ 回复时间:2012-03-29 19:52 ]
[引用]
[回复]
[ top ] | |
荣誉值:0
信誉值:0
注册日期:2012-03-26 19:52 |
|
|