C语言–内存管理
文章目录
一. 内存分类
计算机的内存有“运行内存”和“存储内存(磁盘)”,程序启动期间申请的内存来自于运行内存。
运行内存可以简单分类为:栈区、堆区、全局区、常量区和代码区。
1.1 栈区(Stack)
定义:
栈区用于存储函数的局部变量、函数参数和返回地址。栈区内存由系统自动分配和释放,具有后进先出(LIFO)的特性。
特点:
- 分配效率高,但空间有限;
- 编译器自动管理,无需程序员手动释放。
注意:
用于保存局部变量。栈上的内容只在函数的范围内存在,当函数运行结束,这些内容 也会自动被销毁。
1.2 堆区(Heap)
定义:
堆区用于动态分配内存,即程序运行时使用malloc、new等函数分配的内存。
特点:
- 可以动态分配和释放大块内存。
- 内存由程序员手动管理,需要手动释放(使用free函数)。
- 内存分配效率相对较低,但空间较大。
注意:
其生命周期由 free 或 delete 决定。 在没有释放之前一直存在,直到程序结束。
1.3 全局区
全局/静态存储区:存放全局变量和静态变量(包括静态全局变量与静态局部变量),初始化的全局变量和静态局部变量放在一块,未初始化的放在另一块。
1.4 常量区
常量在统一运行被创建,常量区的内存是只读的,程序结束后由系统释放。
1.5 代码区
存放程序的二进制代码,内存由系统管理。
二. 内存分配方式
2.1 静态内存分配
静态内存分配是在程序编译时进行的,它将内存分配给全局变量和静态变量,存在周期最长。
优点:
内存分配和释放的效率高。
缺点:
内存使用不灵活,无法根据需要动态调整内存大小。
2.2 栈内分配
栈内存分配是在程序运行时进行的,它将内存分配给函数内部的局部变量。
优点:
内存管理简单,不需要程序员手动释放。
缺点:
内存空间有限,不适合分配大内存,且存在栈溢出(写入长度超过申请长度)的风险。
2.3 动态内存分配
动态内存分配是在程序运行时根据需要进行的内存分配。
优点:
内存使用灵活,可以根据需要动态调整内存大小。
缺点:
内存管理复杂,需要程序员手动分配和释放,容易出现内存泄漏等问题。
常用的动态内存分配函数包括malloc()、calloc()和realloc(),分别用于分配内存、分配并初始化为0的内存、以及重新调整已分配内存的大小。
2.3.1 申请内存
注意:
不要申请一个大小为0的空间。
2.3.2 释放内存
注意:
free 完之后, 一定要给指针置 NULL,否则该指针也会变为一个野指针。
三. 常见内存错误
- 未初始化的指针;
- 指针释放后未置空;
- 局部变量指针逃逸: 当函数返回时,其栈上的局部变量将不再有效。如果指针仍然指向这些局部变量,它们将成为野指针;
- 指针运算错误:对指针进行算术运算时,如果运算后的指针指向了未知或无效的内存区域,也会形成野指针;