你好,游客 登录 注册 搜索
背景:
阅读新闻

栈以及内存操作在汇编中的实现

[日期:2016-12-28] 来源:Linux社区  作者:stormpeach [字体: ]

一、 学习过程

要在计算机中用到一段存储空间,必须要知道两个信息:(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.

再看下一个程序:

 

 

 

观察程序可以发现,函数f()的返回值放在ax中。那么如果程序有多个返回值应该怎么存放呢?还有如果从main函数向f()传参,也是用ax存放吗?如果返回的参数不是int型,而是char或者long int型,应该用什么存放呢?通过网络查阅资料,发现如果是1字节的数据,用al存放,如果是4字节的数据,高16位用dx传递,低16位用ax传递。

再看下一个程序:

 

#define是宏定义,在程序中用Buffer代替((char *)*(int far *)0x02000000)

malloc20)是开辟20个字节的内存空间。

我个人的理解:这里是将指向开辟的20个字节的内存空间的指针赋给buffer,即2000000~2000001里存储的是开辟的20个字节的内存空间的首地址。Buffer[10]处是一个计数器,将a~h分别存到buffer[0]buffer[7]的内存空间中。

二、 解决的问题

(1) c语言中直接使用对内存空间进行操作和先把内存单元地址给寄存器再对寄存器所代表的内存空间进行操作是一样的,而且C语言中可以将内存空间的地址直接与寄存器相加减

(2) 为什么*(char far *)(0x20001000+_BX)=*(char *)_AX;是把0x2000里的值赋给0x20003000而不是0x20002000.?

答:在语句*(char *)(_BX+_BX)=’a’;执行后寄存器bx的值是已经改变了的,为0x2000.

(3) C语言将全局变量存放在哪里?将局部变量存放在哪里?每个函数开头的“push bpmov bpsp。有何含义?”

答:全局变量是存放在指定的内存空间中,而局部变量是存放在栈中,到函数生命周期结束时释放。因为要用bp记录sp原来的位置,以便函数结束时将sp还原,所以要对bp进行保护,所以会有

push bp;mov bp,sp.

(4) C语言将函数的返回值放在哪里?

答:如果是1字节的数据,用al存放,如果是2字节的数据,用al存放,如果是4字节的数据,高16位用dx传递,低16位用ax传递。

三、 未解决的问题

1*char *0x2000=’a’ 只给出了偏移地址,那么段地址在哪里呢,是该语句所在的函数的段地址吗?

2函数有多个返回值在汇编应该怎么存放呢?

(3)如果从main函数向f()传参,也是用ax存放吗?

(4)最后一个程序是怎么在汇编中实现的?

四、 学习感想

在研究试验中,我们是以汇编的角度去看C语言的问题,弄明白C语言的一些东西在汇编中是怎么实现的。但是这样就涉及到一些C语言方面的知识,我的做法是查资料和请教别人,但是别人的回答都是从C语言的角度而不是汇编的角度,这样并不能给我们解决问题带来帮助。有时候一个东西从不同的角度来看会有不同的样子,也许从一个角度看来不能理解的问题,从另一个角度来看可能会比较容易理解。但是如果需要大量时间才能搞清楚,却对当前学习没有很大帮助的问题,是可以暂时放一放,去完成更加重要的学习进度的。

本文永久更新链接地址http://www.linuxidc.com/Linux/2016-12/138894.htm

linux
相关资讯         汇编  内存操作 
本文评论   查看全部评论 (0)
表情: 表情 姓名: 字数

       

评论声明
  • 尊重网上道德,遵守中华人民共和国的各项有关法律法规
  • 承担一切因您的行为而直接或间接导致的民事或刑事法律责任
  • 本站管理人员有权保留或删除其管辖留言中的任意内容
  • 本站有权在网站内转载或引用您的评论
  • 参与本评论即表明您已经阅读并接受上述条款