本文和后文中讲的都是32位机,也就是i386的系统
计算机语言(机器语言)可以通过0和1生成机器指令,将指令输入到cpu 来执行,通过机器指令计算机可以实现各种各样的功能,而机器语言又分为操作码和操作数,假如:
int a = 1;
生成的机器码为(下面是随便输的):
101010(操作码) 10101010101011011111(操作数)
其中就包含了操作码和操作数,在内存当中每一个基础执行单元为8位,1byte(字节)等于8bit(位),而int a 是四字节,所以在内存当中开辟空间的时候会在内存中开辟4byte的空间,而在开辟内存的过程中,必须指定内存的地址,此时就引出的高地址和低地址的概念。
高地址和低地址
可以简单的认为,可以把主存看成一本空白的作业本,内存地址就相当于主存的页码,你现在要在笔记本上记录一些内容,他的页码排序是
第一页 : 0x0000001
第二页 : 0x0000002
...
最后一页: 0x0000092
1 如果你选择`从前向后记录`(用完第一页,用第二页,类推)这就是先使用低地址,后使用高地址.
0x0000001 -> 0x0000002-> ... -> 0x0000092
业内有这样表述:动态分配内存时堆空间向高地址增长,说的就是这种情况.
这个向高地址增长就是先使用低地址,后使用高地址的意思.
2 如果你选择`从后往前记录`(先用笔记本的最后一页,用完后使用倒数第二页,类推) 这就是先使用高地址,后使用低地址
0x0000092 -> ... ->0x0000002 -> 0x0000001
业内表述:`0xbfac 5000-0xbfad a000`是栈空间,其中高地址的部分保存着进程的环境变量和命令行参数,低地址的部分保存函数栈帧,**栈空间是向低地址增长的**.
这个向低地址增长就是先使用高地址,后使用低地址的意思.
栈底(栈基址)和栈顶
通过上面的高地址和低地址我们明白,在内存中是以类似于数组的方式存储数据,而0x0000092之类的数值代表了数据在内存中的位置,也就是我们的内存地址(类似于下标),我们如果要找到指定的数据,就必须在CPU当中存储栈底和栈顶的地址,这时就牵扯到了CPU的寄存器
SP寄存器(stack pointer) 和BP寄存器(base pointer)
SP寄存器存储了栈顶的地址,而BP寄存器则存储了栈底的地址,初始状态下内存中不存在数据,栈顶和栈底指向了同一个位置(就像数组第一个下标是0,最后一个下标也是零,第一个下标相当于栈顶,最后一个下标相当于栈底),当我们需要在内存中开辟一个空间的时候,通过sp寄存器的位置减去要开辟的内存空间,如果写成汇编代码则可以如下表示:
例如:上面的int a 为四字节,所以要在内存中开辟四字节的空间,所以SP 寄存器的减去4个字节,则相当于在内存中开辟了4字节的内存空间
sub 4,sp
sub在英文中代表了减,我们就可以看出在内存中减去了四个字节,而在内存中存放数据的时候是存在指定的顺序的,这里就引出了一个关键知识点:字节序。
字节序
字节序分为大端序和小端序,我们的内存也存在高低之分,越往右是低,越往左是高,低位置叫做小端序,高位置叫做大端序,而大端序和小端序则是代表了内存的两种排序规则。
大端序(英:big-endian)或称大尾序。,数据以8bit为单位:
地址增长方向 →
0x0A | 0x0B | 0x0C | 0x0D |
小端序(英:little-endian)或称小尾序。数据以8bit为单位:
0x0D | 0x0C | 0x0B | 0x0A |
Inter 64 位和AI 32 位的处理器使用的都是小端序
Inter 寄存器的变迁
在1978年Inter 处理器为16位,SP 寄存器和BP寄存器的名称并没有变化,在之后处理变为了32位后,SP寄存器和BP寄存器则被称为ESP 寄存器和EBP寄存器,也就是扩展的SP和BP寄存器,而64位将E改为了R叫做RBP和RSP寄存器。
高八位和低八位
以EBP寄存器为例:一个EBP由两个BP组成,而我们的数据越往右越低,所以EBP寄存器中往左的16位为高16位,往右的16位为低16为。AX 寄存器为通用寄存器,为16位寄存器,变成32位,被称为EAX,变成64位被称为RAX,一个16位可以表示成两个八位,往左的8位为高八位,往右的8位为高八位,将AX中的X替换为H 和 L,AH代表高八位,AL代表低八位,X 在这里代表了未知数。
再来看汇编
因为本文研究的是32位机,所以寄存器的名称为ESP和EBP,那此时int a = 1;我们在内存中开辟一块空间,使用汇编语言可以这样写:
sub 4,esp
mov 1,-4(ebp)
mov 1,-4(ebp)表示将数值1 移动到-4的ebp位置。