C语言动态内存管理:从 “malloc“ 到 “free“
动态内存管理是 C 语言中一项核心能力它允许程序在运行时向操作系统申请内存而不是在编译时就固定数组大小。掌握它意味着你能写出更灵活、更高效的程序。一、为什么需要动态内存假设你要写一个程序让用户输入学生人数然后录入成绩int n; scanf(%d, n); int scores[n]; // C99 支持变长数组但...变长数组VLA虽然语法简洁却有局限性大小受栈空间限制生命周期局限于所在作用域。当数据量很大比如百万级别或者需要在函数间传递大块数据时堆Heap上的动态内存才是正确选择。二、四大核心函数C 标准库stdlib.h提供了四个关键函数函数作用malloc(size)申请size字节的未初始化内存calloc(n, size)申请n * size字节并清零realloc(ptr, new_size)调整已申请内存的大小free(ptr)释放内存归还操作系统三、malloc 与 free 的基本用法#include stdio.h #include stdlib.h int main() { int n; printf(请输入学生人数: ); scanf(%d, n); // 申请能放 n 个 int 的内存 int *scores (int *)malloc(n * sizeof(int)); if (scores NULL) { printf(内存申请失败\n); return 1; } // 输入成绩 for (int i 0; i lt; n; i) { printf(第 %d 个学生成绩: , i 1); scanf(%d, amp;scores[i]); } // 计算平均分 int sum 0; for (int i 0; i lt; n; i) { sum scores[i]; } printf(平均分: %.2f\n, (double)sum / n); // 释放内存 free(scores); scores NULL; // 好习惯防止野指针 return 0; }关键点malloc返回void*通常强制转换为所需类型指针。必须检查返回值是否为NULL申请失败时不能继续使用。free后把指针置为NULL避免后续误用造成难以定位的崩溃。四、calloc带清零的内存申请如果你需要一块全为 0 的内存calloc比malloc更方便int *arr (int *)calloc(100, sizeof(int)); // 等价于 malloc(100 * sizeof(int)) memset(arr, 0, 100 * sizeof(int))calloc的两个参数分别是元素个数和每个元素的大小语义更清晰且自动清零适合初始化数组或结构体。五、realloc灵活调整大小动态内存的优势在于动态。如果数组满了可以用realloc扩容#include stdio.h #include stdlib.h int main() { int capacity 4; // 初始容量 int count 0; // 当前元素个数 int *arr (int *)malloc(capacity * sizeof(int)); int num; printf(输入数字-1 结束:\n); while (scanf(%d, amp;num) 1 amp;amp; num ! -1) { if (count gt; capacity) { capacity * 2; // 容量翻倍 int *tmp (int *)realloc(arr, capacity * sizeof(int)); if (tmp NULL) { printf(扩容失败\n); free(arr); return 1; } arr tmp; printf(扩容至 %d\n, capacity); } arr[count] num; } printf(共录入 %d 个数: , count); for (int i 0; i lt; count; i) { printf(%d , arr[i]); } printf(\n); free(arr); return 0; }注意realloc可能将数据迁移到新地址所以要用临时指针接收返回值确认非空后再赋值给原指针。否则一旦realloc失败原内存地址会丢失造成内存泄漏。六、常见错误与避坑指南1. 内存泄漏申请了却不释放void leak() { int *p malloc(100); // 函数结束p 被销毁但 100 字节仍在堆上无法访问也无法释放 }解决确保每条malloc/calloc/realloc路径都有对应的free。2. 使用已释放的内存Use-After-Freefree(p); printf(%d\n, *p); // 危险p 指向的内存已归还系统解决free后立即将指针置NULL。3. 重复释放Double Freefree(p); free(p); // 未定义行为可能崩溃解决同样释放后置NULLfree(NULL)是安全的什么都不会发生。4.malloc与free不匹配不要用free释放非动态申请的内存如栈上的局部数组也不要用delete那是 C 的。七、二维数组的动态分配有时你需要动态的矩阵。一种直观的方式是指针的指针#include stdio.h #include stdlib.h int main() { int rows 3, cols 4; // 申请行指针数组 int **matrix (int **)malloc(rows * sizeof(int *)); for (int i 0; i lt; rows; i) { matrix[i] (int *)malloc(cols * sizeof(int)); } // 使用 matrix[1][2] 100; printf(matrix[1][2] %d\n, matrix[1][2]); // 释放先释放每行再释放行指针数组 for (int i 0; i lt; rows; i) { free(matrix[i]); } free(matrix); return 0; }这种方式每行内存不一定连续。如果需要物理上连续的一块内存利于缓存友好可以一次性申请rows * cols大小然后通过宏或手动计算索引访问。八、总结要点说明malloc申请未初始化内存记得检查NULLcalloc申请并清零适合数组初始化realloc扩容/缩容用临时指针接收结果free配对释放释放后置NULL核心原则谁申请谁释放申请必检查释放防野指针动态内存管理是 C 语言强大的根源之一也是最容易出错的地方。理解堆与栈的区别养成申请-检查-使用-释放的完整闭环习惯你的程序会更加健壮。