程序编译后运行时的内存分配
1.程序编译时的内存状况
编译时不分配内存。
编译器能够识别语法,数据类型等;然后逐行逐句检查编译成数据的.obj文件;然后再由连接程序将其连接成一个EXE文件//此时的程序以EXE文件的形式存放在磁盘上。
- 编译只是一个扫描过程,进行此法语法检查,代码优化。
- 编译只是根据声明时的类型进行占位,到以后程序执行时分配内存才会正确。换言之,声明是给编译器看的,聪明的编译器能根据声明帮你识别错误。
- 编译程序越好,程序运行时就越高效
2. 程序运行时的内存状态
当执行这个EXE文件以后,此程序就被加载到内存中,成为进程。
此时一开始程序会初始化一些全局变量;然后找到入口函数(main()或者WinMain()),就开始按程序的执行语句开始执行。此时需要的内存只能在程序的堆上进行动态增加/释放了。
首先上个例子,慢慢阐述
int a = 0;//全局初始化区
char *p1;//全局未初始化区
int main()
{
int b;//栈
char s[] = "abc";//s[]在栈区,abc在常量区
char *p2;//栈
char *p3 = "123456";//p3在栈上,123456\0在常量区
static int c = 0;//全局(静态)初始化区
p1 = (char *)malloc(10);
p2 = (char *)malloc(20);//分配得来10和20字节的区域就在堆区
strcpy(p1,"123456");//123456\0在常量区,编译器可能会把p3所指向的"123456"优化在一起
}
- 内存结构图示
- 内存不同区段说明
- text段
即代码段,用来存放程序的代码(函数)和部分整数常量。只读。 - data段
初始化过的全局变量数据段。包含初始化的静态变量,即全局变量和静态局部变量。该段的大小由程序源代码中值的大小确定,并且在运行时不会更改。用来保存初始化了的非0的全局变量,如果全局变量初始化为0,则编译有时会出于优化的考虑,将其放在bss段中。因为也是全局变量,所以在程序运行的整个生命周期内都存在于内存中。与bss段不同的是,data段中的变量既占程序运行时的内存空间,也占程序文件的储存空间。
可读写,因为可以在运行时更改变量的值。 - BSS段
用来存放没有被初始化或初始化为0的全局变量,因为是全局变量,所以在程序运行的整个生命周期内都存在于内存中。这个段中的变量只占用程序运行时的内存空间,而不占用程序文件的储存空间。 - heap堆区
为成员分配和释放。
heap(堆)是最自由的一种内存,一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。它与数据结构中的堆是两回事!分配方式倒是类似于链表。在C/C++中,用alloc系统函数和new申请的内存都存在于heap段中。
eg:
int main()
{
// C 中用 malloc() 函数申请
char* p1 = (char *)malloc(10);
cout<<(int*)p1<<endl; //输出:00000000003BA0C0
// 用 free() 函数释放
free(p1);
// C++ 中用 new 运算符申请
char* p2 = new char[10];
cout << (int*)p2 << endl; //输出:00000000003BA0C0
// 用 delete 运算符释放
delete[] p2;
}
- stack栈区
由编译器自动分配释放 ,存放函数参数值,局部变量的值(即临时变量)等。其操作方式类似于数据结构中的栈。
通常栈是向下(即向低地址)增长的,当向栈中push一个元素,栈顶指针就会向低地址移动,当从栈中pop一个元素,栈顶指针就会向高地址移动。栈中的数据只在当前函数或下一层函数中有效,当函数返回时,这些数据自动被释放,如果继续对这些数据进行访问,将发生未知的错误。通常我们在程序中定义的不是用malloc系统函数或new出来的变量,都是存放在栈中的。
eg:
int main()
{
int b; //栈
char s[] = "abc"; //栈
char *p2; //栈
}