函数的创建和销毁
本文探讨了编程中函数的创建与销毁过程,重点分析了函数调用时的栈帧管理机制。在创建阶段,系统通过移动栈指针分配空间存储局部变量和参数,并详细介绍了寄存器(如ebp、esp等)在函数调用中的关键作用。销毁阶段则通过弹出栈帧回收资源,防止内存泄漏。文章通过C语言示例展示了从main函数到Add函数的完整调用过程,包括参数传递、栈帧切换和返回值处理,同时解释了汇编指令如push、mov、lea等在内存操作
目录
在编程中,函数的创建和销毁涉及函数在内存中的生命周期管理。这包括函数被定义、调用时内存的分配(创建),以及函数执行完毕后内存的回收(销毁)。理解这一过程有助于优化程序性能和避免资源泄漏。下面我将逐步解释这一概念,并提供示例。
函数的创建和销毁
注在vs编译器中越简单我们越容易观察理解 我们这里使用vs2013编译器
函数的创建和初始化
- 栈帧的分配:每个函数调用会创建一个栈帧(Stack Frame),用于存储局部变量、参数和返回地址。栈帧的大小取决于函数内部定义的变量和参数数量。
- 内存分配公式:设函数参数和局部变量总大小为 $s$ 字节,栈指针 $sp$ 的移动可表示为: $$sp \leftarrow sp - s$$ 这表示栈指针向下移动以分配空间。
#include<stdio.h>
#include<windows.h>
int Add(int x, int y)
{
int z = 0;
z = x + y;
return z;
}
int main()
{
int a = 10;
int b = 20;
int c = 0;
c = Add(a, b);
printf("%d", c);
system("pause");
return 0;
}
寄存器
函数有寄存器储存
- eax:通常用来执行加法,函数调用的返回值一般也放在这里面
- ebx:数据存取
- ecx:通常用作计数器,比如for循环
- edx:暂不清楚
- esp:栈顶指针,指向栈的顶部
- ebp:栈底指针,指向栈的底部,通常用ebp+偏移量的形式来定位函数存放在栈中的局部变量
- edi:字符串操作时,用于存放数据源的地址
- esi:字符串操作时,用于存放目的地址的,和edi两个经常搭配一起使用,执行字符串的复制等操作
专用词介绍
esp为栈顶指针
ebp为栈底指针
push 压栈 往栈顶放值
mov 交换
lea 加载
sub 减法
call 调用下一条指令地址
pop 弹出指令
add 加法
打开反编代码 内存 监视调控 工具

一开始 创建的空间 esp在栈顶 ebp在栈底

push ebp
往esp栈顶加一个esp 栈中地址是由高地址向低地址使用

mov ebp,esp
把ebp移动到esp的位置 两个地址相同

sub esp,0E4h
把esp减0E4h 向上

push ebx
push esi
push edi
把三个寄存器往esp-0E4h压栈从而esp地址指向不断减低

lea edi,[ebp+FFFFFF1Ch]
把 [ebp+FFFFFF1Ch] 加载到ebp里
mov ecx,39h
mov eax,0CCCCCCCCh
rep stos dword ptr es:[edi]
从edi把main空间初始化cccccccc

从 edi 开始初始化ccccccc
![]()

数值的寄存

mov dword ptr [ebp-8],0Ah
把0Ah放在epb-8的位置

而一个a等于10 一个int4个字节
epb-8 往上两个字节

dword ptr [ebp-14h],14h
dword ptr [ebp-20h],0
同理



mov eax,dword ptr [ebp-14h]
push eax
mov ecx,dword ptr [ebp-8]
push ecx
把[ebp-14h][ebp-8] 寄存到 eax ecx里
[ebp-14h] =b [ebp-8]=a
call 008910E6
add esp,8
mov dword ptr [ebp-20h],eax
call 调用下一条指令 按两次 F11进入自定义函数
此时 esp ebp 为Add函数维护空间


push ebp
压栈 此时ebp压的是main函数的ebp 为的自定义函数返回时找到ebp的位置
push ebp
mov ebp,esp
sub esp,0CCh
push ebx
push esi
push edi
lea edi,[ebp+FFFFFF34h]
mov ecx,33h
mov eax,0CCCCCCCCh
rep stos dword ptr es:[edi]
同理 上述和main函数相同 都是为了函数初始化 而我们在用vs中烫烫就是这么出现的
mov dword ptr [ebp-8],0
把 0也就是z存在ebp-8里
mov eax,dword ptr [ebp+8]
把ebp+8的值=b=20放eax里
add eax,dword ptr [ebp+0Ch]
[ebp+0Ch]=a=20放eax里相加=30
mov eax,dword ptr [ebp-8]
把[ebp-8]寄存到eax里 局部函数结束生命周期销毁时数值也不会被销毁
pop edi
pop esi
pop ebx
pop弹出 把edi esi ebx 弹出 进行回值操作
mov esp,ebp
把esp的位置移动到esp来 返回有一步ebp存的地址 ebp main

pop ebp
弹出 esp ebp z 的值此时 自定义函数 销毁生命周期结束
ret
返回call存下的的地址也就下一步指令 完成了函数的返回值


mov dword ptr [ebp-20h],eax
把eax=20放在[ebp-20h],也就是此时值就返回了 可以打印出来



函数的销毁
销毁确保内存不被浪费,防止栈溢出或内存泄漏。在支持垃圾回收的语言中,销毁过程可能延迟,但栈帧通常是立即回收的。
在编程中,函数的创建和销毁涉及函数在内存中的生命周期管理。这包括函数被定义、调用时内存的分配(创建),以及函数执行完毕后内存的回收(销毁)。理解这一过程有助于优化程序性能和避免资源泄漏
更多推荐

所有评论(0)