一、 学习过程
要在计算机中用到一段存储空间,必须要知道两个信息:(1)存储空间在哪?(2)存储空间有多大。对于寄存器来说,只要给出寄存器的名字就可以了,因为每个寄存器在计算机中都是唯一的,而且寄存器大小是规定的。对于内存空间来说,就要给出地址和数据类型,数据类型就代表一个存储空间的大小。
以前学习C语言的时候,并没有仔细研究过,原来C语言中还可以以这种语法直接向内存单元中写入数据:*(char *)0x2000=’a’;向偏移地址为2000h的内存空间写入char型数据a。*(char far *)0x20000000=’a’;向段地址为2000h,偏移地址为0000的内存空间写入char型数据a。比较上面两种写法可以发现,前一种只给出了偏移地址,那么段地址在哪里呢,是该语句所在的函数的段地址吗?
我们在编写C语言程序时要赋值给内存单元,通常的做法是声明变量再对变量进行赋值,即把值赋给内存空间,类似于在汇编中把偏移地址赋给寄存器,再把数据赋给寄存器表示的内存空间,而这里的直接赋值,类似于汇编中mov ds:[si],200h的语句。
编写程序um1.c编译连接:
在debug中用U命令查看相关代码:
这里我发现,编译器在编译时会把段地址给es,偏移地址给bx,结果存放在
es:[bx]中。
执行第一条语句后,内存单元内容为:
执行第二条语句后,内存单元内容为:
执行第三条语句后,内存单元内容为:
执行第四条语句后,内存单元内容为:
执行第五条语句后,内存单元内容为:
执行第六条语句后,内存单元内容为:
可见在c语言中直接使用对内存空间进行操作和先把内存单元地址给寄存器再对寄存器所代表的内存空间进行操作是一样的,而且C语言中可以将内存空间的地址直接与寄存器相加减(如*(char far *)(0x20001000+_BX)=*(char *)_AX;),而且在语句*(char *)(_BX+_BX)=’a’;执行后寄存器bx的值是已经改变了的,即后一条语句*(char far *)(0x20001000+_BX)=*(char *)_AX;是把0x2000里的值赋给0x20003000而不是0x20002000.
要在屏幕的中间显示一个绿色的字符a,则要将a赋给b800:[12*160+40*2],将2赋给b800:[12*160+40*2+1],但是书上要求要用一条C语句实现。一个字符a在C语言中为char类型,占一个字节,int类型占两个字节,那么我们可以把2和字符a转化成一个int型数据再赋给b800:[12*160+40*2]。如图:
执行结果是正确的。
再看下一个程序:
编译发现有许多警告,这里不用去管。
编译连接后用debug的u命令查看,发现a1的地址为01a6,a2的为01a8,a3的为01aa,b1、b2、b3的没有直接给出,而是由bp-6、bp-4、bp-2表示,而在开头程序将sp的值给了bp,sp自减了6,因为sp存放的是栈的栈顶偏移地址,可知实际上程序是将b1、b2、b3依次放入了栈中。但是在程序结尾处有mov bp,sp,把栈顶指针又还原了,这时b1、b2、b3的内存空间已不在栈中,随时可以被其他程序的数据覆盖。可知,全局变量是存放在指定的内存空间中,而局部变量是存放在栈中,到函数生命周期结束时释放。因为要用bp记录sp原来的位置,以便函数结束时将sp还原,所以要对bp进行保护,所以会有push bp;mov bp,sp.
再看下一个程序: