浅谈C函数栈帧

一、地址空间与物理内存 

(1)地址空间与物理内存是两个完全不同的概念,真正的代码及数据都存在物理内存中。 

物理储存器是指实际存在的具体储存器芯片,CPU在操纵物理储存器的时候都把他们当做内存来对待,把他们看成由若干个储存单元组成的逻辑储存器,这个逻辑储存器就是我们所说的地址空间。 

地址空间大小与逻辑储存器大小不一定相等。 

(2)进程的地址空间分布 

进程的地址空间包括:栈区(heap)、共享区、堆区(stack)、未初始化静态全局区(uninit)、已初始化静态全局区(init)、静态只读区、代码段(code)。如图:


二、栈帧的建立 

函数的调用过程:每一次函数调用都是一个过程,这个过程我们通常称之为函数的调用过程

函数栈帧:函数调用过程要为函数开辟栈空间,用于本次函数的调用中临时变量的保存,现场保护。这块栈空间,我们称之为函数栈帧

每一个函数都有自己的栈帧空间,并且独占自己的栈帧空间, 

在研究函数的栈帧之前,我们先得知道了解一下这么几个寄存器:

EAX:是“累加器”,它是很多加法,乘法指令的缺省寄存器

EBX:是“基地址寄存器”,在内存寻址时存放基地址

ECX: 是“计数器”,是重复REP前缀指令和LOOP指令的内定计数器

EDX:总是用来存放整数除法产生的余数

ESI/EDI:分别叫做“源/目标索引寄存器”,因为在很多字符串操作指令中,DS:ESI 指向源串,而ES:EDI 指向目标串

EBP:存放了指向函数栈帧栈底的地址

在未受改变之前始终指向栈帧的开始,也就是栈底,所以ebp的用途是在堆栈中寻址用的

ESP:存放了指向函数栈帧栈顶的地址

EIP:程序计数器,用来存放正在执行指令的下一条指令的地址

先看一段简单的代码:

#include <stdio.h>



int Add(int x, int y) {

int z = 0;

z = x + y;

return z;

}

 

int main() {

int a = 10;

int b = 20;

int ret = Add(a, b);

printf("ret = %d\n", ret);

 

return 0;

}

我们发现main函数是被__tmainCRTStartup函数调用,而__tmainCRTStartup又被mainCRTStartup调用。

调用main函数,我们为main函数分配栈帧空间,那么栈帧维护如下:


(1)main函数的调用就得为main函数创建栈帧:

(2)Add函数的调用



(3)执行call指令的时候按F11,来到这里


(4)再按F11进入Add函数的执行代码处


(5)函数的返回部分









猜你喜欢

转载自blog.csdn.net/qq_39026129/article/details/80273730