学习程序到底怎么调用的和内存怎样分配的,其实了解函数的调用和堆栈的内存管理是很有必要的。
程序的代码是存放在代码区的,一般代码区为只读的,不可修改的,道理很简单就是程序的安全性。其他的常量什么的也有自己的内存区域,
栈的调用过程一般是内存地址向下分配的。
首先来看一个简单的例子: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;
}
汇编代码如下:
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值)
分析过程大家调试即可: