C语言|内存管理

C 内存管理 | 菜鸟教程

一、四大内存管理方法

函数功能描述常用场景
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;
}

发表评论