内存四区
在内存中一般可以分为栈区,堆区,全局区,代码区。
- 栈区:由编译器自动分配,变量离开作用域后栈上的内存会自动释放。存放局部变量,函数形参等。(栈是从高地址向低地址方向增长)
- 堆区:堆是一个大容器,它的容量要远远大于栈。一般比较复杂的数据类型都是放在堆中。由程序员手动分配,需要程序员用后销毁。但若是分配空间后,未进行销毁,系统有可能会帮助销毁。
- 全局区:存放全局变量和静态变量,细分的话有一个常量区,字符串常量和其他常量也放在该位置。程序结束后,由系统自动释放。
- 代码区:存放程序的二进制文件,由系统统一管理。 可以从下图简单的看出程序运行流程:
- 首先是将保存在硬盘上的程序存到内存中;
- 编译器将程序划分为四个区,找到main函数,开始执行;
- 执行过程中,c程序进行内存数据进行管理。
栈内存管理的特点
栈的应用举例:局部变量
栈是一种数据结构,C语言中使用栈来保存局部变量。栈是被发明出来管理内存的。
我们在C中定义一个局部变量(int a),编译器会在栈中分配一段空间(4字节)给这个局部变量用,将a与这个栈中的内存地址关联起来,对应操作是入栈。(Push)。
注意:栈指针的移动和内存分配时自动的。(无需用户操作)
然后我们函数退出的时候,局部变量要销毁,对应栈的操作是出栈(Pop)
栈的优点
优点:栈管理内存,好处是方便,分配和最后回收都不需要程序员操心,C语言自动完成。
分析一个细节:在C语言中若是未初始化,则值是随机的。
定义局部变量,其实就是在栈中通过移动栈指针来给程序提供一个内存空间和这个局部变量绑定。因为这个内存空间在栈上,而栈空间是反复使用(出栈的时候,内容没有清零 ,只是删除前后链接。)。所以说使用栈来实现局部变量定义如果不显示初始化,那么其值就是随机的。
栈的约束
首先,栈是有大小的。所以栈的内存不灵活。太小若是溢出,太大容易浪费。
其次,栈的溢出危害很大,一定要避免。所以我们在c语言中定义局部变量中不能定义太多或者太大(譬如定义局部变量时 int a[50000],使用递归来解决问题时,一定要注意递归收敛。)
什么时候用栈呢?
一般遵循以下三个原则:
- 如果明确知道数据占用多少内存,那么数据量较小时用栈,较大时用堆;
- 如果不知道数据量大小(可能需要占用较大内存),最好用堆(因为这样保险些);
- 如果需要动态创建数组,则用堆。
堆内存管理的特点
堆(heap)是一种内存管理方式。
堆这种内存管理方式的特点是自由(随时申请,释放;大小快随意),堆内存是操作系统规划给堆管理器(操作系统中的一段代码,属于操作系统的内存管理单元。)然后向使用者(用户进程)提供API(malloc和free)来使用内存。
什么时候使用堆内存?
需要内存容量比较大,需要反复使用及释放时,很多数据结构(譬如链表)的实现都要使用堆内存。
特点一:容量大;
特点二:申请和释放都要手工释放;如果没有释放,这段内存就丢失了(在堆内存管理器的记录中,若是没有进行手工释放,这段内存仍然属于这个进程,但是进程自己又以为这段内存已经不用了,再用的时候又会去申请新的内存,这就叫吃内存。)称为内存泄漏。在c/c++内存泄漏是最严重的程序bug。这也是别人认为Java/c#比c/c++优秀的地方。
堆内存的接口
堆内存释放时,直接调用free释放即可。void free(void ptr);
堆内存申请时,有三个可以选择的类似功能的函数:
malloc,calloc,realloc
若要申请10个int元素的内存
malloc(10sizeof(int));
堆内存申请时必须给定大小,然后当申请完成后大小不变。如果要变只能通过realloc接口。
堆内存的优势和劣势
优势:灵活;
劣势:需要程序员处理细节释放内存,容易内存泄漏