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

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

目录

前言:

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

二、动态内存的核心函数: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 <stdlib.h>
#include <stdio.h>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 <stdlib.h>
#include <stdio.h>// 带柔性数组的结构体
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.dtcms.com/a/523735.html

相关文章:

  • ks2e做网站高端品牌设计
  • 华为od-22届考研-C++面经
  • Win10 系统构建仿真 NVIDIA Jetson Orin Nano 环境部署 YOLOv8 模型
  • 英文网站开发付费下插件wordpress
  • 【面板数据】汽车之家及懂车帝汽车配置信息数据集(1999-2025.4)
  • Slotted Aloha
  • 「赤兔」Chitu 框架深度解读(六):剖析 Attention 机制后端实
  • 嵌入式开发中为啥常用do{}while(0)进行宏定义
  • 第六部分:VTK进阶(第172章 vtk-m加速器管线)
  • 矽塔 SA8207 36V输入耐压 高精度可调过流保护与集成智能故障管理 过压过流保护芯片
  • 关键词优化公司网站怎么做网站后台界面
  • 从「Bug 制造机」到「问题解决者」的进化之路
  • 华为新一代鸿蒙操作系统实现与苹果互联
  • 常用 apt 命令及语法(Ubuntu)
  • 华为 AI,建造中的全景图
  • 第二十九篇:动态规划(一):基础与背包问题
  • 深度学习中的训练流程:从输入到权重更新的完整旅程
  • QT------QPainter::save() 和 QPainter::restore() 的使用方法和作用。
  • http trailer 与 http2
  • 有没有会计做兼职的网站wordpress获取文章
  • 中国人在国外做网站网站代理网站群建设 会议 主持
  • 在Ubuntu Linux安装brew 使用brew安装llama.cpp 运行文心Ernie大模型
  • 基于MATLAB/Simulink的风光储联合系统经M3C接入电网的低电压穿越仿真研究
  • CNCF Kepler与MCP:开启云原生绿色计算的人机协作新纪元
  • 昇腾NPU部署GPT-OSS-20B混合专家模型:从环境配置到性能优化的完整实践指南
  • java8中的‘+‘的使用注意事项
  • 德国莱茵金属公司使用Varjo XR-4创建虚拟现实培训解决方案
  • STM32的GPIOx_ODR,GPIOx_BSRR,GPIOx_BRR寄存器的区别与使用
  • 网站建设指南 菜鸟教程简历模板做的最好的是哪个网站
  • Prometheus + Alertmanager + 钉钉告警