现在的位置: 首页 > 综合 > 正文

函数调用和栈的内存分配过程分析

2013年10月04日 ⁄ 综合 ⁄ 共 3193字 ⁄ 字号 评论关闭

学习程序到底怎么调用的和内存怎样分配的,其实了解函数的调用和堆栈的内存管理是很有必要的。

程序的代码是存放在代码区的,一般代码区为只读的,不可修改的,道理很简单就是程序的安全性。其他的常量什么的也有自己的内存区域, 

栈的调用过程一般是内存地址向下分配的。

首先来看一个简单的例子:VC6.0测试

// struct.cpp : Defines the entry point for the console application.
//

#include 
"stdafx.h"
#include 
<string.h>

int compare(int a)
{
    
int c = 0;
    c 
= 20;

    
if(a>c)
    
{
        
return a;
    }

    
else
    
{
        
return c;
    }


}




int main(int argc, char* argv[])
{
    
int i = 2;
    
    
int c = compare(i);
    printf(
"%d  ",c);

    
return 0;
}


 

汇编代码如下:

25:   int main(int argc, char* argv[])
26:   {
00401080   push        ebp
00401081   mov         ebp,esp
00401083   sub         esp,48h
00401086   push        ebx
00401087   push        esi
00401088   push        edi
00401089   lea         edi,[ebp-48h]
0040108C   mov         ecx,12h
00401091   mov         eax,0CCCCCCCCh
00401096   rep stos    dword ptr [edi]
27:       int i = 2;
00401098   mov         dword ptr [ebp-4],2
28:
29:       int c = compare(i);
0040109F   mov         eax,dword ptr [ebp
-4]
004010A2   push        eax
004010A3   call        @ILT
+20(swap) (00401019)
004010A8   add         esp,
4
004010AB   mov         dword ptr [ebp
-8],eax
30:       printf("%d  ",c);
004010AE   mov         ecx,dword ptr [ebp
-8]
004010B1   push        ecx
004010B2   push        offset 
string "%d  " (0042001c)
004010B7   call        printf (
00401190)
004010BC   add         esp,
8
31:
32:       return 0;
004010BF   xor         eax,eax
33:   }

004010C1   pop         edi
004010C2   pop         esi
004010C3   pop         ebx
004010C4   add         esp,48h
004010C7   cmp         ebp,esp
004010C9   call        __chkesp (
00401210)
004010CE   mov         esp,ebp
004010D0   pop         ebp
004010D1   ret

-----------------------------------------------------------------
00401019   jmp         compare (00401030)
-----------------------------------------------------------------

   
int compare(int a)
8:    {
00401030   push        ebp
00401031   mov         ebp,esp
00401033   sub         esp,44h
00401036   push        ebx
00401037   push        esi
00401038   push        edi
00401039   lea         edi,[ebp-44h]
0040103C   mov         ecx,11h
00401041   mov         eax,0CCCCCCCCh
00401046   rep stos    dword ptr [edi]
9:        int c = 0;
00401048   mov         dword ptr [ebp-4],0
10:       c = 20;
0040104F   mov         dword ptr [ebp
-4],14h
11:
12:       if(a>c)
00401056   mov         eax,dword ptr [ebp+8]
00401059   cmp         eax,dword ptr [ebp-4]
0040105C   jle         compare
+33h (00401063)
13:       {
14:           return a;
0040105E   mov         eax,dword ptr [ebp
+8]
00401061   jmp         compare+36h (00401066)
15:       }

16:       else
17:       {
18:           return c;
00401063   mov         eax,dword ptr [ebp-4]
19:       }

20:
21:   }

00401066   pop         edi
00401067   pop         esi
00401068   pop         ebx
00401069   mov         esp,ebp
0040106B   pop         ebp
0040106C   ret

 

个人总结部分如下:

1 函数返回值一般在调用函数时已经分配指向该返回值的内存地址或寄存器;

2 函数调用的栈部分是连续的,通过esp,ebp相互赋值实现。创建新函数首先会将新的esp作为新函数的ebp;(因为该原因,所以可以被利用来作缓冲区溢出)

3 函数调用需要保存此前的CPU寄存器的值,通过新创建的栈空间来保存push(value)。

4 函数调用结束前会pop(value)返回前面函数时的cpu环境,从而实现了函数的调用。

5 函数内的变量是在ebp的基础上进行内存分配的。(如果分配的内存不够用,就会出现缓冲区溢出问题)

运行时栈的转换过程截图如下:(下面的就是0x0012ff80和0x0012ff20两个函数的ebp,compare函数的最后pop就是返回前函数的ebp值)

分析过程大家调试即可:

 

抱歉!评论已关闭.