首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >收集飞花令碎片——【C语言】动态内存管理&&数据存储的内存结构

收集飞花令碎片——【C语言】动态内存管理&&数据存储的内存结构

作者头像
枫亭湖区
发布2025-11-12 18:09:32
发布2025-11-12 18:09:32
140
举报

一、动态内存管理

C语言的动态内存管理。这可是C语言的“精髓”之一,也是新手(甚至老手)的“噩梦”之源 它就像给了你一个超能力:你可以在程序运行时,向上帝(操作系统)“借”一块内存,用完了再“还”回去

🧰 1)为什么需要动态内存管理?

我们先看看“不动态”的内存是什么样的:

  • 静态/全局内存 (Static/Global):
代码语言:javascript
复制
int global_var = 10;

这些变量在程序一开始运行时就被分配了,直到程序结束才释放。它们“长命百岁”,但不够灵活。


  • 自动内存 (Automatic / Stack)
代码语言:javascript
复制
void my_func() { int local_var = 5; }

函数里的局部变量,放在“栈”上。它们的好处是“自动”:函数一调用,它们就出生;函数一返回,它们就去世并自动释放


  • 动态内存(Heap / 堆)

从一块叫堆(Heap) 的巨大内存池里按需分配的


🧰 2)核心工具箱:四个关键函数

🌊🌊所有动态内存管理都围绕着 <stdlib.h> 头文件里的四个函数🌊🌊

1)malloc - 内存分配

摆渡游船:点击前往malloc函数官网

  • 原型: void* malloc(size_t size);
  • 作用: 你告诉它你需要多少字节(size),它会去堆里找一块这么大的、连续的空闲内存
  • 返回值:
    • 成功: 返回一个 void* 类型的指针,指向这块内存的起始地址。void* 是“通用指针”,你需要将它强制类型转换为你需要的类型(比如 int*, char* 等)。
    • 失败: 如果系统没钱(内存)了,它会返回NULL
代码语言:javascript
复制
// 申请一块足够存放 100 个整数的内存
int* my_array = (int*)malloc(100 * sizeof(int)); 

// 🚨 **极其重要:永远检查返回值!**
if (my_array == NULL) {
    // 内存分配失败了!
    printf("天呐!内存不足!\n");
    // 此时应该做一些错误处理,比如退出程序
    return 1; 
}

// 现在你可以像使用普通数组一样使用它
my_array[0] = 10;
my_array[99] = 123;

2)calloc - 连续分配

摆渡游船:点击前往calloc函数官网

  • 原型: void* calloc(size_t num_elements, size_t element_size);
  • 作用: 你告诉它你需要多少个元素(num_elements)以及每个元素多大(element_size)。
  • malloc 的区别:
    • 参数更直观(比如 calloc(100, sizeof(int)))。
    • 它会自动把分配的内存全部初始化为0 malloc 不会,malloc 给你的内存里可能是任何“垃圾数据”

3)realloc - 重新分配

摆渡游船:点击前往realloc函数官网

  • 原型: void* realloc(void* ptr, size_t new_size);
  • 作用: 调整 ptr 指向的那块内存的大小为 new_size

注意: 1. 如果新大小new_size小于 原大小,它会“截断”多余的内存 2. 如果new_size大于 原大小,它会尝试在原地扩容 3. 如果原地空间不够(这很常见),realloc 会在堆上另找一个足够大的新地方,把旧内存里的数据复制到新地方,然后释放旧的内存,最后返回新地址。

正因如此,你必须用一个新指针接收 realloc 的返回值!

代码语言:javascript
复制
// 之前申请了 100 个整数
int* my_array = (int*)malloc(100 * sizeof(int));
// ... 使用 ...

// 发现不够用,需要 200 个
int* bigger_array = (int*)realloc(my_array, 200 * sizeof(int));

if (bigger_array == NULL) {
    // 重新分配失败!
    // 注意:此时原来的 my_array 仍然是有效的(如果 realloc 失败)
    // 你需要决定怎么处理这种情况
} else {
    // 成功了,现在 my_array 可能已经失效了
    // 应该用 bigger_array 代替
    my_array = bigger_array; 
}

4)free - 释放

摆渡游船:点击前往free函数官网

  • 原型: void free(void* ptr);
  • 作用: 告诉操作系统,ptr 指向的这块动态内存你不用了,可以回收了
  • 规则:
    • 你只能 free 通过 malloc, calloc, realloc 得到的指针。
    • free(NULL) 是安全的,什么也不做
代码语言:javascript
复制
int* my_array = (int*)malloc(100 * sizeof(int));
// ... 尽情使用 ...

// 好了,用完了,必须释放
free(my_array);

🧰 3)动态内存的注意事项

3.1)动态开辟内存忘记释放(内存泄漏)
  • “只借不还” 1.你 malloc 了一块内存,但用完后忘记 free。这块内存就“丢失”了。你的程序还在运行,但它既不能使用这块内存(因为你把指向它的指针弄丢了),系统也不能把它分给别人。 2.如果你的程序(比如服务器)长时间运行,并且不断地泄漏内存,它最终会耗尽系统所有内存,然后崩溃。
在这里插入图片描述
在这里插入图片描述

3.2)野指针
  • “你退了房,但还拿着钥匙。”free(ptr) 之后,ptr 指针本身的值(那个地址)并没有变,但它指向的内存已经不属于你了。
  • 问题: 如果你稍后不小心又通过ptr去读写那块内存,你就是在“非法闯入”。你可能会读到垃圾数据,或者(更糟的)你可能会破坏掉其他人(程序里的另一部分)正在使用的数据。
  • 最佳实践: free 之后,立即将指针设为 NULL
代码语言:javascript
复制
free(my_array);
my_array = NULL; // 好习惯!

// 之后如果不小心访问
if (my_array != NULL) {
    my_array[0] = 5; // 这段代码就不会执行了
}

3.3)重复释放
  • “同一张账单付了两次钱。”free(ptr) 之后,过了一会儿又(不小心) free(ptr) 了一次。这会严重破坏堆的内部管理结构,几乎肯定会导致程序崩溃。
在这里插入图片描述
在这里插入图片描述

(这就是为什么 free 后设为 NULL 是个好习惯,因为 free(NULL) 是安全的)。


3.4)访问越界
  • “租了10平米的仓库,却往里塞了11平米的东西。”malloc(10 * sizeof(int)),只申请了10个整数的空间,但你却试图访问 my_array[10](这是第11个元素)
在这里插入图片描述
在这里插入图片描述

二、数据存储的内存结构

🧠 计算机内存中的“数据存储”是什么? 当你写下:

代码语言:javascript
复制
int x = 10;

这句代码看起来只是一个整数,但程序运行时,它必须存在某个地方才能被 CPU 操作。 这个地方就是 内存(Memory)

  • C 语言的数据存储模型主要描述: - 程序中不同数据存放在 哪一块内存区域 - 这些数据 何时被创建,何时被销毁 - 谁能访问它们(作用域)

🧮 1)内存区的详细特征

1️⃣ 栈(Stack)

  • 自动分配释放函数调用时开辟,返回时销毁
  • 存放 局部变量函数参数返回地址
  • 空间较小但速度极快(顺序分配)
  • 典型错误:返回栈变量的地址!
代码语言:javascript
复制
int *func() {
    int x = 100;
    return &x; // ❌ x 是栈变量,函数返回后内存已被回收
}

2️⃣ 堆(Heap)

  • 手动分配与释放: malloc / free
  • 生命周期: 程序员决定
  • 灵活但易出错(内存泄漏、悬空指针)
代码语言:javascript
复制
int *p = malloc(sizeof(int));
*p = 42;
free(p);     // ✅ 释放
p = NULL;    // ✅ 置空避免野指针

3️⃣ 全局区(静态区 / Data Segment)

  • 已初始化区:int a = 10;
  • 未初始化区(BSS 段):int b; (默认值为0)
  • 它们在程序启动时由系统分配,程序结束时系统释放

4️⃣ 常量区(Constant Segment)

  • 存放字面常量,如字符串常量 hello
  • 通常只读,不允许修改
代码语言:javascript
复制
char *s = "hello";
s[0] = 'H'; // ❌ 运行时错误(段错误)
  • 注意事项
代码语言:javascript
复制
char s[] = "hello"; // ✅ 拷贝到栈区,可修改
s[0] = 'H';

🧮 2)图片详解

在这里插入图片描述
在这里插入图片描述

三、总结

动态内存管理是C语言强大灵活性和性能的基石。它允许你构建真正复杂、高效的数据结构(如链表、树、哈希表) 但是也会导致各种安全漏洞

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-11-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、动态内存管理
    • 🧰 1)为什么需要动态内存管理?
    • 🧰 2)核心工具箱:四个关键函数
      • 1)malloc - 内存分配
      • 2)calloc - 连续分配
      • 3)realloc - 重新分配
      • 4)free - 释放
    • 🧰 3)动态内存的注意事项
      • 3.1)动态开辟内存忘记释放(内存泄漏)
      • 3.2)野指针
      • 3.3)重复释放
      • 3.4)访问越界
  • 二、数据存储的内存结构
    • 🧮 1)内存区的详细特征
    • 🧮 2)图片详解
  • 三、总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档