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

一、 学习过程

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

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

我个人的理解:这里是将指向开辟的20个字节的内存空间的指针赋给buffer,即200:0000~200:0001里存储的是开辟的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 bp;mov bp,sp。有何含义?”

答:全局变量是存放在指定的内存空间中,而局部变量是存放在栈中,到函数生命周期结束时释放。因为要用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语言的角度而不是汇编的角度,这样并不能给我们解决问题带来帮助。有时候一个东西从不同的角度来看会有不同的样子,也许从一个角度看来不能理解的问题,从另一个角度来看可能会比较容易理解。但是如果需要大量时间才能搞清楚,却对当前学习没有很大帮助的问题,是可以暂时放一放,去完成更加重要的学习进度的。

时间: 2024-07-29 01:28:30

栈以及内存操作在汇编中的实现的相关文章

数据结构中的堆和栈 与 内存分配中的堆区和栈区 分析

比較全面的总结了诸多版本号,知识无国界.感谢各位的辛勤劳作. 在计算机领域,堆栈是一个不容忽视的概念,我们编写的C/C++语言程序基本上都要用到.但对于非常多的初学着来说,堆栈是一个非常模糊的概念. (1) 数据结构的栈和堆 首先在数据结构上要知道堆栈,虽然我们这么称呼它,但实际上堆栈是两种数据结构:堆和栈. 堆和栈都是一种数据项按序排列的数据结构. 栈就像装数据的桶或箱子 我们先从大家比較熟悉的栈说起吧.它是一种具有后进先出性质的数据结构,也就是说后存放的先取.先存放的后取.这就如同我们要取出

【转】《深入理解计算机系统》C程序中常见的内存操作有关的典型编程错误

原文地址:http://blog.csdn.net/slvher/article/details/9150597 对C/C++程序员来说,内存管理是个不小的挑战,绝对值得慎之又慎,否则让由上万行代码构成的模块跑起来后才出现内存崩溃,是很让人痛苦的.因为崩溃的位置在时间和空间上,通常是在距真正的错误源一段距离之后才表现出来.前几天线上模块因堆内存写越界1个字节引起各种诡异崩溃,定位问题过程中的折腾仍历历在目,今天读到<深入理解计算机系统>第9章-虚拟存储器,发现书中总结了C程序中常见的内存操作有

【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )

[嵌入式开发]ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 ) 一. 内存 简介 1. 两大内存分类 ( 1 ) DRAM 简介 ( 定期刷新 | 速度慢 | 成本低 ) DRAM 简介 : 1.硬件描述 : DRAM 基本由一个个小电容基本原件组成, 电容的两端保留电荷; 2.优缺点描述 : ① 优点 : 成本很低, 很便宜; ② 缺点 : 需要 定期刷新数据, 速度较慢; a.

用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型

import java.util.Stack; /**  * 用两个栈来实现一个队列,完成队列的Push和Pop操作. 队列中的元素为int类型.  * @author user  *  *思路:队列是先入先出,栈是先入后出,可以将数据压入第一个栈后,在弹出来压入第二个栈,弹得时候直接从第二个栈弹出,弹出后又将  *第二个栈中的所有数据弹出重新压入的一个栈  */ public class Solution {     Stack<Integer> stack1 = new Stack<

Delphi中复制带有String的记录结构时不能使用Move之类的内存操作函数

请看下面的代码: program TestRecord; {$APPTYPE CONSOLE} uses  SysUtils,  Math; type  TRecordA = record    Name: string;  end; procedure RunTestRecord;var  R1, R2: TRecordA;begin  R1.Name := StringOfChar('A', RandomRange(64, 256) * 1024);  Move(R1, R2, SizeOf

为什么X86汇编中的mov指令不支持内存到内存的寻址?

在X86汇编中,MOV [0012H], [0016H]这种指令是不允许的,至少得有一个操作数是寄存器.当然,这种问题在用高级语言的时候看不到,感觉好像基本上都是从内存到内存啊,为毛到了汇编就不行了???这个问题在stack overflow有个解释不错: The answer involves a fuller understanding of RAM. Simply stated, RAM can only be in two states, read mode or write mode.

64位汇编第二讲——64位汇编中局部变量使用及抬栈方法29171230

一.纯写64位汇编时局部变量处理和参数寄存器保存位置 纯写64位汇编和用VS2013写64位C代码生成的汇编会有一些格式上的区别,VS2013写64位C代码生成的汇编中是没用到栈基址寄存器rbp的,但是纯写汇编时只要申明了参数和使用了@LOCAL定义的局部变量,就会用到rbp.且看如下例子:1)用C写64位程序空函数生成的汇编代码, ;C代码 void FunTest2() { } ;汇编代码 000000013F753290 40 57 push rdi 000000013F753292 5F

【汇编杂项】关于_高级语言中 数组越界与汇编中 栈溢出的_联系的思考

数组越界 数组越界,是刚开始学习编程时,就不断被别人提醒的一个点,“相当可怕”.获取不合理数值,造成程序异常or操作计算机重要内存,造成威胁...原因是什么呢?数组在汇编中以栈机制实现,其中内存分配的机制与数组越界的风险有很大关系.今天做个小实验,来简单探讨下这个. 代码 先展示问题代码 1 #include<stdio.h> 2 int main(){ 3 int a[3]={0,1,2}; 4 for(int i=0;i<=3;i++){ 5 a[i]=0; 6 printf(&qu

第41课 内存操作经典问题分析一

1. 野指针 (1)指针变量中的值是非法的内存地址,进而形成野指针 (2)野指针不是NULL指针,是指向不可用内存地址的指针 (3)NULL指针并无危害,很好判断,也很好调试 (4)C语言中无法判断一个指针所保存的地址是否合法 2. 野指针的由来 (1)局部指针变量没有被初始化 (2)指针所指向的变量在指针之前被销毁 (3)使用己经释放过的指针 (4)进行了错误的指针运算 (5)进行了错误的强制类型转换 [实例分析]野指针初探 #include <stdio.h> #include <m