函数调用过程理解

一、栈

        栈是常见的数据结构,在发生程序调用的情况下,操作系统会为被调用的子程序开辟一块栈空间。栈除了先入后出(FILO)外还有以下特性:

每一个进程在用户态对应一个调用栈结构(call stack)
程序中每一个未完成运行的函数对应一个栈帧(stack frame),栈帧中保存函数局部变量、传递给被调函数的参数等信息
栈底对应高地址,栈顶对应低地址,栈由内存高地址向低地址生长

二、寄存器

        寄存器位于CPU内部,用于存放程序执行中用到的数据和指令,CPU从寄存器中取数据比从内存中取快。对于有特定用途的几个寄存器,简要介绍如下:

ax(accumulator): 可用于存放函数返回值
EBP(extended base pointer): 存放着一个指针,该指针永远指向系统栈最上面一个栈帧的栈底
ESP(extended stack poinger): 存放着一个指针,该指针永远指向系统栈最上面一个栈帧的栈顶
ip(instruction pointer): 指向当前执行指令的下一条指令

                        

        如上图所示,以 i386 为例,栈底地址为0x0A,存储在 EBP 寄存器中, 栈顶地址为0x00,保存在 ESP 寄存器中。如果执行 PUSH 指令,向栈上压入数据会导致 ESP 的值减小,执行 POP 指令,从栈里弹出数据则会使得 ESP 增大。

三、函数调用过程

函数调用步骤为:

参数入栈:将函数的参数从右向左依次压入系统栈中
返回地址入栈:将当前代码区调用指令的下一条指令地址压入栈中,供函数返回时继续执行
代码区跳转:处理器从当前代码区跳转到被调用函数的入口处
栈帧调整:a、保存当前栈帧状态值,已备后面恢复本栈帧时使用(EBP入栈)
         b、将当前栈帧切换到新栈帧。(将ESP值装入EBP,更新栈帧底部)
         c、给新栈帧分配空间。(把ESP减去所需空间的大小,抬高栈顶)

假设代码运行的顺序为mian函数调用fun_A,然后fun_A调用fun_B,为了方便理解栈帧变化状态,在下图中的栈底位于最下方。

               

函数执行过程为:

在main函数调用func_A的时候,首先在自己的栈帧中压入函数返回地址,然后为func_A创建新栈帧并压入系统栈在func_A
调用func_B的时候,同样先在自己的栈帧中压入函数返回地址,然后为func_B创建新栈帧并压入系统栈
在func_B返回时,func_B的栈帧被弹出系统栈,func_A栈帧中的返回地址被“露”在栈顶,此时处理器按照这个返回地址重新跳到func_A代码区中执行
在func_A返回时,func_A的栈帧被弹出系统栈,main函数栈帧中的返回地址被“露”在栈顶,此时处理器按照这个返回地址跳到main函数代码区中执行



参考资料:

https://www.zhihu.com/question/22444939/answer/22200552
https://zhuanlan.zhihu.com/p/24291978?refer=hinus
https://zhuanlan.zhihu.com/p/25149493
https://blog.csdn.net/boer521314/article/details/41411593


猜你喜欢

转载自blog.csdn.net/u012474535/article/details/80230386
今日推荐