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

【C语言内存管理】第三章 堆内存管理

第三章 堆内存管理

在C语言中,堆内存管理是较为灵活的内存管理方式之一,它允许程序在运行时根据需求动态分配和释放内存,与静态分配相比更为灵活。下面我们对堆内存管理进行详细讲解。

1. 动态内存分配函数

在C语言中,有四个标准函数用于动态内存分配和释放:

  • malloc: 分配指定大小的字节,并返回指向该内存块的指针。
  • calloc: 分配包含给定数量的元素且每个元素大小为一个指定字节数的内存区域,并初始化为0。
  • realloc: 调整以前分配的内存块的大小。
  • free: 释放以前分配的内存块。
malloc函数
#include <stdlib.h>int* allocateArray(int size) {int* array = (int*)malloc(size * sizeof(int));if (array == NULL) {// 处理内存分配失败情况printf("Memory allocation failed.\n");exit(1);}return array;
}
  • malloc分配了一个新的内存块,并返回指向该内存块的指针。
  • 如果内存分配失败,malloc返回 NULL
calloc函数
#include <stdlib.h>int* initializeArray(int count) {int* array = (int*)calloc(count, sizeof(int));if (array == NULL) {// 处理内存分配失败情况printf("Memory allocation failed.\n");exit(1);}return array;
}
  • calloc函数分配内存并将其初始化为0。
  • 参数的第一个值是元素个数,第二个值是每个元素的大小。
realloc函数
#include <stdlib.h>int* resizeArray(int* array, int newSize) {int* newArray = (int*)realloc(array, newSize * sizeof(int));if (newArray == NULL) {// 处理内存分配失败情况printf("Memory reallocation failed.\n");free(array); // 避免内存泄漏exit(1);}return newArray;
}
  • realloc函数调整给定内存块的大小。
  • 如果新大小大于旧大小,realloc可能在新位置分配内存,并复制数据,如果新大小小于旧大小,数据被截断。
free函数
#include <stdlib.h>void deallocateArray(int* array) {free(array);
}
  • free函数释放之前由malloc, callocrealloc 分配的内存。
  • 释放后,指针不再指向有效内存区域。通常将指针设置为 NULL 避免悬空指针(即 Dangling Pointer)。
2. 静态内存与动态内存的对比
  • 静态内存分配

    • 确定性:内存大小在编译时确定。
    • 位置:使用全局变量或局部变量在栈上分配。
    • 管理:内存由编译器自动管理,不需要显式释放。
  • 动态内存分配

    • 灵活性:内存大小在运行时确定。
    • 位置:使用 malloc, calloc, realloc 在堆上分配。
    • 管理:需要显式调用 free 释放内存,防止内存泄漏。
3. 动态内存管理实践
内存分配与释放

以下示例展示如何动态分配和释放内存:

#include <stdlib.h>void example() {int* array = (int*) malloc(10 * sizeof(int)); // 动态分配内存 [1]if (!array) {// 处理内存分配失败}// 使用数组进行某些操作...free(array); // 释放内存 [2]
}
  1. 内存分配malloc 用于在堆上分配一片连续内存。程序员需要检查分配是否成功,避免使用 NULL 指针。
  2. 内存释放free 函数用于释放之前分配的内存,以防止内存泄漏。
内存泄漏的预防

内存泄漏是指程序无法再访问某块内存,但该内存未被释放,导致内存浪费。预防内存泄漏的要点包括:

  • 确保每块动态分配的内存都有对应的 free 调用。
  • 使用工具如 Valgrind 检测内存泄漏。

示例代码:

#include <stdlib.h>void memoryLeakExample() {int* leakyArray = (int*)malloc(10 * sizeof(int));// 忘记释放内存,造成内存泄漏 [3]
}void noMemoryLeakExample() {int* array = (int*)malloc(10 * sizeof(int));if (!array) return;// 使用数组进行某些操作...free(array); // 释放内存,避免泄漏 [4]
}
  1. 内存泄漏leakyArray 在函数结束时没有被释放,导致内存泄漏。
  2. 内存释放:调用 free 释放动态分配的内存,避免内存泄漏。
内存碎片化问题及优化

内存碎片化是指堆内存中由于频繁的分配和释放而产生很多小而分散的内存块。解决方案包括:

  • 使用内存池:提前分配一大块内存,并根据需求从内存池中取。
  • 合并空闲块:实现自己的内存管理器,将相邻的空闲内存块合并。

示例代码:

#include <stdlib.h>void memoryFragmentationSolution() {// 简化的内存池使用示例 [5]static char memoryPool[1024];static int poolIndex = 0;void* customMalloc(size_t size) {if (poolIndex + size > sizeof(memoryPool)) {return NULL; // 内存不足 [6]}void* ptr = &memoryPool[poolIndex];poolIndex += size;return ptr;}// 使用自定义的 malloc [7]int* array = (int*)customMalloc(10 * sizeof(int));if (array == NULL) {printf("Memory allocation failed.\n");} else {// 使用数组进行某些操作...}
}
  1. 内存池初始化:内存池预先分配一大块内存,并维护一个索引来跟踪已使用的内存。
  2. 内存分配检查:检查内存池是否有足够空间。如果不足返回 NULL。
  3. 分配内存并使用:从内存池中分配内存并使用。这种方法减少了堆碎片化。

通过合理使用动态内存分配和释放,程序员可以有效管理内存,保持程序稳定可靠。

总结,堆内存管理在C语言中占有重要地位,掌握好动态内存分配函数的使用方法及其管理策略,对于编写高效、稳定的程序具有重要意义。通过避免内存泄漏和优化内存碎片化问题,可以大大提升程序的可靠性和性能。

相关文章:

  • 《ToDesk 云电脑、易腾云、青椒云移动端体验实测:让手机秒变超级电脑》
  • ARM 服务器上安装 OpenEuler (欧拉)
  • 银河麒麟桌面操作系统V10登录闪退问题解决
  • python并发编程实战
  • R 语言 | 取数据框一列子集时,如何保持数据框结构?drop=F
  • 【Python】YOLO牛刀小试:快速实现视频物体检测
  • Windows批处理文件编写指南
  • GEE数据集:全球城市热岛强度(UHII)数据集(更新)
  • 一起了解AI的发展历程和AGI的未来展望
  • 完成UI界面的绘制
  • Unity3D 创建一个人物,实现人物的移动
  • springboot+大数据基于数据挖掘的招聘信息可视化大屏系统【内含源码+文档+部署教程】
  • 大数据-155 Apache Druid 架构与原理详解 数据存储 索引服务 压缩机制
  • RocksDB Compaction的常见问题
  • uni-app进行微信小程序开发,快速上手
  • @jsonView过滤属性
  • [译] 理解数组在 PHP 内部的实现(给PHP开发者的PHP源码-第四部分)
  • gitlab-ci配置详解(一)
  • GitUp, 你不可错过的秀外慧中的git工具
  • HTTP那些事
  • js操作时间(持续更新)
  • js继承的实现方法
  • Python进阶细节
  • React-flux杂记
  • React的组件模式
  • Spring Boot MyBatis配置多种数据库
  • 成为一名优秀的Developer的书单
  • 高性能JavaScript阅读简记(三)
  • 机器学习中为什么要做归一化normalization
  • 批量截取pdf文件
  • 前端存储 - localStorage
  • 手机app有了短信验证码还有没必要有图片验证码?
  • 长三角G60科创走廊智能驾驶产业联盟揭牌成立,近80家企业助力智能驾驶行业发展 ...
  • 正则表达式-基础知识Review
  • ​3ds Max插件CG MAGIC图形板块为您提升线条效率!
  • (2024)docker-compose实战 (8)部署LAMP项目(最终版)
  • (八)Flask之app.route装饰器函数的参数
  • (六)激光线扫描-三维重建
  • (论文阅读30/100)Convolutional Pose Machines
  • .bat批处理出现中文乱码的情况
  • .h头文件 .lib动态链接库文件 .dll 动态链接库
  • .net core docker部署教程和细节问题
  • .NET Core SkiaSharp 替代 System.Drawing.Common 的一些用法
  • .net core 的缓存方案
  • .net 生成二级域名
  • .net 重复调用webservice_Java RMI 远程调用详解,优劣势说明
  • .NET/C# 中你可以在代码中写多个 Main 函数,然后按需要随时切换
  • .Net中的设计模式——Factory Method模式
  • .pyc文件是什么?
  • /etc/X11/xorg.conf 文件被误改后进不了图形化界面
  • /usr/lib/mysql/plugin权限_给数据库增加密码策略遇到的权限问题
  • @EnableAsync和@Async开始异步任务支持
  • @WebService和@WebMethod注解的用法
  • [ vulhub漏洞复现篇 ] Grafana任意文件读取漏洞CVE-2021-43798
  • [2019.3.5]BZOJ1934 [Shoi2007]Vote 善意的投票