动态内存管理

文章目录

    • 前言
    • 一、为什么要有动态内存分配
      • 动态内存的优势:
      • 示例:
    • 二、malloc和free
      • 2.1 malloc —— 内存分配器
      • 示例:
      • 2.2 free —— 内存释放器
      • 示例:
    • 三、calloc和realloc
      • 3.1 calloc —— 安全版本的malloc
      • 示例:
      • 3.2 realloc —— 动态扩容
      • 示例:
    • 四、常见的动态内存的错误
      • 4.1 对NULL指针的解引用操作
      • 4.2 对动态开辟空间的越界访问
      • 4.3 对非动态开辟内存使用free释放
      • 4.4 使用free释放一块动态开辟内存的一部分
      • 4.5 对同一块动态内存多次释放
      • 4.6 动态开辟内存忘记释放(内存泄漏)
    • 五、柔性数组
      • 5.1 柔性数组的特点
      • 5.2 柔性数组的使用
      • 5.3 柔性数组的优势
    • 六、总结C/C++中程序内存区域划分
    • 总结

前言

内存管理在 C/C++ 编程中尤为重要。尤其是 C 语言,缺少自动垃圾回收机制,程序员必须手动分配和释放内存。虽然这样提高了效率,但也容易出错。

本篇博客详细介绍 C 语言中的动态内存分配,包括基本函数的使用、常见错误,以及柔性数组的高级用法,帮助你写出更稳定、可靠的代码。
动态内存管理_第1张图片


一、为什么要有动态内存分配

在编程中,我们经常会遇到这样的问题:

“我怎么知道一个数组应该多大?”

当我们写:

int arr[100];

这就是静态内存分配,它的大小在编译时就固定了。但在很多场景中,我们需要的大小是运行时才能确定的,这时候就需要动态内存分配。

动态内存的优势:

  • 运行时申请大小,灵活
  • 空间用多少申请多少,节省内存
  • 支持构建动态结构,如链表、树、图。

示例:

#include 
#include 

int main() {
    int n;
    printf("请输入数组元素个数:");
    scanf("%d", &n);

    int* arr = (int*)malloc(n * sizeof(int));
    if (arr == NULL) {
        printf("内存申请失败\n");
        return 1;
    }

    for (int i = 0; i < n; i++) {
        arr[i] = i;
        printf("%d ", arr[i]);
    }

    free(arr);
    return 0;
}

二、malloc和free

2.1 malloc —— 内存分配器

malloc 是最常见的动态内存分配函数,它的作用是:

“在堆区分配一块指定大小的连续内存空间。”

语法如下:

void* malloc(size_t size);
  • size 是你想分配的字节数。
  • 返回值是 void*,需要强制类型转换。
  • 分配失败返回 NULL

示例:

int* p = (int*)malloc(5 * sizeof(int)); // 分配5个int的空间
if (p == NULL) {
    printf("内存分配失败\n");
    exit(1);
}

注意:malloc不会自动初始化,所以空间内是“垃圾值”。


2.2 free —— 内存释放器

分配了就必须释放。否则程序退出前这块内存一直被占着,造成内存泄漏

free(p); // p 是malloc/calloc分配的指针

释放后建议把指针置为NULL,防止野指针:

free(p);
p = NULL;

示例:

int* p = (int*)malloc(3 * sizeof(int));
// ... 使用p
free(p);
p = NULL;

三、calloc和realloc

3.1 calloc —— 安全版本的malloc

malloc 类似,但会把分配的内存全部初始化为0

void* calloc(size_t n, size_t size);
  • n 是元素个数。
  • size 是每个元素的大小。

示例:

int* arr = (int*)calloc(5, sizeof(int));
for (int i = 0; i < 5; i++) {
    printf("%d ", arr[i]); // 全是0
}
free(arr);

3.2 realloc —— 动态扩容

有时候分配的空间不够用,需要扩容,这时就用 realloc

void* realloc(void* ptr, size_t new_size);

它会尝试:

  • ptr 指向的内存扩容到 new_size 字节。
  • 返回新的指针(可能地址变了)。

示例:

int* p = (int*)malloc(3 * sizeof(int));
p[0] = 1; p[1] = 2; p[2] = 3;

int* temp = (int*)realloc(p, 5 * sizeof(int));
if (temp != NULL) {
    p = temp;
    p[3] = 4;
    p[4] = 5;
}

for (int i = 0; i < 5; i++) {
    printf("%d ", p[i]);
}
free(p);

注意: 原来的指针不能直接用 realloc 覆盖!避免扩容失败导致原内存丢失。


四、常见的动态内存的错误

4.1 对NULL指针的解引用操作

如果分配失败,返回 NULL,解引用将会崩溃:

int* p = (int*)malloc(999999999 * sizeof(int));
*p = 1; // 崩溃

正确做法是:先判断是否为NULL


4.2 对动态开辟空间的越界访问

int* p = (int*)malloc(3 * sizeof(int));
p[3] = 10; // 越界访问,未定义行为!

注意下标从 0 开始,最大只能到 2


4.3 对非动态开辟内存使用free释放

int x = 10;
free(&x); // 错误!不是malloc分配的!

只能释放 malloc/calloc/realloc 分配的空间。


4.4 使用free释放一块动态开辟内存的一部分

int* p = (int*)malloc(5 * sizeof(int));
free(p + 1); // 错误!只能释放p本身

必须释放完整起始地址。


4.5 对同一块动态内存多次释放

int* p = (int*)malloc(4);
free(p);
free(p); // 错误,重复释放!容易崩溃

正确做法:

free(p);
p = NULL;

4.6 动态开辟内存忘记释放(内存泄漏)

void func() {
    int* p = (int*)malloc(100);
    // 没有free(p),导致内存泄漏
}

程序运行时间长、开辟多了会让系统资源被吃光!


五、柔性数组

柔性数组是 C99 提出的结构体技巧,结构体最后一个元素声明为不定长数组:

struct S {
    int n;
    int arr[]; // 柔性数组
};

5.1 柔性数组的特点

  • 必须在结构体最后一个成员
  • 不能独立使用,必须通过动态内存配合。

5.2 柔性数组的使用

struct S {
    int size;
    int data[];
};

int main() {
    int n = 5;
    struct S* ps = (struct S*)malloc(sizeof(struct S) + n * sizeof(int));
    ps->size = n;
    for (int i = 0; i < n; i++) {
        ps->data[i] = i * i;
    }

    for (int i = 0; i < n; i++) {
        printf("%d ", ps->data[i]);
    }

    free(ps);
    return 0;
}

5.3 柔性数组的优势

  • 数据和结构体在一块连续内存中,更高效。
  • 比普通指针数组更紧凑、更安全。
  • 减少内存碎片,提高性能。

六、总结C/C++中程序内存区域划分

内存区域 描述
代码段 存放程序的机器指令
数据段 存储全局变量和静态变量的已初始化部分
BSS段 存储未初始化的全局变量/静态变量
栈区 存储函数调用时的局部变量、返回地址等,系统自动分配和释放
堆区 程序员手动管理的内存空间,需使用 malloc/free 手动操作

总结

动态内存管理是 C 编程的必修课。你要学会使用:

  • malloc/free 分配和释放内存;
  • calloc/realloc 进行初始化和扩容;
  • 了解并避免常见的内存使用错误;
  • 掌握柔性数组提升数据结构能力。

一条原则:谁申请的,谁负责释放!

你可能感兴趣的:(#,C语言基础知识,C语言动态内存分配,c语言,开发语言)