C语言下程序的堆栈调用(详细,图示)
来源:互联网 发布:计算机网络就业 知乎 编辑:程序博客网 时间:2024/05/19 11:44
以前接触程序时,只知道程序写的对,一般都能运行出来,但是却不知道程序是怎么一步一步将每一步编译链接起来的,今天我们用汇编来看一下程序到底是怎么在程序中运行的。
#include <stdio.h>int Add(int x,int y){int sum = 0;sum = x+y;return sum;}int main(){int a = 2;int b = 3;int ret = Add(a,b);printf("%d\n",ret);return 0;}
我们在写程序的时候都是先写main函数,知道main函数是系统函数,直接调用就行。但是,实际上main函数是被三个其他的函数一次调用而来的,而程序在执行的时候会依次给main函数开辟一块内存,然后在根据我们写的程序依次以堆栈的形式在这块内存上存取数据。如此一来,我们队程序的调用就可以清楚明确的了解了。
在调试的过程中,我们转到反汇编写来看看。在调用main函数之后,系统先将ebp(栈底指针)和esp(栈顶指针)压入到一块堆栈中(运行时堆栈,又叫栈帧),然后是esp与4cH想减(即把esp指针向上挪4c空间大小),再将ebx,esi,edi分别压到
堆栈中,经过le啊,mov,mov三条之令之后,main函数的初始空间就被填充成了13h(二进制位19)个cc
为了更加形象的了解堆栈的创建和销毁,我们以图示的方式来演示一下
当吧ebx,esi,edi这三个寄存器压入堆栈之后,将13h大小的空间初始化cc后,esp指针向上移动到edi处,此时main函数调用才算结束。程序接着往下执行,这时候到了真正的主程序,执行int a=2, int b=3;看看内存中a=2和b=3是怎么存储的
有没有注意到刚刚的ebp指针指向的位置是0018FF3C,现在02和03的位置正是在刚刚ebp指针的上面,一次我们可以想象到一条程序的存储是先从栈底开始存放,逐层向上
接下来程序往下走进入函数部分
00401020 push ebp00401021 mov ebp,esp00401023 sub esp,44h00401026 push ebx00401027 push esi00401028 push edi00401029 lea edi,[ebp-44h]0040102C mov ecx,11h00401031 mov eax,0CCCCCCCCh00401036 rep stos dword ptr [edi]4: int sum = 0;00401038 mov dword ptr [ebp-4],05: sum = x+y;0040103F mov eax,dword ptr [ebp+8]00401042 add eax,dword ptr [ebp+0Ch]00401045 mov dword ptr [ebp-4],eax6: return sum;00401048 mov eax,dword ptr [ebp-4]7: }0040104B pop edi0040104C pop esi0040104D pop ebx0040104E mov esp,ebp00401050 pop ebp00401051 ret
接下来到了好戏时间,先是执行int sum = 0;系统从ebp处往上分配给sum4个字节的内存赋值为0,接着执行int x+y,仔细看这块对应的汇编语言,它将ebp+8和ebp+0ch相加起来然后赋给了ebp-4,而我们知道,程序刚刚给sum = 0分配的内存正是ebp-4,而ebp+8h和ebp+0ch就是刚刚a和b形参的位置。这就说明,系统把形参a,b相加起来,把和放在了指向sum的内存处。
程序到这,堆栈的创建就算完成了,接下来就要到把在函数里执行的结果返回到刚刚在main函数里创建的指针处,继续看汇编程序。
0040104B pop edi0040104C pop esi0040104D pop ebx0040104E mov esp,ebp00401050 pop ebp00401051 ret
下来就是刚刚创建函数的堆栈的销毁了,先是pop(弹出堆栈)edi,esi,ebx这三个寄存器,然后将ebp给esp,也就是说把刚刚上面开辟的空间收回,然后经过ret跳回至刚刚在main函数处调用函数那儿(现场保护)。
回到调用处是执行add esp+8,就是说把栈顶指针向下移动8位,也就是刚刚跳过存放形参的位置
经过了汇编程序这么一走,我们看到了程序a+b的值在函数里运算完,传给了最初的ebp-0ch,就是刚刚实参b的上面(ret指向的内存),然后再经过printf函数打印出来,然后再执行一遍刚刚销毁Add函数的步骤,这样整个函数的堆栈创建和销毁就算执行完了。
整理一下上面的步骤就是:
1、先给main函数开辟空间
2、将寄存器和实参分别压到堆栈中
3、开辟一片形参空间,然后实参传给形参
4、给Add函数开辟一片空间,将寄存器和函数里的程序压入到这片空间中
5、把刚刚的形参进行运算,将计算结果放在函数的空间中
6、把结果传递给主函数,并且打印出来
7、Add函数的销毁
8、主函数的销毁
- C语言下程序的堆栈调用(详细,图示)
- c 语言调用汇编堆栈的详细分析
- 为何C语言(的函数调用)需要堆栈…
- 为何C语言(的函数调用)需要堆栈…
- 为何C语言(的函数调用)需要堆栈,而汇编语言却不需要堆栈
- 为何C语言(的函数调用)需要堆栈,而汇编语言却不需要堆栈
- 为何C语言(的函数调用)需要堆栈,而汇编语言却不需要堆栈(转载)
- ARM基础:为何C语言(的函数调用)需要堆栈,而汇编语言却不需要堆栈
- 为何C语言(的函数调用)需要堆栈,而汇编语言却不需要堆栈
- 为何C语言(的函数调用)需要堆栈,而汇编语言却不需要堆栈
- 为何C语言(的函数调用)需要堆栈,而汇编语言却不需要堆栈
- 为何C语言(的函数调用)需要堆栈,而汇编语言却不需要堆栈
- 为何C语言(的函数调用)需要堆栈,而汇编语言却不需要堆栈
- Linux(ubnutu)下编写运行c,c++程序的方法gcc+gdb+make(图示)
- Linux下函数调用堆栈帧的详细解释
- Linux下函数调用堆栈帧的详细解释
- Linux下函数调用堆栈帧的详细解释
- Linux下函数调用堆栈帧的详细解释
- HDU-5102-The K-th Distance【思维】【好题】
- Sass mixin
- DES(ecb)加密
- [BZOJ2788][Poi2012]Festival(差分约束+floyed+tarjan)
- Python性能分析
- C语言下程序的堆栈调用(详细,图示)
- POJ-1101_The Game
- 数据结构--平衡二叉树的插入详解
- Codeforces 801D Volatile Kite 几何
- 关于synchronized(object){//} 同步代码块
- 如何用abaqus建造8号槽钢并分析槽钢的受力
- Linux下mariadb的使用
- (转)机会还是陷阱:诺亚财富的私募股权策略
- JS中的变量提升