程序编译后运行时的内存分配
一、编译时与运行时的内存情况
1.编译时不分配内存
编译时是不分配内存的。此时只是根据声明时的类型进行占位,到以后程序执行时分配内存才会正确。所以声明是给编译器看的,聪明的编译器能根据声明帮你识别错误。
2.运行时必分配内存
运行时程序是必须调到“内存”的。因为CPU(其中有多个寄存器)只与内存打交道的。程序在进入实际内存之前要首先分配物理内存。
3.编译过程
只能简单说一下,因为如果要详细的话,就是一本书了《编译原理》。编译器能够识别语法,数据类型等等。然后逐行逐句检查编译成二进制数据的obj文件,然后再由链接程序将其链接成一个EXE文件。此时的程序是以EXE文件的形式存放在磁盘上。
4.运行过程
当执行这个EXE文件以后,此程序就被加载到内存中,成为进程。此时一开始程序会初始化一些全局对象,然后找到入口函数(main()或者WinMain()),就开始按程序的执行语句开始执行。此时需要的内存只能在程序的堆上进行动态增加/释放了。
二、程序运行时的内存结构: X
第一种图示:
另外一种图示:
各区段功能说明:
1、程序代码区:存放函数体的二进制代码。
2、全局区(静态区)(static):全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 程序结束后由系统释放。
3、文字常量区 :常量字符串就是放在这里的。 程序结束后由系统释放。
4、堆区(heap): 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。
5、栈区(stack):由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
堆指:(满足堆性质的)优先队列的一种数据结构,第1个元素有最高的优先权;
栈指:满足先进后出的性质的数学或数据结构。
trong>例如:
#include
#include
int overflow(char *buf)
{
char output[20];
strcpy(output,buf);
printf('out char output[20]/n',output);
getchar();
return 0;
}
int main()
{
char buf[100];
int j;
for(j=0;j<100;j )
{
buf[j]='A';
}
overflow(buf);
return 0;
}
例:
ESP,EBP,EIP都是系统的寄存器,里面存的都是些地址。
为什么要说这三个指针,是因为我们系统中栈的实现上离不开他们三个。
栈的数据结构,主要有以下特点: 后进先出。 它还有以下两个作用:1.栈是用来存储临时变量,函数传递的中间结果。2.操作系统维护的,对于程序员是透明的。
通过一个小例子说说栈的原理:先写个小程序:void fun(void){ printf('hello world');}void main(void){ fun() printf('函数调用结束');}这是一个简单的函数调用的例子。当程序进行函数调用的时候,我们经常说的是先将函数压栈,当函数调用结束后,再出栈。这一切的工作都是系统帮我们自动完成的。但在完成的过程中,系统会用到下面三种寄存器:1.ESP2.EBP3.EIP
当调用fun函数开始时,三者的作用。
1.EIP寄存器里存储的是CPU下次要执行的指令的地址。 也就是调用完fun函数后,让CPU知道应该执行main函数中的printf('函数调用结束')语句了。
2.EBP寄存器里存储的是是栈的栈底指针,通常叫栈基址,这个是一开始进行fun()函数调用之前,由ESP传递给EBP的。(在函数调用前你可以这么理解:ESP存储的是栈顶地址,也是栈底地址。)
3.ESP寄存器里存储的是在调用函数fun()之后,栈的栈顶,并且始终指向栈顶。
当调用fun函数结束后,三者的作用:
1.系统根据EIP寄存器里存储的地址,CPU就能够知道函数调用完,下一步应该做什么,也就是应该执行main函数中的printf(“函数调用结束”)。
2.EBP寄存器存储的是栈底地址,而这个地址是由ESP在函数调用前传递给EBP的。等到调用结束,EBP会把其地址再次传回给ESP。所以ESP又一次指向了函数调用结束后,栈顶的地址。
其实我们对这个只需要知道三个指针是什么就可以,对我们学习栈溢出的问题以及看栈这方面的书籍有些帮助。 PS:<转>例子://main.cpp int a = 0; //全局初始化区
char *p1; //全局未初始化区
main()
{
int b; //栈
char s[] = 'abc''; //栈
char *p2; //栈
char *p3 = '123456'; //123456\0在常量区,p3在栈上。
static int c =0; //全局(静态)初始化区
p1 = (char *)malloc(10);
p2 = (char *)malloc(20);
}
分配得来得10和20字节的区域就在堆区。
strcpy(p1, '123456'); 123456\0放在常量区,编译器可能会将它与p3所指向的'123456'优化