首页 > 系统相关 >动态内存管理,如何实现内存的高效分配?良心制作,属实不易!

动态内存管理,如何实现内存的高效分配?良心制作,属实不易!

时间:2025-01-14 21:28:05浏览次数:3  
标签:malloc 属实 释放 内存 动态内存 空间 开辟 指针

目录

前引

与之相关的4个重要函数

malloc—申请所需内存

对开辟空间有效性的判断

free内存释放

calloc—申请所需内存

malloc与calloc的区别

realloc调整动态空间

realloc调整空间的2种情况

常见的动态内存错误(重点)

动态开辟必须流程

内存大概分配图

几个经典的笔试题

 

前引

静态与动态内存:

1:相较于静态内存,动态内存可以按需分配,取多少用多少,避免空间浪费,比如(静态的创建变量,这种就随便开辟了一块空间,可能用不完):

2:突破栈空间限制,栈空间有限且容易溢出,而动态空间利用堆空间进行存储

3:延长变量生命周期,内部变量在函数返回后任然有效,实现局部变量的全局化使用

后面还给大家整理了常见的错误点,避免了这些坑,动态开辟直接满分!

看到这里,有没有想一睹动态内存真容的冲动!我们一起来了解了解吧!

与之相关的4个重要函数

malloc (申请所需内存)                    calloc (申请所需内存)                                                                                        realloc  (调整内存空间)                  free (释放开辟的内存空间)             

  头文件均是:<stdlib.h>

动态分配的重点是这四个函数,我们来一一讲解,千万认真记笔记!

在前面我们先只了解这4个函数的参数、返回值、作用,后面会整理各种做法的原因!

malloc—申请所需内存

学习库函数,我们需要了解它的参数,以及返回值,可以登入下面的网站,搜素想了解的库函数


https://legacy.cplusplus.com/

我们可以看到,它只需要一个参数:字节大小 

返回值:地址             

 比如:

创建了一个 int 类型的指针 p ,指针指向 malloc 开辟空间的起始位置,空间的大小是16个字节

 注意:

            1:开辟的空间需要注明类型,为了之后指针的移动方式来存储数据

            2:malloc 后面的参数可以写成表达式,但是要保证表达式的结果是多少多少字节

对开辟空间有效性的判断

下面我们来看一个例子:

我们可以看到,虽然确实打印成功了,但是编译器提醒了你,如果开辟失败,那么P就是空指针,也就不符合语法了,所以我们做一下改进这个很重要,如果不加判断,那么可能会发生非法访问 ,因为P是空指针,强制移动P可能指向其余不属于自己的空间

判断的2种方式:(自选一种即可)

第一种,需要头文件:<string.h>以及<errno.h>

 

第二种:

 

最后提醒三遍:动态开辟空间的,都需要做判断,防止非法访问

                         动态开辟空间的,都需要做判断,防止非法访问

                         动态开辟空间的,都需要做判断,防止非法访问 

free内存释放

 我们在前引里面说过了,动态开辟可以延长变量的生命周期,那么我们开辟了一块动态空间,一直开辟,一直开辟下去,是不是有效空间会越来越少,所以我们需要再使用完后及时释放开辟的空间

那么我们如何释放开辟的动态空间?

方式:free(指针)

           指针置空

比如:

注意:先释放后置空,这两步缺一不可! 

calloc—申请所需内存

这个函数的申请空间其实和malloc差不多,至于差距我们后面会细说!先了解这个函数的使用就行了!

它需要2个参数:元素个数   元素类型的大小

怎么样?calloc跟malloc的参数其实意思一样,只是calloc的参数把malloc的参数分解成了2个

比如:

malloccalloc这两个函数都是开辟动态内存,那么区别是什么?

malloc与calloc的区别

malloc:开辟空间后,没有初始化,直接返回空间位置的起始地址

calloc:开辟空间后,进行初始化(默认全部初始化为0),然后返回空间位置的起始地址

我们也可以从内存上面看到它们两个的区别:

realloc调整动态空间

顾名思义 ,realloc就是用来调整的,我们再开辟空间时,因为malloccalloc开辟的空间是连续的,那么我们如果所需要的内存空间过大,后面不够了咋办?这时候就需要realloc来进行调整

2个参数:调整对象   总共需要的空间大小

返回值:地址

注意:返回的地址再用指针接收时,不能用原来的指针,接受后还是需要判断是否开辟成功,再进行指针指向的托付(后面会在易错点里面说,放心!

比如:(经过realloc调整的空间可以继续使用,因此注意下面图中的 i 的取值)

 下面我们来对realloc进行讨论,相信看完,绝对是炉火纯青了!

realloc调整空间的2种情况

第一种情况:

当调整空间足够大时,那么realloc会在原空间后面连续开辟剩余的空间

第二种情况:

当调整空间不足以支撑剩余的所需空间时,会找其它地方重新开辟总大小的连续空间,并把旧看空间的数据拷贝过来,再将旧空间自动释放,同时返回新空间的起始地址,继续使用就行了

 比如:(对比这2幅图,我们用realloc调整后可以继续使用,指针指向的位置还是上一个空间结束的位置)

 注意点:

如果realloc调整的对象为空指针NULL,就相当于随便找位置开辟了一块指定大小的空间,类似malloc函数作用,比如:

常见的动态内存错误(重点)

1:对NULL指针的解引用操作(未判断指针的有效性)

只要开辟了动态空间,就需要判断指针的有效性,如果开辟失败,那么这个指针就是空指针,再访问就是非法访问了

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

我们开辟的是16字节大小的空间,但是使用时却超出了16字节的范围,这就发生了越界访问

3:对非动态开辟内存使用free释放 

我们创建的变量是在栈区的,但是我们却对它进行了内存释放,内存释放释放的是堆区,两者毫不相干 

4:使用free释放开辟的动态内存的一部分(就是指针指向发生了改变)

free释放内存的原则:指针指向必须是开辟动态空间的的起始位置 

下面几种形式就需要特别注意,因为指针发生改变:

这种直接对p指向改变的需要先记录p的起始位置,空间使用完后,让p回到起始位置,再释放空间 

比如:

 5:对同一块动态空间多次释放

那么我们可以怎么改呢?直接先置空,这样就没有问题了,因为释放置空后,这个空间就不属于它了,再释放也就等于释放一个不存在的空间,没有影响,比如:

 

 6:动态内存的忘记释放

我们知道动态内存可以延长变量的生命周期,如果开辟了,不释放,那么它就一直在堆区存在,这样堆区内存越来越少,程序就崩了! 

比如下面这个代码:

内存会一直一直开辟下去,最终会崩溃!我们一定要记得及时释放,用完就释放 !

                   

动态开辟必须流程

使用malloc或者calloc或者realloc开辟动态空间后,都要判断空间有效性,即指针是否是空指针

最后记得进行及时释放 

内存大概分配图

几个经典的笔试题

                                                                       第一题 

 

大家可以先看上面的原题,找找错误,再看解析:

首先,strp没有建立真正联系,因为是传值操作

其次,GetMemory函数接收的参数应该是二级指针,应该是对*p开辟内存,因为*p才是一个指针

最后,没有对*p进行判断,也没有进行空间释放,   下面是改正:

                                                                         第二题 

 错误点很明显:就是GetMemory中有个数组,str需要指向这个数组,但是这个数组是临时变量,函数调用完销毁后,主要是解决这个数组被销毁的问题

改进:(我这里忘记释放内存了!不好意思啊,大家记得加上!)

                                                                          第三题

 

这个问题不大,缺一个指针有效性判断跟空间释放,下面是改进:

标签:malloc,属实,释放,内存,动态内存,空间,开辟,指针
From: https://blog.csdn.net/Dovis5884/article/details/145083370

相关文章

  • 【Linux性能】Linux 下利用 Valgrind 进行内存调试
    一、概述Valgrind是一个开源的内存调试和性能分析工具,用于帮助开发者找出程序中的内存错误,如内存泄漏、使用未初始化的内存、非法内存访问等问题。它在Linux平台上广泛使用,并且支持下多种处理器架构。二、Valgrind的使用2.1基本格式valgrind--tool=memcheck-–gen-supp......
  • Java内存与缓存
    Java内存管理和缓存机制是构建高性能应用程序的关键要素。它们之间既有联系又有区别,理解这两者对于优化Java应用至关重要。Java内存模型Java内存模型(JMM)定义了线程如何以及何时可以看到其他线程修改过的共享变量的值,并且规定了所有线程在读取或写入共享变量时必须遵循的一......
  • 高性能、零内存分配的Go日志库--Zerolog
    简介Zerolog是一个高性能、零内存分配的Go日志库。它为不需要垃圾回收的延迟敏感型应用程序提供结构化日志记录功能。您可以以完全零分配的方式使用,这样在初始化记录器对象后,堆上不会再分配其他对象,从而防止触发垃圾回收。Zerolog包提供了一个专用于JSON输出的快速而简......
  • 《 C++ 点滴漫谈: 二十 》内存的权杖:C++ new 和 delete 的致胜之道
    摘要本文全面探讨了C++中的new和delete关键字及其在动态内存管理中的核心作用。从基本概念到底层实现,本文详细分析了其工作机制,并揭示了动态内存管理中的常见问题与陷阱,如内存泄漏和空悬指针。为解决这些问题,现代C++提供了智能指针和STL容器等高效替代方案,同时本......
  • 动态内存管理
    本章重点1.为什么存在动态内存分配2.动态内存函数的介绍3.mallocfreecallocrealloc4.常见的动态内存错误一.为什么存在动态内存分配二.动态内存函数的介绍#define_CRT_SECURE_NO_WARNINGS#include<stdio.h>#include<stdlib.h>#include<errno.h>#inc......
  • Python内存优化全攻略:深入理解对象池与__slots__的应用
    《PythonOpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门!解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界在Python开发过程中,内存管理是提升应用性能的关键因素之一。随着应用规模的扩大,内存占用问题日益凸显,尤其是在处理大量对象时。本文将深入探讨......
  • 用DevEco Studio性能分析工具 高效解决鸿蒙原生应用内存问题
    在鸿蒙原生应用开发过程中,可能由于种种原因导致应用内存未被正常地使用或者归还至操作系统,从而引发内存异常占用、内存泄漏等问题,最终导致应用卡顿甚至崩溃,严重影响用户体验。为了帮助鸿蒙应用开发者高效定位并解决内存问题、提升应用稳定性与体验,华为在DevEcoStudio上提供了专属......
  • freertos的基础(二)内存管理:堆和栈
    1. 堆(Heap)定义堆是FreeRTOS中用于动态内存分配的内存区域。FreeRTOS提供了多种堆管理方案(如heap_1、heap_2、heap_4等),开发者可以根据需求选择合适的内存管理策略。作用用于动态分配内存,例如创建任务、队列、信号量等内核对象时,从堆中分配内存。堆的大小由开发者......
  • [微服务]redis内存回收原理
    过期KEY处理Redis提供了expire命令,给key设置TTL(存活时间)可以发现,当key的TTL到期以后,再次访问name返回的是nil,说明这个key已经不存在了,对应的内存也得到释放从而起到内存回收的目的。这里有两个问题需要我们思考:Redis是如何知道一个key是否过期呢?Redis的本身是键值......
  • 数据在内存的存储
    数据类型介绍前面我们已经学习了基本的内置类型:(后边跟的是字节)char    //字符数据类型  1字节 打印%c      short   //短整型    2字节  打印%hdint    //整形    4字节  打印%dlong ......