当前位置: 首页 > news >正文

第22讲:动态内存管理

📚 第22讲:动态内存管理 🧠💥

从“栈上小打小闹”到“堆上自由驰骋”,掌握C语言的“内存遥控器”!
告别固定大小的束缚,让程序按需生长!


📂 目录

  1. 为什么要有动态内存分配 —— 程序的“弹性需求” 🌱
  2. mallocfree —— 申请与释放的“黄金搭档” 💰
  3. callocrealloc —— 智能初始化与灵活扩容 📏
  4. 常见的动态内存错误 —— 程序员的“十大禁令” ⚠️
  5. 动态内存经典笔试题分析 —— 面试官最爱的“陷阱题” 🎣
  6. 柔性数组 —— 结构体中的“可变长字段” 🔄
  7. C/C++程序内存区域划分 —— 内存世界的“地图” 🗺️

📖 正文开始

前面我们用 int a[10]; 在栈上开辟空间,简单直接。
但问题来了:

  • 数组大小必须编译时确定
  • 程序运行中才知道要多大怎么办?
  • 数组开小了,能“长大”吗?

这时候,动态内存管理就登场了!它让我们在堆区自由申请、释放内存,灵活无比!


为什么要有动态内存分配 —— 程序的“弹性需求” 🌱

🌟 生活比喻:租房 vs 买房

  • 栈空间

    :像“租房”——大小固定,到期自动退房。

    int arr[10]; // 房子大小10,不能改
    
  • 堆空间

    :像“买地自建房”——想多大就多大,自己管理。

    int* p = malloc(n * sizeof(int)); // 按需申请
    

✅ 动态内存分配的核心:程序运行时才知道要多少空间!


mallocfree —— 申请与释放的“黄金搭档” 💰

🔹 malloc:申请内存

void* malloc(size_t size);
✅ 特点:
  • 成功:返回指向连续空间的指针。
  • 失败:返回 NULL必须检查!
  • 返回 void* → 需要强制类型转换
  • size 为0 → 行为未定义(取决于编译器)。

🔹 free:释放内存

void free(void* ptr);
✅ 特点:
  • 专门释放 malloc/calloc/realloc 申请的内存。
  • ptrNULL → 什么都不做(安全)。
  • ptr 不是动态内存 → 未定义行为!危险!

✅ 完整示例:动态数组

#include <stdio.h>
#include <stdlib.h>int main() {int num;scanf("%d", &num);int* ptr = (int*)malloc(num * sizeof(int));if (ptr == NULL) {printf("内存申请失败!\n");return 1;}for (int i = 0; i < num; i++) {ptr[i] = i;}free(ptr);      // ✅ 释放ptr = NULL;     // ✅ 避免野指针(强烈推荐!)return 0;
}

🔑 黄金法则mallocfree 必须成对出现!


callocrealloc —— 智能初始化与灵活扩容 📏

🔹 calloc:带初始化的申请

void* calloc(size_t num, size_t size);
  • 功能:申请 num 个大小为 size 的元素,并自动初始化为0
  • malloc 唯一区别:初始化
✅ 示例:
int* p = (int*)calloc(10, sizeof(int));
// 输出:0 0 0 0 0 0 0 0 0 0

✅ 如果需要清零,优先用 calloc


🔹 realloc:灵活调整大小

void* realloc(void* ptr, size_t size);
✅ 两种情况:
情况行为
原地扩容后面有足够空间 → 直接追加,返回原地址
异地扩容后面不够 → 找新空间,拷贝数据,释放旧空间,返回新地址

⚠️ 使用陷阱:直接赋值危险!

// ❌ 危险!如果realloc失败,ptr变成NULL,原内存丢失!
ptr = (int*)realloc(ptr, 1000);// ✅ 正确做法:用临时指针
int* temp = (int*)realloc(ptr, 1000);
if (temp != NULL) {ptr = temp; // 确认成功后再更新
}

✅ 安全第一!永远不要直接覆盖原指针!


常见的动态内存错误 —— 程序员的“十大禁令” ⚠️

🔴 禁令1:对 NULL 指针解引用

int* p = malloc(INT_MAX);
*p = 20; // ❌ 如果malloc失败,p是NULL,崩溃!

✅ 解决:永远检查 malloc 返回值!


🔴 禁令2:越界访问

int* p = malloc(10 * sizeof(int));
for (int i = 0; i <= 10; i++) { // i=10时越界!p[i] = i;
}

✅ 解决:循环条件写对!


🔴 禁令3:释放非动态内存

int a = 10;
int* p = &a;
free(p); // ❌ p不是malloc来的!未定义行为!

✅ 解决:free 只用于 malloc/calloc/realloc


🔴 禁令4:释放部分内存

int* p = malloc(100);
p++;
free(p); // ❌ p不再指向起始位置!

✅ 解决:free 时指针必须指向 malloc 返回的地址!


🔴 禁令5:重复释放

free(p);
free(p); // ❌ 重复释放!可能崩溃!

✅ 解决:释放后立即将指针置为 NULL


🔴 禁令6:内存泄漏

void test() {int* p = malloc(100);// 忘记free!函数结束,指针消失,内存永远丢失!
}

✅ 解决:谁申请,谁释放! 每次 malloc 都要想好在哪里 free


动态内存经典笔试题分析 —— 面试官最爱的“陷阱题” 🎣

💣 题目1:传值陷阱

void GetMemory(char* p) {p = malloc(100); // ❌ 修改的是形参p,不影响str
}
void Test() {char* str = NULL;GetMemory(str);strcpy(str, "hello"); // ❌ str仍是NULL,崩溃!
}

✅ 正确:传二级指针


💣 题目2:返回栈空间

char* GetMemory() {char p[] = "hello world"; // p在栈上return p; // ❌ 函数结束,p被销毁,返回野指针!
}

✅ 错误:返回局部数组地址!


💣 题目3:正确使用二级指针

void GetMemory(char** p, int num) {*p = malloc(num); // ✅ 修改str指向的地址
}
void Test() {char* str = NULL;GetMemory(&str, 100); // ✅ 成功申请strcpy(str, "hello"); // ✅ 安全写入
}

✅ 正确!输出:hello


💣 题目4:释放后使用

char* str = malloc(100);
strcpy(str, "hello");
free(str);
if (str != NULL) { // str值可能未变,但内存已释放!strcpy(str, "world"); // ❌ 使用已释放内存!未定义行为!
}

✅ 解决:释放后立即将 str = NULL


柔性数组 —— 结构体中的“可变长字段” 🔄

🌟 生活比喻:可伸缩的行李箱

普通结构体像“固定大小的箱子”,而柔性数组像“可拉伸的行李箱”!


✅ 什么是柔性数组?

C99 允许结构体最后一个成员是未知大小的数组

struct st_type {int i;int a[]; // 柔性数组成员
};

⚠️ 有些编译器不支持 [],可用 a[0] 替代。


🔍 柔性数组的特点

  1. 前面至少有一个其他成员。
  2. sizeof(结构体) 不包含柔性数组的内存。
  3. 必须用 malloc 动态分配,且分配空间 > sizeof(结构体)
printf("%d\n", sizeof(struct st_type)); // 输出:4 (只有int i)

✅ 使用方式

struct st_type* p = malloc(sizeof(struct st_type) + 100 * sizeof(int));
p->i = 100;
for (int i = 0; i < 100; i++) {p->a[i] = i;
}
free(p); // ✅ 一次释放!

🆚 柔性数组 vs 指针成员

方式代码优点
柔性数组int a[];1. 一次 malloc/free 2. 内存连续,访问快
指针成员int* p_a;需要两次 malloc 和两次 free

✅ 推荐:优先使用柔性数组,更安全、高效!


C/C++程序内存区域划分 —— 内存世界的“地图” 🗺️

🌍 五大区域

区域特点存放内容
栈区 (stack)自动管理,高效局部变量、函数参数、返回地址
堆区 (heap)手动管理,灵活malloc/new 申请的内存
数据段 (静态区)程序运行期间存在全局变量、static 变量
代码段只读函数代码、字符串常量
常量区只读const 变量、字符串字面量

🧠 内存布局图解

高地址
+------------------+
|     栈区         | ← 局部变量,向下增长
+------------------+
|                  |
|     堆区         | ← malloc,向上增长
|                  |
+------------------+
|    全局/静态区   |static, 全局变量
+------------------+
|    常量区        |"hello", const
+------------------+
|    代码段        | ← 函数体
+------------------+
低地址

✅ 理解内存布局,是成为高级C程序员的必经之路!


🎯 总结:动态内存“三要三不要”

要 ✅不要 ❌
要检查 malloc 返回值不要对 NULL 解引用
free 后置 NULL不要释放非动态内存
要成对使用 malloc/free不要忘记释放(内存泄漏)

🎯 恭喜你!
你已经掌握了C语言中最强大也最危险的工具——动态内存管理!
记住:能力越大,责任越大!

http://www.dtcms.com/a/500492.html

相关文章:

  • 将 XMind 测试用例转换为 CSV 文件导入测试管理平台
  • 网站开发用什么语言写免费网站域名注册个人
  • ORB_SLAM2原理及代码解析:Viewer 线程——Viewer::Run()
  • FullCalendar:现代Web应用中的专业日历解决方案
  • 商城网站设计图哈尔滨网站建设贴吧
  • Windows安装MongoDB保姆级教程(图文详解)
  • 生活中花钱请人做网站昆明公司网站制作
  • 东莞专业网站推广平台网站功能价格表
  • 博客网站的建设流程邯郸网站建设网页设计网络推广
  • wordpress 发布站重庆响应式网站建设
  • 建网站联系电话上海公司注册核名查询
  • 制作企业网站价格网页版梦幻西游大闹天宫八卦炉
  • 《小白学随机过程》第一章:随机过程——定义和形式
  • UPPAAL学习
  • 嘉定网站设计制作价格宣传片拍摄合同范本
  • 忻州网站建设培训网站备案填写电话号码
  • 阿里云服务器 放多个网站h5移动端网站模板下载
  • 老年衰弱与气虚体质
  • 企业微信聚合应用系统,ipad协议接口
  • namecheap建站wordpress.jsp网站开发技术
  • 淘宝客绑定网站备案号做网站对于不同的分辨率
  • Python基础_03_函数
  • 自由学习记录(107)
  • 宝山网站制作杭州家装设计公司排名榜
  • 购物网站开发步骤视频演示网页制作难学吗
  • 好看的网站 你知道的2021中国北京出啥大事了
  • 从零构建生产级日志分析系统:Flask + Docker + Nginx 完整实战
  • 济南市建设局网站查房产信息鞍山人才网站
  • 网站流量被用完了wordpress页面创建失败
  • 企业seo整站优化方案石家庄网站推广方案