原来我以为在C语言中指针已经是非常麻烦了,没想到栈帧给我甜蜜一击,但最后一路学习下来也不是多么麻烦的事。

首先我们得明确为什么有函数,其作用是:在面向过程语言的重要组成成分,它将具有相同功能的语句组合到一块,便于我们使用,提高程序可读性,减少代码量。

以main函数为例,在使用过程中首先调用__tmainCRTStartup函数,然后又调用mainCRTStartup函数,而每一次的函数调用就是一个过程——函数的调用过程;在这个过程中我们要为函数开辟栈空间,用于临时变量的保存、现场的保护(函数的返回值和参数、调用前寄存器的状态,调用前栈帧的顶部和底部的地址),这块栈空间我们称之为函数

栈帧。

栈帧——汇编语言详解

由C语言的内存分布空间可得,在不断地函数过程中,栈向下增长,那么具体的调用过程是怎么样的嘞?举一段代码表示说明(本次代码在vc++6.0环境下运行):

栈帧——汇编语言详解

很简单的一段代码,将其对应汇编代码一步步开始进行分析,在此之前我们了解一下关于汇编的基本知识:

寄存器:esp栈顶指针、ebp是存取堆栈底指针——存取某时刻的栈顶指针,以方便对栈的操作;

pc(程序计数器)当前正在执行的指令的下一条指令的地址;

eax是"累加器"(accumulator), 它是很多加法乘法指令的缺省寄存器、ebx 是"基地址"(base)寄存器, 在内存寻址时存放基地址、ecx是计数器(counter), 是重复(REP)前缀指令和LOOP指令的内定计数器、edx则总是被用来放整数除法产生的余数。

指令:push入栈、pop出栈、ret当前保存的寄存器地址出栈,其值赋给IP寄存器。

一、main函数栈帧的创立:

栈帧——汇编语言详解

一个函数的栈帧用ebp和esp这两个寄存器划定范围。寄存器esp始终指向栈的顶部,也即指向当前函数栈帧的顶部。而ebp寄存器指向函数栈帧的一个固定位置,所以ebp寄存器又被称为帧指针。这两个寄存器的详细操作方式为:函数调用时上一级调用者的帧底被压入当前ebp内容所指的地址,也就是当前帧的帧底位置保存了上一级调用者的ebp指针值(帧底),而每个ebp的前一个单元存放的就是当前函数的返回地址(它是由调用者在call指令中入的栈)。这样就可以根据当前ebp的值回溯出整个任务的调用栈(调用过程)。

二、add函数的调用,首先形参实例化(具体过程不表,但具体要用到call命令跳转至add函数的代码执行处);

call:将当前的汇编指令地址保存,以便于恢复,jmp(修改eip)跳转至目标函数的地址处。

栈帧——汇编语言详解

整个函数调用到这里就结束了,以后将继续完善~

相关文章: