网站首页 > 技术文章 正文
我们都知道,在C语言中,有个东西叫做函数,任何的高手编写C语言的程序,基本都会用到函数,用函数去实现一个基本的功能,举个例子,实现一个求和的函数,如下:
int fun_xuanph(int a , intb){
return a+b;
}
int main(){
int a = 1;
int b = 2;
printf("sum=%d\n",fun_xuanph(a,b));
}
上面的程序是那么的简单,虽然简单,但是对于讲解函数调用背后的逻辑,足以了,可能有的人看到这个程序第一眼,会觉得这太简单了,不就是一个调用一个函数然后返回一个求和的值吗?我想说,你说得对,就是这么个玩意,但是,你可知道调用一个函数之前,都需要做什么吗?这就涉及到函数入栈的问题,这里要表达一个知识点,那就是任何函数都有自己的栈顶和栈底,那么为什么要有栈呢?
一个程序运行中,之所以要有栈,主要有以下的原因:
1) 保存上下文的环境
我们知道一个函数调用完后,要回到原来的地方去执行,那么就需要保留之前的数据,比如,返回地址,寄存器等,这些值会被存到栈中。
2) 局部变量的值也要保存到栈空间中。
一个函数内部使用的局部变量,也要存到栈中。
现在我们知道,一个函数想要执行,一定要开辟一段内存空间,来存放上下文以及函数内部的局部变量,这块空间就是栈。
我们刚才收到一个函数有自己的栈顶和栈底,那么用什么来表示栈顶和栈底呢?
其实是用两个寄存器来表示,栈顶是esp寄存器,栈底是ebp寄存器,出栈和入栈都会操作esp寄存器,将上面的程序进行反汇编,得到下面的汇编代码,下面就基于汇编程序讲讲函数入栈和出栈的过程:
000000000040052d <fun_xuanph>:
40052d: 55 push %rbp
40052e: 48 89 e5 mov %rsp,%rbp
400531: 89 7d fc mov %edi,-0x4(%rbp)
400534: 89 75 f8 mov %esi,-0x8(%rbp)
400537: 8b 45 f8 mov -0x8(%rbp),%eax
40053a: 8b 55 fc mov -0x4(%rbp),%edx
40053d: 01 d0 add %edx,%eax
40053f: 5d pop %rbp
400540: c3 retq
0000000000400541 <main>:
400541: 55 push %rbp
400542: 48 89 e5 mov %rsp,%rbp
400545: 48 83 ec 10 sub $0x10,%rsp
400549: c7 45 f8 01 00 00 00 movl $0x1,-0x8(%rbp)
400550: c7 45 fc 02 00 00 00 movl $0x2,-0x4(%rbp)
400557: 8b 55 fc mov -0x4(%rbp),%edx
40055a: 8b 45 f8 mov -0x8(%rbp),%eax
40055d: 89 d6 mov %edx,%esi
40055f: 89 c7 mov %eax,%edi
400561: e8 c7 ff ff ff callq 40052d <fun_xuanph>
400566: 89 c6 mov %eax,%esi
400568: bf 04 06 40 00 mov $0x400604,%edi
40056d: b8 00 00 00 00 mov $0x0,%eax
400572: e8 99 fe ff ff callq 400410 <printf@plt>
400577: b8 00 00 00 00 mov $0x0,%eax
40057c: c9 leaveq
40057d: c3 retq
可以看到第15行,将esp向下移动了16个字节,实际上这就是为main函数开辟的栈空间,这16个字节,存放的是局部变量a,局部变量b,以及调用fun_xuanph函数时,下一条指令的地址,如下图所示:
通过上图我们知道,rsp指向的是下一条指令的下面,这是由call指令产生的,在调用call指令时,会被call指令的下一条指令的地址进行入栈,因此rsp往下走8个字节,存下一条指令的地址,接下来,我们再看fun_xuanph函数的栈空间:
可以看到,汇编语言的第二行,调用了push rbp ,将rbp寄存器压入了栈中,进行保存,紧接着又调用了mov rsp,rbp 将rsp赋值给了rbp寄存器,此时rbp就是函数fun_xuanph的栈底,rsp就是fun_xuanph函数的栈顶。
猜你喜欢
- 2024-12-17 c语言函数递归调用 c语言函数递归法
- 2024-12-17 动态语言调用关系生成器,阅读代码效率大提升
- 2024-12-17 你的想象力限制了python能力,自动识别函数调用关系,还能可视化
- 2024-12-17 C语言学习之-----(十) 函数的定义和调用
- 2024-12-17 你所不知道的C语言:函数调用篇 c语言函数调用怎么写
- 2024-12-17 5.3 函数调用及执行过程 5.3 函数调用及执行过程视频
- 2024-12-17 C语言函数的调用原理不理解?深入浅出通俗易懂,一文读懂函数栈
- 2024-12-17 「图文」函数调用过程中栈的变化 函数调用的栈帧结构细节
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- oraclesql优化 (66)
- 类的加载机制 (75)
- feignclient (62)
- 一致性hash算法 (71)
- dockfile (66)
- 锁机制 (57)
- javaresponse (60)
- 查看hive版本 (59)
- phpworkerman (57)
- spark算子 (58)
- vue双向绑定的原理 (68)
- springbootget请求 (58)
- docker网络三种模式 (67)
- spring控制反转 (71)
- data:image/jpeg (69)
- base64 (69)
- java分页 (64)
- kibanadocker (60)
- qabstracttablemodel (62)
- java生成pdf文件 (69)
- deletelater (62)
- com.aspose.words (58)
- android.mk (62)
- qopengl (73)
- epoch_millis (61)
本文暂时没有评论,来添加一个吧(●'◡'●)