热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

王爽-汇编语言-综合研究三-使用内存空间

(一)研究概述数据不仅可以存储在寄存器中,还可以存储在内存中。这次我们就研究在C语言中,怎样直接在内存中存储数据。以及这样做的一些延伸问题。另外,在附录研究中,我们还探究了C语言中循环和分

 

(一) 研究概述

数据不仅可以存储在寄存器中,还可以存储在内存中。这次我们就研究在C语言中,怎样直接在内存中存储数据。以及这样做的一些延伸问题。另外,在附录研究中,我们还探究了C语言中循环和分支结构的实现。

(二) 研究过程

1) 直接在C语言中使用内存空间

此处援引书中的话:

对于存储空间来说,要使用他们一般都需要给出两个信息:一是指明存储空间所在、是哪个的信息;二是指明存储空间有多大的类型信息。

对于寄存器来说,就需要给出寄存器的名称,寄存器的名称就也包含了他们的类型信息。

对于内存空间来说,就需要给出地址(准确的说,是内存空间首地址)和空间存储数据的类型。

我们知道,在C语言里,用指针型数据来表示内存空间的地址和空间存储数据的类型。比如要向偏移地址为2000h、存储一个字节的内存空间写入一个字符‘a’,我们用如下的方法:

*(char *)0x2000 = ’a’;

第一个‘*’表示要访问的是一个内存空间;

“0x2000”是一个数值(0x表示十六进制),“(char *)”里面的‘*’指明了这个

数值表示一个内存空间地址,“char”指明了这个地址是存储char型数据的内存空间地址。

当然也可以用给出段地址和偏移地址的方法访问内存空间,比如我们要向地址为2000:0、存储一个字节的内存空间写入字符‘a’,如下:

*(char far *)0x20000000=’a’;

“far”指明内存空间的地址是段地址和偏移地址,“0x20000000”中的“0x2000”给出了段地址,“0000”给出了偏移地址。

由此,我们知道了C语言直接访问内存空间的基本方法。

2) 编写一个直接访问内存的C语言程序

我们编写一个程序um1.c如下:

clip_image001

编译链接完成,debug加载反编译如下:

clip_image003

clip_image005

3) 编写一个程序,用一条C语言实现在屏幕中间显示一个绿色的字符“a”

我们编写源程序如下:

clip_image006

编译链接运行,效果如下:

clip_image008

4) 关于全局变量和局部变量

我们分析下面程序中所有函数的汇编代码:

clip_image009

编译链接之后debug加载,反汇编如下。

clip_image011clip_image013clip_image015

在这里,我们非常直观的看出。程序中的全局变量,被放在了程序的数据段中,而局部变量则放在了其栈段中。这也正好说明了在每个函数开头,都有push bp;mov bp sp的作用。我们把局部变量放在了栈段中,那我们使用时必然要顺利的找到这个变量。而使用sp的话,假如程序中有入栈出栈的操作,栈顶指针变了,我们就找不我们存放的变量了。所以使用BP这个寄存器,在程序的开始把Sp的值给BP,然后,将SP的值增加,把局部变量的位置留出来。这时,我们可以方便的用BP找到局部变量,而也可以方便的使用栈的功能。函数返回时,BP的值又赋值给SP,函数内的局部变量就消失了。这也就是为什么C语言中局部变量的值只在函数内有效的原因。

5) C语言函数返回值存放在哪儿?

我们研究了变量的存放位置,那么,C语言的函数返回值存放在什么地方呢?我们写如下的程序。

clip_image016

编译链接后反汇编分析:

clip_image018

clip_image020

我们看到020A处,为函数f()的汇编指令。前五句,根据前面的内容,我们很容易理解。但是不同的是,多了一句mov ax,[01AA]。那么这条语句的作用是什么?会不会跟我们函数返回值有关?是不是函数返回值是用AX传出的呢?

我们调试运行程序:g 021d,结果如下

clip_image022

可见,函数的返回值的确是由寄存器ax传出的。

6) 综合分析

我们编写一个程序如下:

clip_image023

首先我们要了解#define和malloc这两条语句。

#define的作用,是宏定义,简单的讲在这里就是将((char *)*(int far *)0x02000000)这条语句用Buffer代替,以后再用到这条语句的时候,直接用Buffer就可以了。

((char *)*(int far *)0x02000000)表面是一个char型的指针,它指向的是0200:0000这个地址。

malloc(X)的作用,是申请X长度的内存。

Buffer=(char *)malloc(20)是申请20个字节的内存单元,将首地址赋值给Buffer指向的内存单元。

理解了这个,我们可以理解这个C程序的内容

我们编译链接,反汇编如下:

clip_image025

clip_image027

clip_image029

我们单步执行中,发现

clip_image031

AX中存放的应该是由malloc申请返回来的。应该是我们申请内存的首地址。

我们继续执行

clip_image033

我们发现这个地址果然是malloc返回来的申请内存的偏移地址。

(三) 附录研究

注:附录研究与内存单元的使用无关,主要是研究C语言中的三种结构。顺序结构我们可以很容易的理解,循环结构和选择分支结构研究如下:

1. C语言中循环结构for语句的实现

C语言中循环语句,for语句是如何实现的呢?我们编写如下程序

clip_image034

我们尽量的减小问题的复杂程度,使其突出我们所要研究的重点。这里循环内并没有其他的语句,仅仅是一条空语句。就是为了便于我们把问题的焦点放在for循环的实现上。

我们编译连接,加载后反汇编查看,如下:

clip_image035

我们可以看到,for的循环是使用了si寄存器,首先将si入栈保存,然后清空si,通过不断自加si的值,然后将si与我们原定的数值进行比较。

那么,如果在for中加入语句的话,那么这条语句会加在哪里呢?

我们分析,jmp 0203这条语句,这应该是转到循环内的。那么,应该是先比较,后执行循环内语句呢?还是先执行循环内语句,后比较呢?

我们接着分析,si的值是从0开始的,最后与5作比较,小于则转跳。如果先比较后执行的话,执行的次数就成了4次,这是不正确的。所以,应该是先执行,后比较。

我们验证:

clip_image037

我们在看jmp,这次直接jmp到了020B,果真是每次循环先执行,后比较。但是同时,我们发现,一进入for循环的时候,他是转跳到了比较语句。这又是为什么呢?

假如我们写这样的C语句:for(i=0;i<1;i++)会出现什么结果?如果不先比较一下的话,循环内语句必然要执行一次。这就是一进入for循环,必须先比较一下的原因。

2. C语言中分支结构if的实现

我们编写一个程序如下:

clip_image038

编译连接后加载查看如下:

clip_image040

我们可以直观的看到,依然是使用cmp,jnz等比较语句来进行程序的选择分支的实现。

所以,参照这个研究,返回头看我们的汇编程序。我们可以更加系统的使用汇编语言,来实现程序的各种选择,循环结构。

(四) 个人感悟

内存单元,寄存器,变量之间的关系到底如何?这次的研究给出了一部分答案。局部变量的内涵,全局变量的内涵,返回值的实现。拿研究C语言后的汇编知识在反观汇编程序的实现,应该会有更深入的理解,更结构化的编程概念。


推荐阅读
  • 那你就是学的c语言,跟我学c语言
    本文目录一览:1、如何学习C语言?2、新手如何 ... [详细]
  • c语言基础编写,c语言 基础
    本文目录一览:1、C语言如何编写?2、如何编写 ... [详细]
  • 本文介绍了基于c语言的mcs51单片机定时器计数器的应用教程,包括定时器的设置和计数方法,以及中断函数的使用。同时介绍了定时器应用的举例,包括定时器中断函数的编写和频率值的计算方法。主函数中设置了T0模式和T1计数的初值,并开启了T0和T1的中断,最后启动了CPU中断。 ... [详细]
  • Java String与StringBuffer的区别及其应用场景
    本文主要介绍了Java中String和StringBuffer的区别,String是不可变的,而StringBuffer是可变的。StringBuffer在进行字符串处理时不生成新的对象,内存使用上要优于String类。因此,在需要频繁对字符串进行修改的情况下,使用StringBuffer更加适合。同时,文章还介绍了String和StringBuffer的应用场景。 ... [详细]
  • 本文探讨了C语言中指针的应用与价值,指针在C语言中具有灵活性和可变性,通过指针可以操作系统内存和控制外部I/O端口。文章介绍了指针变量和指针的指向变量的含义和用法,以及判断变量数据类型和指向变量或成员变量的类型的方法。还讨论了指针访问数组元素和下标法数组元素的等价关系,以及指针作为函数参数可以改变主调函数变量的值的特点。此外,文章还提到了指针在动态存储分配、链表创建和相关操作中的应用,以及类成员指针与外部变量的区分方法。通过本文的阐述,读者可以更好地理解和应用C语言中的指针。 ... [详细]
  • 全面介绍Windows内存管理机制及C++内存分配实例(四):内存映射文件
    本文旨在全面介绍Windows内存管理机制及C++内存分配实例中的内存映射文件。通过对内存映射文件的使用场合和与虚拟内存的区别进行解析,帮助读者更好地理解操作系统的内存管理机制。同时,本文还提供了相关章节的链接,方便读者深入学习Windows内存管理及C++内存分配实例的其他内容。 ... [详细]
  • linux进阶50——无锁CAS
    1.概念比较并交换(compareandswap,CAS),是原⼦操作的⼀种,可⽤于在多线程编程中实现不被打断的数据交换操作࿰ ... [详细]
  • 本文主要介绍了gym102222KVertex Covers(高维前缀和,meet in the middle)相关的知识,包括题意、思路和解题代码。题目给定一张n点m边的图,点带点权,定义点覆盖的权值为点权之积,要求所有点覆盖的权值之和膜qn小于等于36。文章详细介绍了解题思路,通过将图分成两个点数接近的点集L和R,并分别枚举子集S和T,判断S和T能否覆盖所有内部的边。文章还提到了使用位运算加速判断覆盖和推导T'的方法。最后给出了解题的代码。 ... [详细]
  • 该楼层疑似违规已被系统折叠隐藏此楼查看此楼*madebyebhrz*#include#include#include#include#include#include#include ... [详细]
  • 本文介绍了C函数ispunct()的用法及示例代码。ispunct()函数用于检查传递的字符是否是标点符号,如果是标点符号则返回非零值,否则返回零。示例代码演示了如何使用ispunct()函数来判断字符是否为标点符号。 ... [详细]
  • 李逍遥寻找仙药的迷阵之旅
    本文讲述了少年李逍遥为了救治婶婶的病情,前往仙灵岛寻找仙药的故事。他需要穿越一个由M×N个方格组成的迷阵,有些方格内有怪物,有些方格是安全的。李逍遥需要避开有怪物的方格,并经过最少的方格,找到仙药。在寻找的过程中,他还会遇到神秘人物。本文提供了一个迷阵样例及李逍遥找到仙药的路线。 ... [详细]
  • STL迭代器的种类及其功能介绍
    本文介绍了标准模板库(STL)定义的五种迭代器的种类和功能。通过图表展示了这几种迭代器之间的关系,并详细描述了各个迭代器的功能和使用方法。其中,输入迭代器用于从容器中读取元素,输出迭代器用于向容器中写入元素,正向迭代器是输入迭代器和输出迭代器的组合。本文的目的是帮助读者更好地理解STL迭代器的使用方法和特点。 ... [详细]
  • python中安装并使用redis相关的知识
    本文介绍了在python中安装并使用redis的相关知识,包括redis的数据缓存系统和支持的数据类型,以及在pycharm中安装redis模块和常用的字符串操作。 ... [详细]
  • 流数据流和IO流的使用及应用
    本文介绍了流数据流和IO流的基本概念和用法,包括输入流、输出流、字节流、字符流、缓冲区等。同时还介绍了异常处理和常用的流类,如FileReader、FileWriter、FileInputStream、FileOutputStream、OutputStreamWriter、InputStreamReader、BufferedReader、BufferedWriter等。此外,还介绍了系统流和标准流的使用。 ... [详细]
  • 流程控制与Java基本类似,Kotlin提供了if和when两种分支语句,when可以替代 ... [详细]
author-avatar
mobiledu2502859073
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有