一、四大内存管理方法
| 函数 | 功能描述 | 常用场景 |
| void *calloc(int num, int size); | 在内存中动态地分配 num 个长度为 size 的连续空间,并将每一个字节都初始化为 0。所以它的结果是分配了 num*size 个字节长度的内存空间,并且每个字节的值都是 0。 | 需要初始值为 0 的数组(如前缀和) |
| void *malloc(int num); | 在堆区分配一块指定大小的内存空间,用来存放数据。这块内存空间在函数执行完成后不会被初始化,它们的值是未知的。 | 通用内存分配(一维 / 二维数组) |
| void *realloc(void *address, int newsize); | 该函数重新分配内存,把内存扩展到 newsize,原数据保留(新增部分未初始化) | 动态扩容数组(如结果集变长) |
| void free(void *address); | 该函数释放 address 所指向的内存块,释放的是动态分配的内存空间。 | 所有动态内存的销毁 |
二、一维数组
#include <stdio.h>
#include <stdlib.h>
int main() {
int n = 5; // 数组长度
int *arr = NULL;
// 方式1:malloc(未初始化,速度快)
arr = (int*)malloc(n * sizeof(int));
if (arr == NULL) { // 必做:检查内存分配是否成功
printf("内存分配失败\n");
return -1;
}
// 手动初始化(可选)
for (int i = 0; i < n; i++) {
arr[i] = i + 1; // [1,2,3,4,5]
}
// 方式2:calloc(自动初始化为0,无需手动初始化)
// arr = (int*)calloc(n, sizeof(int)); // 直接得到 [0,0,0,0,0]
// 方式3:realloc 扩容(比如从5个元素扩容到8个)
int new_n = 8;
int *temp = (int*)realloc(arr, new_n * sizeof(int)); // 临时指针接收
if (temp == NULL) { // 扩容失败,原arr仍有效
printf("扩容失败\n");
free(arr); // 释放原内存
arr = NULL;
return -1;
}
arr = temp; // 扩容成功,更新指针
// 扩容后的新增元素(5-7)未初始化,需手动赋值
for (int i = 5; i < new_n; i++) {
arr[i] = i + 1; // [1,2,3,4,5,6,7,8]
}
// 使用数组(刷题时替换为业务逻辑)
for (int i = 0; i < new_n; i++) {
printf("%d ", arr[i]);
}
// 2. 销毁内存(核心:free + 置NULL)
free(arr);
arr = NULL; // 避免野指针
return 0;
}
三、二维数组
方式一:非连续内存
#include <stdio.h>
#include <stdlib.h>
int main() {
int rows = 3, cols = 4;
int **matrix = NULL;
// 1. 分配内存(两步:先分行指针,再分每行的列内存)
// 第一步:分配行指针数组(3行 → 3个int*指针)
matrix = (int**)malloc(rows * sizeof(int*));
if (matrix == NULL) {
printf("行指针分配失败\n");
return -1;
}
// 第二步:为每行分配列内存(每行4个int)
for (int i = 0; i < rows; i++) {
matrix[i] = (int*)malloc(cols * sizeof(int));
if (matrix[i] == NULL) { // 某行分配失败,需回滚已分配的内存
for (int j = 0; j < i; j++) {
free(matrix[j]);
matrix[j] = NULL;
}
free(matrix);
matrix = NULL;
printf("列内存分配失败\n");
return -1;
}
// 初始化(可选,calloc可省略这步)
for (int j = 0; j < cols; j++) {
matrix[i][j] = i * cols + j; // 0,1,2,3 | 4,5,6,7 | 8,9,10,11
}
}
// 2. 使用二维数组
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
printf("%d ", matrix[i][j]);
}
printf("\n");
}
// 3. 销毁内存(必须分层释放:先释放每行,再释放行指针)
for (int i = 0; i < rows; i++) {
free(matrix[i]);
matrix[i] = NULL; // 避免野指针
}
free(matrix);
matrix = NULL;
return 0;
}
方式二:连续内存
#include <stdio.h>
#include <stdlib.h>
int main() {
int rows = 3, cols = 4;
int **matrix = NULL;
int *data = NULL; // 存储所有元素的连续内存
// 1. 分配内存(两步:先分连续数据区,再构建行指针)
// 第一步:分配连续的总内存(3*4=12个int)
data = (int*)malloc(rows * cols * sizeof(int));
if (data == NULL) {
printf("连续内存分配失败\n");
return -1;
}
// 第二步:分配行指针数组,指向每行的起始位置
matrix = (int**)malloc(rows * sizeof(int*));
if (matrix == NULL) {
free(data);
data = NULL;
printf("行指针分配失败\n");
return -1;
}
for (int i = 0; i < rows; i++) {
matrix[i] = data + i * cols; // 每行指向data的对应偏移
}
// 初始化
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
matrix[i][j] = i * cols + j;
}
}
// 2. 使用(和方式1完全一致)
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
printf("%d ", matrix[i][j]);
}
printf("\n");
}
// 3. 销毁内存(先释放连续数据区,再释放行指针)
free(data);
data = NULL;
free(matrix);
matrix = NULL;
return 0;
}
三、注意事项
1.内存分配失败的处理
LeetCode 中题目输入通常不会导致内存分配失败,但调试时需加NULL检查;若某行分配失败,必须回滚已分配的内存(避免内存泄漏)。
2.锯齿数组(每行长度不同)
比如 LeetCode 118 题(杨辉三角),每行长度不同,用指针数组实现:
// 杨辉三角:第i行有i+1个元素
int** generate(int numRows, int* returnSize, int** returnColumnSizes) {
*returnSize = numRows;
// 分配行指针
int** res = (int**)malloc(numRows * sizeof(int*));
// 分配每行列数的数组(返回给题目)
*returnColumnSizes = (int*)malloc(numRows * sizeof(int));
for (int i = 0; i < numRows; i++) {
(*returnColumnSizes)[i] = i + 1;
res[i] = (int*)malloc((i + 1) * sizeof(int));
// 填充杨辉三角逻辑...
}
return res;
}
3.避免野指针
free(p) 后必须执行 p = NULL,否则p仍指向已释放的内存(野指针),后续操作会导致程序崩溃。
4.realloc 的正确用法
不要直接 arr = realloc(arr, new_size),需用临时指针接收:
int *temp = (int*)realloc(arr, new_size);
if (temp != NULL) {
arr = temp; // 扩容成功才更新
} else {
// 扩容失败,原arr仍有效,需处理错误
free(arr);
arr = NULL;
}