C语言—内存的管理和释放
一、Linux下内存分配管理
1.编译好的C程序文件分区
分析C语言下程序的内存分配,我们从一段简单C程序源码test.c开始。如下所示:
#include <stdio.h>
#include <stdlib.h>
int a = 1;
int main(void)
{
int b = 2;
printf("hello, world!\n");
exit(0);
}
Linux5.4环境下gcc编译生成a.out可执行文件,执行命令size a.out,结果如下:
我们发现:
C源代码进过预处理、编译、汇编和链接4步生成一个可执行程序。
程序在没有运行之前,也就是说程序没有被加载到内存前,可执行程序内部已经分好3段信息,分别是代码区(text)、数据区(data)和未初始化数据区(bss)三个部分。(部分人直接把data和bss合起来叫做全局区或静态区)。
2.C程序运行时内存分区
运行可执行程序,系统把程序加载到内存,除了根据可执行程序的信息分出代码区、初始化数据区和未初始化数据区之外,还额外增加了栈区和堆区。
下图所示为可执行代码存储时结构和运行时结构的对照图。一个正在运行着的C编译程序占用的内存分为代码区、初始化数据区、未初始化数据区、堆区和栈区5个部分。
典型的存储器安排,Linux下的内存分配:
text段、data段、bss段、heap和stack
(1) 代码区(text 段)
代码区存放CPU执行的机器指令。通常代码区是可共享的(即另外的执行程序可以调用它),因为对于频繁被执行的程序,只需要在内存中有一份代码即可。代码区通常是只读的,为了防止程序意外地修改了它的指令。(在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等)
(2) 全局初始化数据区/静态数据区(data 段)
该区包含了在程序中明确被初始化的全局变量、已经初始化的静态变量(包括全局静态变量和局部静态变量)和常量数据(如字符串常量),只初始化一次,数据段属于静态内存分配。
(这里需要注意的是const修饰的变量并不会存放在该区,而是取决于它定义的地方,局部定义的就存在栈区,全局定义的就存放在静态区。)
注: 字符串常量归类在代码区还是数据区有争议,也有把它单独拿出来归类文字常量区(只读数据区)。我们只要自己清楚它是在数据区和代码区的之间就行了。该区一般用于存储只读数据,字面常量都存在这个区里面。
(3) 未初始化数据区(bss段)
存入的是全局未初始化变量和未初始化静态变量。未初始化数据区的数据在程序开始执行之前被内核初始化为0或者空(NULL)。
(4) 堆区(heap)
堆是一个大容器,它的容量远大于栈,用于动态内存分配,堆在内存中位于BSS区和栈区之间。一般由程序员分配和释放,若不主动释放,程序结束时,由操作系统回收。堆区通常加载音频文件、视频文件、图像文件、文本文件以及大小超过栈大小由程序员主动申请分配的内存的大数组等。
(5) 栈区(stack)
栈区, 是用户存放程序临时创建的局部变量,也就是说我们函数括弧"{}"中定义的变量(但不包括static 声明的变量,static 意味着在数据段中存放变量)。除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。由于栈的先进先出特点,所以栈特别方便用来保存/ 恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。
注意:
①所有未初始化的静态变量和全局变量,编译器会默认赋初值0
②程序在加载到内存前,代码区和全局区(data和bss)的大小就是固定的,程序运行期间不能改变
③data段和bss区中的数据的生存周期为整个程序运行过程
④data段、text区和bss区是由编译器在编译时分配的,堆和栈是由系统在运行时分配的。
下面通过一段代码来查看C程序执行时的内存分配情况:
#include <stdio.h>
#include <stdlib.h>
int a; //全局区(静态区),bss段
int b = 5; //全局区(静态区),data段
static int c; //全局区(静态区),bss段
static int d = 6; //全局区(静态区),data段
void FunTest()
{
int *p1 = (int*)malloc(sizeof(int)); //在堆上开辟
// int *p2 = new int; //在堆上开辟(c++)
free(p1);
}
int main()
{
int e; //栈区
int f = 2; //栈区
static int g; //全局区(静态区),bss段
static int h = 4; //全局区(静态区),data段
const int i = 3; //栈区
char* p = "hello word"; //p在栈上,“hello word”在常量区
char str[] = "abcde"; //str为数组变量,存储在栈区
FunTest();
printf("全局未初始化变量a: %p\n",&a);
printf("全局初始化变量b: %p\n",&b);
printf("全局未初始化静态变量c:%p\n",&c);
printf("全局初始化静态变量d: %p\n",&d);
printf("\n");
printf("局部未初始化变量e: %p\n",&e);
printf("局部初始化变量f: %p\n",