为什么要使用动态内存分配?

比较常见的情况是当我们使用一个数组时,我们需要去声明它,同时我们还需要提供给它一个编译时常量用于指定数组的长度。但是,我们有时候需要的数组并不是定长的。例如,我们要存储一个班级所有学生的数据,但是不同班的学生数量可能是不同的,我们虽然可以声明一个尽量大的数组来存储,但是这样会造成资源的浪费,而且也不利于扩展使用。所以,我们需要动态地去分配一个数组的空间。

malloc和free

在c函数库中有两个函数,malloc和free,在c++中同样是存在的,它们分别用于执行动态内存的分配和释放。这些函数维护一个可用内存池,当一个程序另外一些内存时,就调用malloc函数,malloc从内存池中提取一块合适的内存,并向该程序返回一个指向这块内存的指针。这块内存池并没有以任何形式进行初始化,如果需要被初始化,需要你手动进行初始化或者使用calloc。当此内存不再使用,便调用free函数把它归还给内存池。
函数原型如下:

void *malloc(size_t size);
void *free(void *pointer);

malloc函数的参数就是需要分配的内存字节数,如果内存池中的可用内存可以满足这个需求,则返回一个指向被分配的内存块起始位置的指针。malloc所分配的是一块连续的内存,而且实际分配的内存有可能会比你要求的多一些。
如果内存池的可用内存不满足程序的要求,那么malloc就会向操作系统请求,要求得到更多的内存,并在新内存上执行分配任务。如果操作系统无法提供更多的内存,malloc则返回一个NULL指针。
free函数的参数要么是一个NULL,要么是先前从malloc,calloc,realloc返回的值。(而不能是一个new得到的值),那么这里就有一个问题了,malloc函数指定了size,所以能知道要分配多少内存,free函数没有size,是如何知道它需要释放的内存容量的?其实在每一个内存块的首部都维护着一个mem_control_block(内存控制块),它是一个结构体,形式如下:

struct mem_control_block {//is_available指明此内存块是否可以被使用,1为可以,0为不可以int is_available;//指明当前内存块的大小int size;
};

在使用malloc分配内存时,如果malloc函数内部得到的内存区域的首地址为void *p,那么它返回给你的就是p + sizeof(mem_control_block),偏移到真正的内存块开始的位置,而当我们调用free的时候,则将p - sizeof(men_control_block),回到mem_control_block的头部,这里用*q代替,然后使用q->size,程序就能知道需要释放的内存是多少了。形如下图:

另外需要注意的就是free函数释放的是指针所指向的内存块,而不是指针本身,所以我们在释放了内存块之后,还需要将指针指向NULL,否则,就会出现野指针的现象。

calloc和realloc

在C中还有另外两个内存分配函数,calloc和realloc,它们的原型如下:
void *calloc(size_t num_elements,size_t element_size);
void *realloc(void *ptr, size_t new_size);
calloc也用于分配内存,malloc和calloc之间的区别就在于后者在返回指向内存的指针之前把它初始化为0,另外它们之间请求内存的方式也不一样,calloc的参数是所需的元素个数,和每个元素的字节数,根据这个计算出所需的总内存数,然后再去申请。
realloc用于修改一个原先已经分配的内存块的大小,使用这个函数,可以扩大或缩小一个内存块。如果使用它来扩大一个内存块,那么这个内存块原先的内容依然保留,新增加的内存添加到原先内存块的后面,新内存是未被初始化的。如果它被用于缩小一个内存块,则该内存块尾部的部分内存被拿掉,剩余部分内存的原先内容依然保留。
如果原先的内存块无法改变大小,realloc将分配另一块正确大小的内存,并把原先那块内存的内容复制到新的块上。因此,在使用realloc之后,你就不能再使用指向旧内存的指针,而是应该改用realloc所返回的新指针。
而如果,realloc的第一个参数是NULL,那么它的行为就和malloc一摸一样了。

new和delete

new和delete是C++中动态内存分配的操作符,在我们需要对一些类对象动态地分配内存空间和释放空间的时候,malloc和free就有些不够看了。因为我们为类对象动态分配空间的时候,同时需要调用类对象的构造方法对其进行初始化,释放空间的时候,同样要调用类对象的析构函数,所以我们就需要使用new和delete来做这些工作。
讲到new操作符,就经常牵涉到operator new 和new operator的话题,这两个是什么呢?例如:

int *p = new int;

这样一句简单的代码,我们看到的这个new就是new operator,然而在这句代码的背后,执行的步骤简单描述如下:
1. 为对象分配内存空间;
2. 调用对象的构造函数初始化内存空间;
在第一步中分配内存的部门就使用了operator new,详细的可以参考:c++ 中new 操作符是怎么实现的

new与malloc的区别?

  1. new是一个操作符,而malloc是一个库函数;
  2. new能够自动计算需要分配的内存空间,而malloc需要手工计算字节数;
  3. new计算空间是通过数据类型,而malloc通过字节大小;
  4. new和delete带具有具体类型的指针,而malloc和free则是void *;
  5. new是类型安全的,但是malloc不是,例如int *p = new float[2];这样编译器会报错,但是int *p = malloc(2*sizeof(int));这样是不会报错的;
  6. new操作符可以重载,而malloc不可以;
  7. new可以调用构造函数,但是malloc不可以;

从上面我们可以看出,new操作符的作用要比malloc的作用强大,而且malloc有的功能,使用new也可以做到,那么我们为什么还需要保留malloc呢?答案是在C++中,经常需要外部链接调用C的库,而C中管理动态内存需要使用malloc,所以我们就需要保留下malloc。

内存泄漏以及内存溢出?

什么是内存泄露?
内存泄漏是指内存被动态分配以后,当它不再使用时未被释放。内存泄漏会增加程序的体积,有可能导致程序或系统的奔溃。另外,内存泄漏还有可能导致内存不足的问题。所以,每次动态分配了内存空间的时候,当内存不再需要使用时,都要对其进行释放。
什么是内存溢出?
内存溢出简单理解就是你给一个程序的内存空间(或者你本身所拥有的内存)小于该程序运行现状所需要的内存空间,而且你又没有对这种情况做好处理措施,程序就会操作到本不应该被操作到的内存空间,这样的情况就是溢出现象。这种现象可能会被黑客利用攻击。
更详尽的可以参考:内存泄漏和内存溢出有啥区别?

总结

  1. 在使用malloc函数动态分配内存时,需要检查函数返回的指针是否为NULL;
  2. malloc和free,new和delete要成对使用;
  3. 在动态分配的内存空间不再使用时,要释放该空间;
  4. 释放空间之后,要将指向该内存空间的指针指向NULL,防止出现野指针的情况;
  5. 不要重复释放一块内存,否则可能会破坏自由空间;

参考

  1. https://segmentfault.com/q/1010000000160483
  2. https://www.zhihu.com/question/40560123
  3. 《Effective C++》,《C和指针》,《C++ Primer 第5版》

关于C++的动态内存分配相关推荐

  1. 释放变量所指向的内存_C++动态内存分配(学习笔记:第6章 15)

    动态内存分配[1] 动态申请内存操作符 new new 类型名T(初始化参数列表) 功能: 在程序执行期间,申请用于存放T类型对象的内存空间,并依初值列表赋以初值. 结果值: 成功:T类型的指针,指向 ...

  2. 动态内存分配与柔性数组

    什么时动态内存分配 一般我们写程序都是在栈区分配空间,如果我们想根据需求想随时存放随时释放数据,堆区可以实现根据需求想系统申请所需大小的空间. 建立内存的动态分配 内存的动态分配是通过系统提供的函数来 ...

  3. C++中的动态内存分配

    1.Cpp中的内存分配 了解动态内存在C++中是如何工作的是成为一名合格的C++程序员必不可少的.C++程序中的内存分为两个部分: 栈:在函数内部声明的所有变量都将占用栈内存. 堆:这是程序中未使用的 ...

  4. 【 C 】动态内存分配实用案例(二)之复制字符串

    用动态分配内存制作一个字符串的一份拷贝.注意:调用程序应该负责检查这块内存是否分配成功,这样做允许程序以任何它所希望的方式对错误作出反应. #nclude <stdlib.h> #incl ...

  5. 【 C 】动态内存分配实用案例(一)之读取、排序和打印一列整形值

    什么时候用动态内存分配呢?下面这个案例给出了一个比较实用且精彩地使用动态内存的场合,并且教你如何合理地使用动态内存分配? 动态内存分配一个常见的用途就是为那些长度在运行时才知的数组分配内存空间. 下面 ...

  6. 【 C 】动态内存分配案例分析

    声明一个指向char类型的指针,可以在声明的时候就对其进行初始化,这样是合理的. 例如: E1: #include <stdio.h> #include <stdlib.h> ...

  7. 【C 语言】内存管理 ( 动态内存分配 | 栈 | 堆 | 静态存储区 | 内存布局 | 野指针 )

    相关文章链接 : 1.[嵌入式开发]C语言 指针数组 多维数组 2.[嵌入式开发]C语言 命令行参数 函数指针 gdb调试 3.[嵌入式开发]C语言 结构体相关 的 函数 指针 数组 4.[嵌入式开发 ...

  8. 从更底层研究C\C++动态内存分配

    2019独角兽企业重金招聘Python工程师标准>>> 以前在学C++ 的时候,一直不懂:动态内存分配的本质,或者更加深入到底层的意义.虽然说,动态内存分配就是,随机在内存中分配一个 ...

  9. 静态、动态内存分配比较

    首先,在使用动态分配内存技术前,必须明白自己在做什么,这样做与其它的方法有什么不同,特别是会产生哪些负面影响,天下没有免费的午餐.动态分配内存与静态分配内存的区别: 1) 静态内存分配是在编译时完成的 ...

  10. 动态内存分配到底为谁分配内存空间【浅谈动态内存的一个实例】

    为了动态的管理宝贵的内存,许多程序中要使用到动态内存分配.一般情况下,在c语言中,使用malloc()函数来分配指定大小的内存空间,用free()函数来释放这块内存空间.但是,往往初学者在编写这类程序 ...

最新文章

  1. iOS开发之--TableViewCell重用机制避免重复显示问题
  2. 购买IBM System x3650 M4十大理由
  3. java调用dueros_DuerOS纯java sdk 支持windows,唤醒(仅linux),技能
  4. java 判断int是几位_快速判断一个int值是几位数
  5. 大数据 互联网架构阶段 Nginx的使用
  6. 3PAR推InServ-T级存储 EMC们紧张了?
  7. 如何使用SAP CRM增强工具AET创建Table表格类型的增强
  8. 还在用 Swagger(丝袜哥)生成接口文档?我推荐你试试它。。。
  9. 微信小程序css之线性渐变
  10. ~~朴素dijkstra算法
  11. deleter mysql,如何通过env文件传递docker中的环境变量?
  12. 斐讯K1S路由器刷华硕固件教程
  13. 图片/视频获取缩略图的几种方式
  14. [面试问答] 面试常问的40个问题 附带经典答案!
  15. 钓鱼网站检测技术的演进
  16. Qt开发——图片缩放简述
  17. 虚拟机服务器磁盘扩容步骤,vmware ESXi 虚拟机扩容磁盘空间
  18. 苹果天气无线网连接到服务器,苹果手机天气怎么设置?教你玩转天气应用
  19. codeforces 1635E-Cars (二分图染色+拓扑排序)
  20. 【历史上的今天】9 月 11 日:Adobe 公司联合创始人出生;现代游戏机鼻祖诞生;谷歌推出 Android Pay

热门文章

  1. 2020.7.13 筛选tagged后的语料excel中的动名词搭配
  2. EMC电磁兼容性问题总结
  3. 皓龙服务器系列怎么样,双核皓龙服务器导购
  4. 软件测试中的树莓酱定律
  5. oracle数据泵导入导出6,oracle 10g数据泵和导入导出性能对比(六)
  6. YOJ3394-挑剔的H胖胖
  7. VMware GPU虚拟化简介
  8. Python爬虫入门教程 35-100 知乎网全站用户爬虫 scrapy
  9. 四参数拟合曲线_如何用GraphPad Prism 8.0对散点图进行拟合?
  10. 2021中行校招面试