前言
寻址方式这块可以多看看,内存那些的知识其实在第一节的文章中也讲的差不多了
课堂
内存与寄存器的区别
-
寄存器位于 CPU 内部,执行速度快,但比较贵
-
内存速度相对较慢,但成本较低,所以可以做的很大
-
寄存器和内存没有本质区别,都是用于存储数据的容器,都是定宽的
计算机常用的计量单位
-
BYTE 字节 = 8(bit)
-
WORD 字 = 16(bit)
-
DWORD 双字 = 32(bit)
生活中常用的计量单位
-
1KB = 1024 BYTE
-
1MB = 1024 KB
-
1GB = 1024 MB
误区
内存的数量特别庞大,无法每个内存单元都起一个名字,所以用编号来代替,我们称计算机 CPU 是 32 位或者 64 位,主要指的就是内存编号的宽度,而不是寄存器的宽度,有很多书上说之所以叫 32 位计算机是因为寄存器的宽度是 32 位,是不准确的,因为还有很多寄存器是大于 32 位的
计算机内存的每一个字节会有一个编号(即内存编号的单位是字节),如下图:
32 位计算机的编号最大是 32 位,也就是 32 个 1 换成 16 进制为 FFFFFFFF,也就是说,32 位计算机内存寻址的最大范围是 FFFFFFFF+1 ,内存的单位是字节,那内存中能存储的信息最多为:FFFFFFFF+1 字节 即 4G,这也是为什么我们在一个 XP 的系统上面如果物理内存超过 4G 是没有意义的原因
那么只要是 32 位的计算机,那么最多识别的内存为 4G,对吗?
不对的,因为有些操作系统可以打补丁拓展寻址范围,但是一般情况下就是 4GB,特例请看下图
内存地址格式
-
一般在汇编中,内存编号也用 16 进制数表示,立即数也有用十六进制的数写。那么我们怎么才能让计算机区分开呢?只要在内存编号的两侧加
[]
,如[0x12345678]
就表示一个内存编号,即地址 -
每个内存单位的宽度为 8bit,
[编号]
称为地址,地址的作用是:当我们想从内存中读取数据或者想向内存中写入数据,首先应该找到要读、写的位置 -
内存编号是连续的。比如从上到下是[0x00000000]一直连续到[0xffffffff]。也可以从下到上,是自己决定的
操作内存的汇编指令
-
格式:
mov dword ptr ds:[0x0012ff34],0x12345678
-
解释:
-
dword
表示要使用的内存大小为 4 个字节,即要读/写多少;还可以是 byte 和 word 等,嗯嗯数据宽度的概念在无时无刻的体现 -
ptr
就表示代表后面是一个指针,即后面要跟内存编号(固定写法); -
DS
是段寄存器,32 位汇编中段寄存器和内存的属性有关系:如果后面地址直接用数表示,则用DS
;如果后面写 ESP 或者 EBP,就写SS
;如果 EDI 的话就用ES
。所以 32 位汇编中段寄存器就是后面内存属性的一个标识 -
[0x0012ff34]表示后面的立即数存入内存的(起始)地址,如果前面是 dword,表示从这个内存地址对应的一字节内存块开始存储(或读取),一共要存储(或读取)4 字节内存,即 4 个内存块(因为一个地址对应一字节大小内存,那么一个编号可能不够存),即从[0x0012ff34]这个单元块开始存(或读),再往高地址存,依次往下,直到占用了 4 字节的内存,即从[0x0012ff34]到[0x0012ff37]都是用来存这个指令后面的数值的!
-
0x12345678
就是存入内存的数值,称为立即数。如果立即数的宽度比前面指定的内存宽度大,则从低位开始取,取到指定的宽度,高位多余的部分就被丢弃,比如要写入 dword,但是立即数为 0x123456789,则最终写入的内存的值为 0x23456789,一共 4 字节
-
-
注意:地址编号不要随便写,因为内存是有保护的,并不是所有的内存都可以直接读写。建议写表示栈的地址范围内的地址,即 esp 到 ebp 中存的地址之间的地址,一般建议要测试的话 esp 中存的地址值肯定可以使用
-
我们此时可能有疑问:前面不是说一个地址编号对应一个字节的内存块,但是上面 0x0019ff74 地址对应的内存中存的数为 0x769dfa29,这个值大于一个字节,已经 4 个字节大小了,怎么能用一个地址编号表示?其实是因为,这里是用 0x0019ff74 到 0x0019ff77 来存储后面的 4 字节的值,但是这四块内存块用一个地址编号 0x0019ff74(起始地址)表示,相当于一个用 0x0019ff74 表示 0x0019ff74 到 0x0019ff77 这四个内存块,这样看起来更整洁一些!
-
我们分开来看:0x0019ff74 表示了四个内存块,四个内存块中的值一起显示为 0x769dfa29。分开来看:0x0019ff74 地址中的值为 0x29;0x0019ff75 地址中的值为 0xfa;0x0019ff76 地址中的值为 0x9d;0x0019ff77 地址中的值为 0x76!(因为地址从低到高对应存的数也是从低位到高位,一个字节为一个整体,即小端存储)。所以可以发现 OD 其实已经帮我们把栈中的数据反过来了,用我们平时读写的顺序显示。这里要和 VC6++中的内存分开,VC6++没有帮我们把数据反过来
寻址方式
我们知道数据存储在内存中,CPU 需要使用存在内存中的数据,使用一个数据就要在内存中找到这个数据,用什么找,就是用内存编号,即地址,来找到数据在内存中所在的位置,把它取出来使用,或者再放回到指定位置
上述就是一个寻址过程:寻址方式一共有五种,计算机只认识这五种方式来寻址
- [立即数]
- [reg] reg 代表寄存器 可以是 8 个通用寄存器中的任意一个
- [reg+立即数]
- [reg+reg*{1,2,4,8}]
通过[reg+reg*1 或者 2 或者 4 或者 8]来寻址,只能是 1,2,4,8,后面学硬编码就知道了
- [reg+reg*{1,2,4,8}+立即数]
作业
无,其实就和上节文章后面写的作业一样,就是练习
但是这里在写的时候遇到了一个困惑的问题,就是很 VC 有点不一样的地方,详情看下图
看到这个寄存器窗口就这么想:它的内存编号是从上到下的,也就是从小到大的,但是每一排的 4 个字节,是从右到左依次对应着从上到下,其他就没什么了