动态内存管理的了解及使用

news2025/4/25 4:31:01

目录

1.什么是动态内存?

2.为什么要使用动态内存分配空间?

3.动态内存开辟函数malloc,calloc,realloc

3.1 malloc

3.2 calloc 

3.3 realloc

3.4 头文件包含

4.动态内存释放函数free

5.动态内存的基本常见错误

        5.1 对NULL的解引用操作

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

5.3 误对非动态内存进行释放

5.4 使⽤free释放⼀块动态开辟内存的⼀部分

5.5 动态开辟内存忘记释放(内存泄漏)

6.c/c++中的内存区域划分

1.什么是动态内存?

        从内存区域上来看,一般的静态变量,比如

int a;
char c;
int arr[10]={1,2,3,4,5};
char arr2[10]="abcde";
int *p1;
char *p2;
double d;

        诸如此类都是在栈空间分配的,定义是不会变的,固定死的,使用完后由系统自动释放空间。而动态内存,是在堆区开辟的,由用户开辟,用户释放,系统不会主动进行释放操作。可以根据实际需求对内存分配进行扩大。

2.为什么要使用动态内存分配空间?

        在上述代码中,都是静态内存,有那么几个缺点:

  • 无论是变量,整形数组,字符串数组亦或是指针,内存大小都是固定的,一旦确定不可更改。
  • 数组在申明的时候必须要确定长度,数组空间确定了也不可以改变。

        那么在后续使用中,这种静态内存分配不可能满足所有需求,很多程序在运行后会越来越大,我们也无法准确预知程序所需要的内存空间到底是多少,所以需要更加灵活的方式来定义内存空间,程序就不会崩溃。所以可以由用户手动开辟,手动释放的方式——动态内存分配诞生了。

3.动态内存开辟函数malloc,calloc,realloc

        C语言库中提供了几种内存开辟函数,malloc,calloc和realloc,这些都有不同的功能。一一介绍

3.1 malloc

        malloc在cplusplus中是这样的,其实动态内存函数在之前的通讯录优化扩容部分是有提及并且使用它去开辟过数组空间的。它会申请一块连续的空间,并且返回一个指向内存块开头的指针,内存块中数据是未初始化的。

        如果开辟失败,则会返回一个NULL的指针,所以要对返回值进行检查。

        由于返回的类型是void*,所以malloc的时候要强制转化。

malloc

举个例子:

int main()
{
	//申请空间
	int*p =(int*)malloc(60);
	if (p==NULL)
	{
		perror("malloc");
		return 1;
	}

	int arr[] = {0};
	int i = 0;
	for (int i = 0; i < 10; i++)
	{
		*(p + i) = i + 1;
	}
	for (i = 0; i < 10; i++)
	{
		printf("%d ", *(p + i));
	}
	free(p);
	p = NULL;
	return 0;
}

指针p的变化:可以看出是随机的(调试界面)

3.2 calloc 

        calloc是C语言提供的另外一种动态内存开辟的方法,它和malloc不同在于它在开辟之初能直接初始化内存空间为0。开辟的是num个大小为size元素

calloc

例子:

int main()
{
	int* p = (int*)calloc(10,sizeof(int));
	if (p==NULL)
	{
		printf("%s\n",strerror(errno));
		return 1;
	}
	int i = 0;
	/*for ( i = 0; i < 10; i++)
	{
		*(p + i) = i;
	}*/
	for (i = 0; i < 10; i++)
	{
		printf("%d ", p[i]);
	}
	free(p);
	p = NULL;
	return 0;
}

 调试界面的变化:可以看出p是被初始化为0了。

不去对指针进行任何赋值操作,输出结果是: 

赋值后是:

3.3 realloc

         realloc也是一个动态开辟的方法,不过它相较于malloc和realloc更为灵活。来看看它的属性

realloc

        这个单词的前缀是re,而在英语单词中re的意思是重新,再次的意思。所以realloc是重新申请一块内存块。有时会我们发现申请的空间太小了,有时候我们⼜会觉得申请的空间过大了,那为了合理的使用,我们对内存的大小做灵活的调整。那 realloc 函数就可以做到对动态开辟内存大小的调整。

  • ptr是要调整改变的内存地址
  • size是对内存要重新分配多少空间。
  • 返回重新调整后的内存起始地址
  • 在调整内存的新大小之后,会根据情况不同有两种情况
  1. 如果原来空间够大,即是原地扩容,在原来空间中追加空间。
  2. 空间中所剩余的空间不够分配后续的空间,就会另外找一块足够大的空间,这个空间不一定和原空间地址连续。开辟后,会把原空间的数据拷贝到新空间,然后返回新内存块的起始位置。

举个例子:

int main()
{
	int* p = (int *)malloc(40);
	if (p==NULL)
	{
		perror("malloc");
		return 1;
	}
	int i = 0;
	for ( i = 0; i < 5; i++)
	{
		*(p + i) = i;
	}
	//不够了,进行扩容
	int* ptr = (int *)realloc(p,1000);
	if (ptr!=NULL)
	{
		p = ptr;
	}
	
	for ( i = 0; i < 10; i++)
	{
		printf("%d ", *(p + i));
	}
	free(p);
	p = NULL;
	return 0;
}

        调试界面中,一开始指针的地址p是这样的。

        再走下去到这一步,我们把realloc的地址给了指针ptr存放,可以看出p指向的地址和ptr指向的地址不同,再把ptr赋值给p,p一定会变成ptr的地址

 F10走

 (至于为什么原地空间不够大,是因为我扩容了1000个字节,如果只扩容40,100),我想应该是原地扩容,地址不会变动,可以自行尝试。

这是不同情况下realloc的空间示意图,是在通讯录第二篇有画过的。

3.4 头文件包含

        开辟函数malloc,calloc,realloc和释放函数free都是要包含头文件的,<stdlib.h>声明就可以了。

4.动态内存释放函数free

        free函数是专门为了上面三个扩容函数准备的。为什么要free呢,因为静态内存是系统自己在栈区开辟的,你去使用只需要负责用,用完系统自动回收销毁空间。而动态内存是手动开辟的。就可以不用太正式的语言,举个很通俗易懂的比方就是。

        就好比你去餐厅吃饭,这就是静态开辟,你所做的是点菜,吃饭,结账。剩下都是餐厅要做的事情。餐厅要洗菜,切菜,做菜,最后你吃完要打扫餐桌,洗碗,处理厨余垃圾。系统就是餐厅,静态内存就是你吃的菜。

        而你觉得餐厅的饭可能是预制菜,不和你胃口,不营养,你想自己在家做一桌好吃的菜。那就是动态开辟,一切都要自己动手,从买菜,做菜,吃饭到收拾。前面是malloc/calloc/realloc开辟,收拾就是你要free,不free就好比你不收拾,要么菜都发霉了,引来虫子,细菌,然后你被家长教育一顿。而在编程里,不主动free置NULL,系统就会崩溃,内存泄漏,野指针等等问题。

        free的使用很简单,就是把内存开辟所在的指针变量释放,置NULL就可以了。free只是释放了空间,要置空才行,如果不置空,指针变量依然指向那块内存,内存的地址是还在的。在以后的代码中若不小心继续调用指针变量,会出现不可预料的错误。

我特意去调试了一下看看区别:

        这是free后还没置NULL的内存地址,断点在50行,走一步,free了,内存地址不变(因为我是重新调试,地址是系统随机分配的,所以地址不可能和上面的保持一致),还在!只是内存所存放的内容成了随机值。那去访问一个随机值的内存空间,一次两次没事是侥幸,但肯定是有问题的,还是要严谨。

再走一步就是变成了0x00000000,且不可读取内存,说明是NULL。 

5.动态内存的基本常见错误

        5.1 对NULL的解引用操作

 void test() 
{ 
int *p = (int *)malloc(INT_MAX/4);    
 *p = 20;//如果p的值是NULL,就会有问题 
 free(p);
 }

        在编译器里,*p那一行会产生警告,说是取消对null的解引用操作,其实就是在前面没有对指针p进行检查是否为空,因为系统可能无法一次性分体量为(INT_MAX/4)的内存空间给指针p。很可能存在开辟失败,就成了空指针。而*p=20,是空指针的话显然是有问题的。

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

void test() {
 int i = 0;  
 int *p = (int *)malloc(10*sizeof(int));
 if(NULL == p)    
{       
  exit(EXIT_FAILURE);   
}    
 for(i=0; i<=10; i++)
 {      
*(p+i) = i;//当i是10的时候越界访问   
}  
   free(p); 
}

        内存只开辟了40个字节,而i到10的时候是0-10,有11个数据,所以是越界访问。 

5.3 误对非动态内存进行释放

void()
{
int a =10;
int *p =&a;
free(p);
}

        这对吗?肯定是错的,你都没有malloc内存函数开辟空间,就要free,想free什么? 

5.4 使⽤free释放⼀块动态开辟内存的⼀部分

 void test()
 { 
 int *p = (int *)malloc(100);    
 p++;    
 free(p);//p不再指向动态内存的起始位置 
 }

        这是我有时候会犯的错误,经常会报警告,后来DeepSeek一查才直到p++后会使指针不指向起始位置,产生局部释放 ,AI的辅助有时候使让人一下顿悟。

5.5 动态开辟内存忘记释放(内存泄漏)

void test() 
{  
 int *p = (int *)malloc(100); 
 if(NULL != p)     
    {        
 *p = 20;  
    } 
}
int main()
{
test();
return 0;
}

       没有在函数中进行动态开辟内存释放,引发内存泄露问题。函数调用结束后就销毁了,内部没有进行释放,也就释放不了了。

        忘记释放不会复用动态开辟的空间会造成内存泄漏。

        切记:动态开辟的空间⼀定要释放,并且正确释放。         

6.c/c++中的内存区域划分

        关于动态内存的开辟知识 ,在c语言只是涉及,而在后续学习数据结构中,会不断学习 。数据结构中malloc,calloc,realloc和free会不断,频繁见面的。所以还是需要熟练的掌握和使用。

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

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

相关文章

pom.xml配置(mybatisplus增删改查实现;PageInfo分页实现;JSONObject实现)

一、mybatisplus增删改查实现 pom.xml <!-- mybatis-plus --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.2</version></dependency>在mapper文件里…

【运维笔记】Navicat中删除mongo 某个时间之前的数据

【运维笔记】Navicat中删除mongo 某个时间之前的数据 一、场景与需求1.1、场景1.2、需求 二、解决方案三、实战3.1、【Navicat】使用sql语句 &#xff08;推荐&#xff09;Step 1&#xff1a;使用查询窗口 - 查询Step 2&#xff1a;确认第一步的数据是否是需要删除的数据Step 3…

BUUCTF逆向刷题笔记(1-12)

easyre、内涵的软件、xor、不一样的flag&#xff1a; buuctf reverse部分题解&#xff08;实时更新&#xff09;_reverse 题解-CSDN博客 请见小库里的blog。 reverse1 查壳发现没有&#xff0c;而且是64位 粗略改一下部分函数名&#xff0c;看看主要逻辑。 第一个for循环暂…

如何改变怂怂懦弱的气质(2)

你是否曾经因为害怕失败而逃避选择&#xff1f;是否因为不敢拒绝别人而让自己陷入困境&#xff1f;是否因为过于友善而被人轻视&#xff1f;如果你也曾为这些问题困扰&#xff0c;那么今天的博客就是为你准备的。我们将从行动、拒绝、自我认知、实力提升等多个角度&#xff0c;…

【DeepSeek】5分钟快速实现本地化部署教程

一、快捷部署 &#xff08;1&#xff09;下载ds大模型安装助手&#xff0c;下载后直接点击快速安装即可。 https://file-cdn-deepseek.fanqiesoft.cn/deepseek/deepseek_28348_st.exe &#xff08;2&#xff09;打开软件&#xff0c;点击立即激活 &#xff08;3&#xff09;选…

算法之二维装水问题

目录 1. 题目2. 解释3. 思路4. 代码5. 总结 1. 题目 给定一个数组arr&#xff0c;已知其中所有的值都是非负的&#xff0c;将这个数组看作一个容器&#xff0c;请返回容器能装多少水 比如&#xff0c;arr {3&#xff0c;1&#xff0c;2&#xff0c;5&#xff0c;2&#xff0c…

[C语言日寄] 字符串操作函数的使用及其拓展

【作者主页】siy2333 【专栏介绍】⌈c语言日寄⌋&#xff1a;这是一个专注于C语言刷题的专栏&#xff0c;精选题目&#xff0c;搭配详细题解、拓展算法。从基础语法到复杂算法&#xff0c;题目涉及的知识点全面覆盖&#xff0c;助力你系统提升。无论你是初学者&#xff0c;还是…

用Python的Pandas库解锁数据科学:从入门到实战

用Python的Pandas库解锁数据科学&#xff1a;从入门到实战 引言 Python的Pandas库&#xff08;名称源自"Panel Data"&#xff09;作为数据科学生态系统的基石&#xff0c;凭借其强大的数据结构和灵活的操作功能&#xff0c;已成为全球超过90%数据工作者的首选工具。…

MATLAB程序介绍,三维环境下的IMM(交互式多模型),使用CV和CT模型,EKF作为滤波

本文所述的MATLAB代码为三维的交互式多模型&#xff08;IMM&#xff09;滤波器&#xff0c;结合了匀速直线运动&#xff08;CV模型&#xff09;和匀速圆周运动&#xff08;CT模型&#xff09;的状态估计。使用扩展卡尔曼滤波&#xff08;EKF&#xff09;来处理状态更新与观测数…

[Windows] 多系统键鼠共享工具 轻松跨系统控制多台电脑

参考原文&#xff1a;[Windows] 多系统键鼠共享工具 轻松跨系统控制多台电脑 还在为多台电脑需要多套键盘鼠标而烦恼吗&#xff1f;是不是在操控 Windows、macOS、Linux 不同系统电脑时手忙脚乱&#xff1f;现在&#xff0c;这些问题通通能解决&#xff01;Deskflow 软件闪亮登…

【leetcode hot 100 48】旋转图像

方法一&#xff1a;&#xff08;原地旋转&#xff09;对于矩阵中第 i 行的第 j 个元素&#xff0c;在旋转后&#xff0c;它出现在倒数第 i 列的第 j 个位置。matrix[row][col]在旋转后的新位置为matrix[col][n−row−1]。只要旋转四次就能回到原点。 class Solution {public vo…

K8S高可用集群-小白学习之二进制部署(ansible+shell)

一.K8S高可用集群配置概述 序言:本文从一个小白的视角进行K8S的研究和部署,采用二进制的方式是为了更清楚了分解部署流程及了解这个集群是怎么运作的,加上ansible+shell是方便在这个过程中,遇到了问题,我们可以不断的快速重复部署来测试和研究问题的所在点,本文的架构图…

【哇! C++】类和对象(三) - 构造函数和析构函数

目录 一、构造函数 1.1 构造函数的引入 1.2 构造函数的定义和语法 1.2.1 无参构造函数&#xff1a; 1.2.2 带参构造函数 1.3 构造函数的特性 1.4 默认构造函数 二、析构函数 2.1 析构函数的概念 2.2 特性 如果一个类中什么成员都没有&#xff0c;简称为空类。 空类中…

利用Ollama+AnythingLLM+本地向量数据库Milvus+本地DeepSeek大模型实现知识库的搭建

1. Ollama的搭建 基本介绍 Ollama是一个支持在Windows、Linux和MacOS上本地运行大语言模型的工具。它允许用户非常方便地运行和使用各种大语言模型,比如Qwen模型等。用户只需一行命令就可以启动模型。 Ollama 下载&#xff1a;https://ollama.com/download Ollama 官方主页&a…

攻防世界WEB(新手模式)19-file_include

先进行代码分析 include("./check.php");&#xff1a;包含并执行当前目录下的check.php文件&#xff0c;通常用于引入一些通用的函数、类或配置信息。if(isset($_GET[filename]))&#xff1a;检查是否通过 GET 请求传递了名为filename的参数。如果传递了filename参数…

Facebook 的隐私保护数据存储方案研究

Facebook 的隐私保护数据存储方案研究 在这个信息爆炸的时代&#xff0c;数据隐私保护已成为公众关注的热点。Facebook&#xff0c;作为全球最大的社交媒体平台之一&#xff0c;承载着海量用户数据&#xff0c;其隐私保护措施和数据存储方案对于维护用户隐私至关重要。本文将深…

app UI自动化测试框架都包含哪些内容?

UI自动化测试框架是指用于自动化执行用户界面(UI)相关测试的工具和库。它们可以帮助开发团队提高测试效率、发现和解决应用程序中的问题&#xff0c;并确保应用程序的正确性和稳定性。下面将详细介绍一个完整的UI自动化测试框架应该具备的内容。 1. 测试环境配置 UI自动化测试框…

Android+SpringBoot的老年人健康饮食小程序平台

感兴趣的可以先收藏起来&#xff0c;还有大家在毕设选题&#xff0c;项目以及论文编写等相关问题都可以给我留言咨询&#xff0c;我会一一回复&#xff0c;希望帮助更多的人。 系统介绍 我将从经济、生活节奏、技术融合等方面入手&#xff0c;详细阐述居家养老管理模式兴起的…

ORB-SLAM2源码学习(六):相机跟踪(局部地图跟踪和关键帧创建)

目录 1.局部地图跟踪 1.1 更新局部关键帧UpdateLocalKeyFrames 1.2 更新局部地图点&#xff08;来自局部关键帧&#xff09;UpdateLocalPoints() 1.3 投影匹配 2. 对比四种跟踪方式以及使用的投影匹配 3.关键帧创建 3.1 判断是否需要创建新关键帧: NeedNewKeyFrame() 3…

【极客时间】浏览器工作原理与实践-2 宏观视角下的浏览器 (6讲) - 2.6 渲染流程(下):HTML、CSS和JavaScript,是如何变成页面的?

https://time.geekbang.org/column/article/118826 2.6 渲染流程&#xff08;下&#xff09;&#xff1a;HTML、CSS和JavaScript&#xff0c;是如何变成页面的&#xff1f; 2.5介绍了渲染流水线中的 DOM 生成、样式计算和布局三个阶段&#xff0c;2.6讲解渲染流水线后面的阶段…