首页 > 系统相关 >【一文教你学会动态内存管理】

【一文教你学会动态内存管理】

时间:2023-05-25 12:01:16浏览次数:43  
标签:malloc 函数 int 学会 申请 文教 动态内存 空间 NULL

1.为什么会存在动态内存分配?

2. 动态内存函数的介绍

  • 2.1 malloc函数和free函数
  • 2.2 calloc函数
  • 2.3 realloc

3. 常见的动态内存错误

  • 3.1 对NULL指针的解引用操作
  • 3.2 对动态开辟空间的越界访问
  • 3.3 对非动态开辟内存使用free释放
  • 3.4 使用free释放一块动态开辟内存的一部分
  • 3.5 对同一块动态内存多次释放
  • 3.6 动态开辟内存忘记释放(内存泄漏)

1.为什么会存在动态内存分配? 我们现在知道的开辟内存方式是创建变量,创建数组。而这些东西是在栈区上开辟空间的,一旦创建完成,是无法更改的,是固定死的。就像数组,在创建数组时,已经指定了数组的大小,编译后无法再更改。这时候就需要动态内存分配。

2. 动态内存函数的介绍

2.1 malloc函数和free函数 1.malloc函数学习新函数时,就从库里面找函数的声明,解析来学习。void* malloc (size_t size);该函数的功能是,向堆区申请一块size个字节大小的空间。如果申请成功,返回申请的空间的起始地址,如果申请失败,返回空指针NULL。 至于malloc函数在库里的为什么是void*,这是因为malloc函数也不知道使用者需要这块空间来存放什么,具体要什么类型的指针,使用者自己决定。如果参数 size 为0,malloc的行为是标准是未定义的,取决于编译器。下面举个例子来深入了解: 在这段代码中,我们用了malloc函数来社区内40个字节的空间,(一个int是4个字节,*10是40字节) 但是这样写是不对的,前面说过,malloc是向内存中的堆区申请空间,如果申请失败,就返回NULL,如果现在不判断p的值,直接使用的话,就会出问题,所以我们应该判断p的有效性。顺便提一下,perror是一个报错的函数,假如申请空间失败返回NULL,perror会报告相应的错误信息。来演示一下: 假如申请INT_MAX个字节的空间: 这是一个极大的数字,看看是否能够申请成功。 在这里,perror报告错误:没有足够的空间。具体的用法请学习一下。

free函数 free函数和malloc函数是成对出现的。free函数是专门用来释放申请的空间的。具体用法如下:int main() { int* p = (int *)malloc(sizeof(int)*10); if (p == NULL) { perror("malloc"); } free(p); p = NULL; return 0; }在申请完空间之后,如果不用了,就需要释放掉,把申请的空间还给操作系统,即 有借有还,再借不难 的道理。总结:malloc函数是向堆区申请一块空间,如果申请成功,则返回该空间的首地址,如果申请失败,则返回NULL。free函数是将申请的空间释放掉

2.2 calloc函数 calloc函数也是向堆区中申请内存的函数。具体参数如下:void* calloc (size_t num, size_t size);第一个参数是申请的元素个数,第二个参数是申请的每个元素大小。注意:calloc为元素数组分配一个内存块,并将其所有位初始化为零。也就是说,calloc函数不仅申请空间,还将该空间初始化为0。举个例子来证明:int main() { int *p = (int*)calloc(10, sizeof(int)); if (p == NULL) { perror("calloc"); } for (int i = 0; i < 10; i++) { printf("%d ", *(p + i)); } return 0; }结果如下:结果就是:将申请的空间自动初始化为0。calloc函数与malloc函数类似,只是malloc函数只负责申请空间,没有初始化,而calloc函数会申请空间并初始化。不过也不是说calloc函数比malloc函数更高级,calloc函数 初始化也会花费时间,malloc函数不初始化,所以它也节省了一部分时间。每个函数之间各有优缺点。所以在使用函数的时候,结合实际情况来使用。

realloc函数,让我们动态申请的空间更加灵活。当我们觉得动态申请的空间太大或者太小时,可以用realloc函数来调整动态申请的空间大小。realloc函数的原型如下:void* realloc (void* ptr, size_t size);ptr是需要重新调整的空间的起始地址, size 调整之后新大小 注意:是调整后的大小假如刚开始动态申请的空间是40字节,发现不够用了,想加大10个字节的空间,那么使用realloc函数重新调整空间时,参数就是50。realloc函数的返回值是调整后的空间的起始地址。既然返回调整后的起始地址,那会不会出现申请失败的情况?会不会出现空间不足以申请的情况? 会的。情况1: 重新申请空间时,假如后面的空间足够大,那么realloc函数就会返回空间的首地址。原来有的数据不会变化: 情况2: 如果重新申请空间失败,则返回NULL。情况3:****如果在原来的空间后面没有足够大的空间来增容,realloc函数会自动在内存中的其他位置找一块满足我们要求的空间,并把原空间的数据拷贝到新空间中,然后返回新空间的起始地址。 基于情况2和情况3,我们是不是用原空间的指针来接收呢?int main() { int* p = (int*)malloc(sizeof(int) * 5); if (p == NULL) { perror("malloc"); return ; } int i = 0; for (i = 0; i < 5; i++) { *(p + i) = i; } 不够了,增加空间 int* p = (int*)realloc(p, sizeof(int) * 10);还是刚才的例子,上面这段代码对吗? 不对。 因为在重新realloc调整空间的时候,如果用原空间的地址来接收的话,万一申请失败呢? 如果申请失败,realloc函数会返回一个NULL,如果用p来接收的话,p原来指向的空间的数据就丢失了!就找不到了。 所以不能用原空间p来接收realloc返回的地址,我们需要用一个临时指针来接收返回的地址。int* ptr = (int*)realloc(p, sizeof(int) * 10);这样写才是正确的,然后判断ptr是否为空,如果不为空,再把这个ptr存的地址赋给p。if (ptr != NULL) { p = ptr; ptr = NULL;(防止ptr成为野指针) }以上就是三个申请空间的函数的基本情况,根据需求,选择不同的函数。

3. 常见的动态内存错误

3.1 对NULL指针的解引用操作 void test() { int* p = (int*)malloc(INT_MAX); *p = 20; free(p); } int main() { test(); return 0; }这段代码的错误是,没有判断p的值就对p进行解引用,如果申请的空间失败,返回NULL,对NULL进行*操作,是非法的。改正很简单:只需判断p是否为NULL即可。void test() { int* p = (int*)malloc(INT_MAX / 4); if (p == NULL) { perror("malloc"); return; } *p = 20; free(p); p = NULL; }改正结果如上。

3.2 对动态开辟空间的越界访问 void test() { int i = 0; int* p = (int*)malloc(10 * sizeof(int)); if (NULL == p) { perror("malloc"); return; } for (i = 0; i < 20; i++) { *(p + i) = i;//当i是10的时候越界访问 } free(p); p = NULL; } int main() { test(); return 0; }上面这段代码的错误是,malloc函数只申请了10个整型大小的空间,(40)字节,但是在赋值的时候,i的范围取到了20,造成对后面的空间非法访问。改正如下:只需把i的范围调整到i<10即可,或者最初申请的空间到20个整型大小(80字节)void test() { int i = 0; int* p = (int*)malloc(10 * sizeof(int)); if (NULL == p) { perror("malloc"); return; } for (i = 0; i < 10; i++) { *(p + i) = i;//当i是10的时候越界访问 } free(p); p = NULL; }

3.3 对非动态开辟内存使用free释放 void test() { int a = 10; int *p = &a; free(p); p = NULL; } int main() { test(); return 0; }上面代码的错误在于,a是一个变量,在内存中的栈区创建,不是用malloc等函数申请出来的空间,后面free(p)的时候,free释放的是堆区上申请的空间,明显两者有差异。释放是非法的。 运行时也产生错误,并且关掉这个程序会很卡顿。改正方法有多种,可以把free(p)去掉。

3.4 使用free释放一块动态开辟内存的一部分 void test() { int* p = (int*)malloc(100); if(p == NULL) { perror("malloc"); return; } p++; free(p); p = NULL; } int main() { test(); return 0; }上面代码的错误在于,申请的100字节的空间,用p来接收,但是p又++了,此时p不再指向申请的空间的起始地址,释放的时候,只释放一部分,剩下的空间没有释放完,造成内存泄漏。 运行时一样会报错。所以申请的空间的起始地址不能丢失。

3.5 对同一块动态内存多次释放 void test() { int *p = (int *)malloc(100); free(p); free(p);//重复释放 } int main() { test(); return 0; }重复释放也会出现错误。 运行时依然报错。

3.6 动态开辟内存忘记释放(内存泄漏) void test() { int *p = (int *)malloc(100); if(NULL != p) { *p = 20; } } int main() { test(); while(1); }

在test函数内部申请的空间,除了test函数后,没有返回值,意味着申请的这块空间丢失了,没有人记得这块空间的存在,造成了内存泄露!关于动态内存,你学会了吗?学会了不妨关注我吧!

标签:malloc,函数,int,学会,申请,文教,动态内存,空间,NULL
From: https://blog.51cto.com/u_15818575/6346552

相关文章

  • #Powerbi 1分钟学会,SUMMARIZE函数,分组汇总并新建表
    今天我们来学习一个新的表函数,SUMMARIZE函数是DAX中的一个函数,它可以根据一列或多列对数据进行分组,并且可以使用指定的表达式为汇总后的表添加新列,形成一张新表。一:基础语法SUMMARIZE函数的语法是:SUMMARIZE (表, 分组列1 [, 分组列2]… [, 名称, 表达式]…)其中:表,是任何......
  • 动态内存分配复习
    动态内存分配复习为什么要使用动态内存分配:在声明数组时,必须用一个编译常量指定数组长度,但是,数组的长度往往只有在运行的时候才能被确定,这是因为它所需要的内存空间取决于输入数据,但是容易浪费空间,又或者容易溢出malloc和free:malloc执行动态内存分配,free执行释放内存,当使用mal......
  • 第十六篇——学会标记函数,简单实现通达信指标公式做标记(从零起步编写通达信指标公式系
    前面两篇文章介绍了通达信指标公式的画线函数,今天给大家介绍绘图函数的第二种类型——标记函数,讲解DRAWICON、DRAWTEXT、DRAWNUMBER的具体用法。标记函数可以给指标发出的信号做醒目的标记,方便我们查看信号。 一、DRAWICON函数 含义:绘制图标 使用方法: DRAWI......
  • 新手建站:你阿里云服务器搭建网站图文教程
    使用阿里云服务器快速搭建网站教程,先为云服务器安装宝塔面板,然后在宝塔面板上新建站点,阿里云服务器网以搭建WordPress网站博客为例,阿里云百科来详细说下从阿里云服务器CPU内存配置选择、Web环境、域名解析到网站上线全流程:步骤一:云服务器配置选择如果你已经有了阿里云服务器,那么可......
  • Windows10安装Jmeter(图文教程)
    ApacheJMeter是Apache组织开发的基于Java的压力测试工具。用于对软件做压力测试,它最初被设计用于Web应用测试,但后来扩展到其他测试领域。它可以用于测试静态和动态资源,例如静态文件、Java小服务程序、CGI脚本、Java对象、数据库、FTP服务器,等等。JMeter可以用于对服务器......
  • C语言--动态内存分配2
    realloc--追加动态内存空间relloc函数追加的两种形式:原动态内存空间后有足够空间进行追加时直接追加。返回原动态内存地址。原动态内存空间厚不够追加空间时,则重新开辟动态内存空间,复制原有内存空间大小、数据并在  其后追加空间。释放原动态内存,返回新的动态内存地址。动态内......
  • 广西高等教育学会高校教育技术委员会莅临瑞云科技考察交流
    2023年3月18日上午11点整,广西高等教育学会高校教育技术专业委员会组织了一批来自广西各院校的专家老师,来到深圳市瑞云科技股份有限公司(以下简称瑞云科技)参观考察。瑞云科技是一家专注为视觉行业提供垂直云计算服务的公司,用户超20万,遍及50多个国家和地区。参观考察人员包括来自广......
  • 合作谱新篇 携手同发展 | 热烈欢迎中国计算机学会(CCF)苏州一行走访璞华!
    5月7日下午,以“合作谱新篇携手同发展“为主题的“2023年CCF(中国计算机协会)苏州走进璞华”活动在璞华苏州总部成功举办。CCF副秘书长束庆山、CCF苏州分会主席龚声蓉、CCF苏州分部副主席胡伏原、璞华公司董事长管祥红等20多位专家、企业家出席活动。活动伊始,璞华集团董事长管祥......
  • 零基础学会计二:资产<应收、预付、应付、预收>
    一、应收、预付、应付、预收>>应收、预付是资产,应付、预收是负债。>>预付账款和预收账款账户,一用用到底。(借方代表资产,贷方代表负债)>>预付或预收业务不多的企业可以采用“收收核算”或“付付核算”,应保留原来的借贷方向。1.1、坏账准备的账务处理1>计提/补提坏账准备借:信用......
  • 零基础学会计一:会计概述
    一、会计要素及其确认与计量1.1、会计要素资金运动(会计对象)->资产、负债、所有者权益、利润、收入、费用(会计要素)->会计科目1.2、会计恒等式1>资产=负债+所有者权益注:左边代表资金占有,右边代表资金来源。2>利润=收入-负债注:利润归属于所有者权益,所以有3)的代入式公式。3>......