初识C语言14.动态内存管理 - 实践

news/2026/1/25 20:58:35/文章来源:https://www.cnblogs.com/gccbuaa/p/19530470

目录

前言:

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

二、动态内存的核心函数:malloc与free

2.1malloc:申请堆内存

2.2 free:释放堆内存

三、进阶函数:calloc与realloc

3.1 calloc:申请并初始化内存

3.2 realloc:调整已分配内存的大小

四、常见的动态内存错误(必避坑)

4.1 对NULL指针的解引用

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

4.3 使用free释放非堆内存

4.4 对同一块内存多次释放

4.5 动态内存忘记释放(内存泄漏)

五、动态内存的实战:柔性数组

5.1 柔性数组的特点

5.2 柔性数组的使用

六、C/C++程序的内存区域划分

总结:


前言:

在C语言编程中,内存管理是核心能力之一。静态内存分配(如数组)虽简单,但无法满足“运行时灵活调整内存大小”的需求——动态内存管理正是为此而生。本文将从基础原理、关键函数、常见错误到实战技巧,系统解析C语言动态内存管理的全貌。

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

静态内存(如局部变量、全局数组)的缺陷很明显:

int val = 20;//在栈空间上开辟四个字节
char arr[10] = {0};//在栈空间上开辟10个字节的连续空间
int main()
{
int a=20;
int aee[10]={0};
}

1. 大小固定:数组定义时必须指定长度,无法根据运行时数据量调整;
2. 作用域限制:局部变量存于栈区,函数结束后内存释放,无法传递给外部;
3. 空间浪费:若为“最坏情况”分配大数组,实际数据量小时会浪费内存。

动态内存分配(通过 malloc / calloc 等函数向堆区申请内存)则解决了这些问题:

- 内存大小可在运行时决定;
- 堆区内存由程序员手动管理,生命周期不受函数作用域限制;
- 按需申请,避免空间浪费。

二、动态内存的核心函数:malloc与free


C标准库通过 <stdlib.h> 提供了动态内存操作的核心函数,其中 malloc 和 free 是最基础的“申请-释放”组合。

2.1malloc:申请堆内存

void* malloc (size_t size);

- 功能:向系统申请连续的、大小为 size 字节的堆内存;
- 返回值:成功则返回指向该内存的指针,失败则返回 NULL (需检查!);
- 注意: malloc 不会初始化内存,分配的空间是“脏数据”(随机值)。

示例:申请能存5个 int 的堆内存

#include 
#include 
int main() {// 申请5*4=20字节(假设int占4字节)int* arr = (int*)malloc(5 * sizeof(int));if (arr == NULL) { // 必须检查是否申请成功perror("malloc failed");return 1;}// 使用内存for (int i = 0; i < 5; i++) {arr[i] = i + 1;printf("%d ", arr[i]); // 输出:1 2 3 4 5}// 后续需释放内存(见2.2)return 0;
}

注意程序结束后会自动释放内存,但还是要学会自己释放。

2.2 free:释放堆内存

void free(void* ptr);

- 功能:将 ptr 指向的堆内存归还给系统,避免内存泄漏;
- 注意:
1.  ptr 必须是 malloc / calloc / realloc 返回的堆指针;
2. 释放后需将 ptr 置为 NULL ,避免“野指针”(指向已释放内存的指针);
3. 不能重复释放同一块内存,也不能释放非堆指针(如栈变量地址)。

示例:释放上述 arr 的内存

free(arr);
arr = NULL; // 避免野指针(下文会讲)

三、进阶函数:calloc与realloc

除了 malloc ,C还提供了 calloc (带初始化)和 realloc (调整内存大小),进一步增强动态内存的灵活性。


3.1 calloc:申请并初始化内存

void* calloc(size_t num, size_t size);

- 功能:申请 num 个“大小为 size 字节”的连续堆内存,并将所有字节初始化为 0 ;
- 对比 malloc : calloc 等价于 malloc(num*size)  + 内存清零;
- 适用场景:需要“干净内存”的场景(如数组初始化为0)。
示例:用 calloc 申请5个 int 的内存

int* arr = (int*)calloc(5, sizeof(int));
if (arr == NULL) {perror("calloc failed");return 1;
}
// 此时arr[0]~arr[4]均为0

3.2 realloc:调整已分配内存的大小

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

- 功能:将 ptr 指向的堆内存调整为 new_size 字节;
- 行为规则:
1. 若原内存后有足够空间,直接在原地址后扩展,返回原指针;
2. 若原内存后空间不足,系统会分配新的连续内存,并将原数据拷贝到新地址,原内存自动释放;
3. 若 ptr 为 NULL , realloc 等价于 malloc(new_size) ;
4. 若 new_size 为0, realloc 等价于 free(ptr) (部分编译器可能返回 NULL )。

示例:将 arr 的内存从5个 int 扩展到10个

int* new_arr = (int*)realloc(arr, 10 * sizeof(int));
if (new_arr == NULL) {perror("realloc failed");free(arr); // 原内存未被释放,需手动释放return 1;
}
arr = new_arr; // 更新指针
// 此时arr可存10个int,前5个数据保留

四、常见的动态内存错误(必避坑)

动态内存管理是C语言中“bug高发区”,以下是最常见的错误类型:

4.1 对NULL指针的解引用

malloc / calloc / realloc 失败时返回 NULL ,若直接解引用会导致程序崩溃:

// 错误示例:未检查NULL
int* arr = (int*)malloc(100);
arr[0] = 10; // 若malloc失败,arr是NULL,解引用会崩溃

解决:必须在使用指针前检查是否为 NULL 。

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

堆内存的边界由程序员自己维护,越界访问会破坏其他内存的数据(“内存污染”):

int* arr = (int*)malloc(5 * sizeof(int));
for (int i = 0; i < 10; i++) {arr[i] = i; // i>=5时越界,可能破坏其他堆内存
}

解决:严格控制访问范围,避免下标超出申请的大小。

4.3 使用free释放非堆内存

free 只能释放堆内存,若释放栈变量或全局变量的地址,会导致“未定义行为”(程序崩溃、异常等):

// 错误示例:释放栈变量
int a = 10;
free(&a);

4.4 对同一块内存多次释放

重复释放同一块堆内存会导致程序崩溃:

int* arr = (int*)malloc(10);
free(arr);
free(arr); // 重复释放,崩溃

解决:释放后将指针置为 NULL ( free(NULL) 是安全的)。

4.5 动态内存忘记释放(内存泄漏)

若堆内存使用后未 free ,程序运行期间会持续占用内存,长期运行会导致内存耗尽:

// 错误示例:内存泄漏
void func() {int* arr = (int*)malloc(100);// 使用arr,但未free
}
// 函数结束后,arr指针被销毁,但堆内存未释放

解决:遵循“谁申请,谁释放”的原则,确保每一块堆内存都有对应的 free 。

五、动态内存的实战:柔性数组

C99标准引入了“柔性数组”(Flexible Array Member),是一种在结构体中定义“可变长数组”的技巧,常用于实现“动态大小的结构体”。

5.1 柔性数组的特点

- 定义:结构体的最后一个成员是“未知大小的数组”( [] );
- 内存:柔性数组不占用结构体的大小,实际内存需通过 malloc 申请;
- 优势:将结构体和动态数组存放在同一块连续堆内存中,方便管理和释放。

typedef struct st_type
{
int i;
int a[0];//柔性数组成员
}type_a;
int main()
{
printf("%d\n", sizeof(type_a));//输出的是4
return 0;
}

5.2 柔性数组的使用

示例:定义一个带柔性数组的结构体,存储“长度+数据”:

#include 
#include 
// 带柔性数组的结构体
typedef struct {int len;int data[]; // 柔性数组,不占结构体大小
} FlexArray;
int main() {// 申请内存:结构体大小 + 5个int的大小FlexArray* fa = (FlexArray*)malloc(sizeof(FlexArray) + 5 * sizeof(int));fa->len = 5; // 设置数组长度// 初始化柔性数组for (int i = 0; i < fa->len; i++) {fa->data[i] = i + 1;}// 输出:1 2 3 4 5for (int i = 0; i < fa->len; i++) {printf("%d ", fa->data[i]);}free(fa); // 一次释放即可(结构体+数组在同一块内存)return 0;
}

六、C/C++程序的内存区域划分

1. 栈区:存储局部变量、函数参数,由系统自动分配/释放,空间小(通常几MB);
2. 堆区:存储动态分配的内存,由程序员手动管理,空间大(可达GB级别);
3. 全局/静态区:存储全局变量、静态变量( static ),程序启动时分配,结束时释放;
4. 代码区:存储程序的二进制指令,只读。

总结:

动态内存管理是C语言的“双刃剑”:它赋予了程序灵活的内存控制能力,但也要求程序员承担更多责任(手动申请、释放、避坑)。掌握 malloc / free / calloc / realloc 的用法,避开常见错误,结合柔性数组等技巧,才能写出高效、稳定的C程序。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1215760.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

增量微调优化在线更新

&#x1f493; 博客主页&#xff1a;借口的CSDN主页 ⏩ 文章专栏&#xff1a;《热点资讯》 智能优化&#xff1a;增量微调在线更新的效率革命与伦理挑战 目录 智能优化&#xff1a;增量微调在线更新的效率革命与伦理挑战 引言&#xff1a;动态AI的进化需求 现在时&#xff1a;成…

【Java 新手必看】比较运算符:从概念到实战,附可运行代码案例

比较运算符是 Java 中用于判断 “两个值 / 变量关系” 的核心运算符&#xff0c;也是实现程序逻辑判断&#xff08;如 if/else&#xff09;的基础。新手常混淆 “等于” 和 “赋值”、“等于” 和 “对象相等”&#xff0c;这篇笔记结合可直接运行的代码案例&#xff0c;从基础…

网络考试点下一步出错怎么办?试试这样做

场景&#xff1a;家中网络课学习完毕考试&#xff0c;遇到点下一步出错问题 解决方案&#xff1a;打开显示所有题号的面板&#xff0c;点击面板上的题号跳转 注&#xff1a;我这里题号面板是点击进度条打开的&#xff0c;各人根据实际情况找下

【保姆级教程】移动端部署本地知识库与大模型,小白也能轻松上手(建议收藏)

本文详细介绍了在移动端部署本地知识库与大模型的完整方案&#xff0c;包括轻量级模型选型&#xff08;如Phi-2、TinyLlama&#xff09;、推理引擎&#xff08;llama.cpp、MLC LLM&#xff09;和知识库构建方法&#xff08;向量数据库或关键词匹配&#xff09;。提供了Android部…

大模型学习宝典:收藏这份系统性技术框架,从零开始构建LLM

本文基于Stanford CS336课程&#xff0c;系统介绍大模型开发关键技术&#xff0c;涵盖BPE分词、网络结构设计、超参数选择、训练技巧、MoE架构、GPU优化、分布式训练及推理优化等核心内容。详细解析从LayerNorm到RMSNorm、RoPE位置编码、Flash Attention等实现方法&#xff0c;…

STM32F0实战:基于HAL库开发【2.1】

7.4.2 从待机模式唤醒 待机模式允许达到能耗最低,它基于Cortex-M0深度睡眠模式,电压调节器禁用,1.8V域关闭,PLL、HIS和HSE振荡器也关闭,SRAM和寄存器内容丢失。只有RTC寄存器、RTC备份寄存器和备用电路保持工作。可以使用HAL库中的HAL_PWR_EnterSTANDBYMode()函数进入待机…

大语言模型训练原理解析:ChatGPT背后的技术原理与应用价值

大语言模型训练分为三步&#xff1a;预训练阶段通过互联网数据训练基础预测模型&#xff1b;监督微调阶段通过问答数据让模型学会回答问题&#xff1b;强化学习阶段让模型自行探索最佳解法&#xff0c;产生思维链。大模型本质是统计学预测器&#xff0c;通过预测下一个token生成…

MCP与A2A深度解析:AI系统集成与智能体协作的未来之路

MCP与A2A是AI系统集成的两大关键协议。MCP解决AI安全调用工具与数据的问题&#xff0c;提供资源、工具和提示模板三大能力&#xff1b;A2A则规范智能体间的协作&#xff0c;通过任务、消息和产物等组件实现高效接力。它们共同构成AI系统的分层规范&#xff0c;将开发范式从&quo…

【升级版本】基于多目标粒子群算法的微电网优化调度【风光、储能、柴油、燃气、电网交互】附Matlab代码

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;擅长数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。 &#x1f34e; 往期回顾关注个人主页&#xff1a;Matlab科研工作室 &#x1f34a;个人信条&#xff1a;格物致知,完整Matlab代码及仿真…

【实时无功-有功控制器的动态性能】【带有电流控制的两级电压源变流器(VSC)】采用αβ阿尔法-贝塔转换进行电流反馈的实时无功功率控制器附Simulink仿真

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;擅长数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。 &#x1f34e; 往期回顾关注个人主页&#xff1a;Matlab科研工作室 &#x1f34a;个人信条&#xff1a;格物致知,完整Matlab代码及仿真…

【2026最新】大模型面试全攻略:23家科技公司面试经验+高频考点总结,助你轻松上岸

本文详细记录了作者对23家大模型相关公司的面试经历&#xff0c;包括智元机器人、面壁科技、Minimax、阿里夸克、蚂蚁等公司的面试流程与结果。作者总结了大模型面试的高频考点&#xff0c;如多头注意力机制、框架并行方式、BERT/GPT模型细节、大模型训练技巧等&#xff0c;并分…

【使用Copulas对金融时间序列进行波动率估计与预测,涵盖GARCH、EWMA和EqWMA等模型】基于件风险价值(CVaR)、极值理论(EVT)、风险因子及蒙特卡洛模拟进行市场风险管理附Matlab

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;擅长数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。 &#x1f34e; 往期回顾关注个人主页&#xff1a;Matlab科研工作室 &#x1f34a;个人信条&#xff1a;格物致知,完整Matlab代码及仿真…

个人AI产业定义、产业架构与发展趋势白皮书|附60页PDF文件下载

本报告以用户为中心&#xff0c;立足于产业与生态&#xff0c;系统阐述个人AI时代来临的产业逻辑与必然趋势&#xff0c;厘清个人AI的基础架构与核心特征&#xff0c;剖析其对产业链、价值链及竞争格局带来的结构性变革。报告亦将对个人AI主导的未来生态进行前瞻展望&#xff0…

企业AI开发与技术实践白皮书2025|附36页PDF文件下载

白皮书探讨了在GenAI时代&#xff0c;企业如何利用大模型技术实现智能化转型。白皮书指出&#xff0c;大模型能力的不断提升为企业带来了无限想象力&#xff0c;但也面临着战略规划、数据治理、算力资源、场景适配等方面的挑战。企业需要构建覆盖顶层设计、技术选型、数据治理、…

【数据驱动】【航空航天结构的高效损伤检测技术】一种数据驱动的结构健康监测(SHM)方法,用于进行原位评估结构健康状态,即损伤位置和程度,在其中利用了选定位置的引导式兰姆波响应附Matlab代码

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;擅长数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。&#x1f34e; 往期回顾关注个人主页&#xff1a;Matlab科研工作室&#x1f34a;个人信条&#xff1a;格物致知,完整Matlab代码及仿真咨询…

【柔性作业车间调度问题FJSP】基于鹅优化算法(GOOSE Algorithm,GOOSE)求解柔性作业车间调度问题(FJSP)研究附Matlab代码

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;擅长数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。 &#x1f34e; 往期回顾关注个人主页&#xff1a;Matlab科研工作室 &#x1f34a;个人信条&#xff1a;格物致知,完整Matlab代码及仿真…

如何保证服务高可靠? - 实践

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

P1462 通往奥格瑞玛的道路

点击查看代码 #include<bits/stdc++.h> using namespace std;typedef long long LL; typedef pair<LL,int> PII; const int N=1e4+10,M=1e5+10;int h[N],ne[M],idx,e[M],w[M]; int n,m; LL b; LL f[N]; LL…

Codeforces Round 1073 Div.1 写题记录(编号 2190)

A 显然有序 Bob 赢,那么无序的情况就是选出一个不升子序列变成不降子序列,考虑一定有这么一个分界点,是的前面全部取 \(1\),后面全部取 \(0\),看一下合不合法即可,当然有一种更简单的构造方式是排完序后比对哪些…

动态系统思维:告别僵化内耗的破局指南

职场中总有这样的困惑&#xff1a;公司制定了标准化流程&#xff0c;却越执行越低效&#xff1b;团队追求“绝对有序”&#xff0c;反而失去创新活力&#xff1b;个人埋头重复固有工作&#xff0c;却在变化中逐渐被淘汰。我们总以为“稳定有序”是生存之道&#xff0c;却忽略了…