一、内存的简要了解

说到内存,很多人应该都多多少少有点了解了,我们在这再稍微多说几句:

  一般我们可以把内存理解为三个部分:静态区,栈,堆。有些朋友搞不清到底什么是栈什么是堆,堆栈有多人会认为是堆和栈,两个放在一块。其实不然,其中我们口中讲的堆栈就是栈,而不是堆。堆的英文是heap ;栈的英文是stack(也翻译为堆栈)。

存储内容:

  静态区:保存自动全局变量和static变量(包括static全局和局部变量)。静态区的内容在整个程序的生命周期内都存在,有编译器在编译的时候分配(数据段(存储全局数据和静态数据)和代码段(可执行的代码/只读常量))。

  栈:保存局部变量。栈上的内容只在函数范围内存在,当函数运行结束的时候,这些内容也会自动销毁。其特点是效率高但是空间大小有限。

  堆:由malloc系列函数或者new操作符分配的内存。其生命周期由free和delete决定。在没有释放之前一直存在,直到函数结束。其特点是使用灵活,空间比较大,但容易出错。

  我们用一张图来简单看下:
 
  值得注意的一点是:代码段中存储的是可执行的代码和只读常量,很多人看到代码段就认为里面只有代码,数据段里面才是存储数据的,其实不是这样的。
 

二、malloc、calloc、realloc

三者的简单对比:

malloc   函数原型:(void*)malloc(unsigned size);(字节数)

malloc函数在内存中开辟的是一块连续的空间,size是所需要空间的长度,开辟的大小为size*参数类型,开辟完之后返回这块空间的首地址。

calloc    函数原型:void* calloc(size_t numElements, size_t sizeOfElements);(元素的个数, 单个元素的字节数)

和malloc相似,它也是开辟一块连续的空间,空间的大小为:元素的个数*单个元素的字节数。

realloc     函数原型:void* realloc(void* ptr, unsigned newsize);(地址,字节数)

给一个已经分配地址的指针重新分配空间,参数ptr为原有的空间指针,newsize为重新申请的地址长度。它与malloc的区别就是如果你给的指针是NULL,那么你使用的就是malloc,如果你给出的指针是一个已经分配了地址的指针(ptr),那么你使用的就是realloc。

区别:

(1)函数malloc不能初始化所分配的空间,而函数calloc能,也就是说,如果由malloc函数分配的空间原来没有被分配过,则其中每一位都可能是0;反之,如果这一块数据块原来被分配过,那里面可能遗留着各种各样的数据。所以,当你在使用malloc开辟一块新空间的时候,要重新初始化那一块空间(一般调用memset函数来初始化空间)。否则在多次释放、开辟之后,可能会出现使用错误。

(2)calloc函数会将所分配的内存空间中的每一位都初始化为0(这也是它和malloc的主要不同处之一)。也就是说,如果你是为字符类或者整形类的元素分配空间,那么这些元素会保证被初始化为0;如果你是为指针类函数分配内存,那么这些元素都会被初始化为空指针。

(3)malloc向系统申请size个字节的空间,申请完之后返回的是这个空间的首地址,类型为void*,而void*表示未确定的类型,在c/c++中void*可以被强转成任意类型的指针。

(4)realloc可以对给定的指针所指向的空间进行扩大或者缩小,无论是扩大还是缩小,原有内存中的内容将保持不变(如果对于缩小之后的空间,被缩小的那部分空间内的数据还是会丢失)。realloc并不保证调整后的内存空间和原来的内存空间保持同一个地址。相反,realloc指针很可能指向一个新的地址。

(5)realloc是从堆上分配空间的,但当你进行扩大的时候,realloc会试图从堆上现存的数据后面的那些字节中获取附加的字节,如果能满足,就刚好。但如果后面的字节数不够,其就会使用堆上第一个有足够大小的自由块,然后将现存的数据拷贝到新的位置,将老块放回到堆上。在这个过程中,数据会被移动。也就是说,当你使用realloc的时候,数据可能被移动。

 

三、有关malloc的一些扩展(选自《高质量c/c++编程指南(林锐)》)

malloc

malloc的原型:(void*)malloc(int size)(int也可以是unsigned,int只是其中的一种特例)

  malloc函数的返回值是一个void类型的指针,参数为int类型数据,即申请分配的内存大小,单位是byte。内存分配成功之后,malloc函数返回这块内存的首地址。你需要一个指针来接受这个地址。但是由于函数的返回值是void*类型,所以必须强制转换成你所接收的类型。也就是说,这块内存将来要存储什么类型的数据。比如:

char* p = (char*)malloc(100);

在堆上面分配了100个字节内存,返回这块内存的首地址,把地址强制转换成char*类型后赋给char*类型 的指针变量p。同时告诉我们这块内存将来用来存储char*类型的数据。也就是说你只能通过指针变量p来操作这块内存。这块内存本身并没有名字,对它的访问是匿名访问。

内存释放

  有分配就一定有释放。malloc对应的就是free函数。free函数只有一个参数,就是要释放的内存块的首地址。比如:free(p);

free函数做的事情:斩断指针变量与这块内存的关系。就像上面的例子一样malloc函数开辟的这一个数据块空间是属于p的,你只能通过p来访问这一块数据块空间,而free函数做的事情就是斩断malloc和p之间的联系。但是p指针本身存放的地址并没有发生变化,只是它对指针所指向的那块内存已经没有所有权了,不能对内存块进行操作。而那块内存块里面的数据也没有被改变,只是你没有办法去访问或者修改那块数据快中的内容了。

  malloc和free是一一对应的,如果malloc两次但是只free一次就会存在内存泄漏,如果malloc一次但是free了两次,就会出错(第一次使用free的时候,malloc所开辟的空间就已经被释放,第二次使用free就无内存空间可以释放了,这种对内存的误操作就有可能会导致程序的崩溃)。

  

  函数的内存释放完后,一定要把p指针置为NULL。为什么?

  从上面可以看出,free掉之后p只是切断了和内存空间的关系,但是p指针本身内部依旧存在一个地址,如果不把它置成空,那这个指针就会变成一个野指针(悬垂指针),迟早会出事。

例子:
 char* p = (char*)malloc();
strcpy(p, "hello");
free(p);//可以看到这边已经释放了p所指向的那一块空间,但是p本身存储的地址并没有改变
if(NULL != p)//判断不起作用,起不到防护作用
{
strcpy(p, "world");//p没有分配空间,出错。
}

四、new/delete

  前头讲了很多但好像还没有讲到c++的动态内存这方面。下面我们来进行一些讨论。

  我们知道c++是兼容c的,那我们明明已经有了malloc和free来进行动态内容的管理,为什么c++还要定义new和delete运算符来动态管理内存。

  来看一下它们之间的区别和联系:

  1.它们都是动态管理内存的入口。

  2.malloc/free是c/c++标准库的函数,new/delete是c++操作符。

  3.malloc/free只是动态分配/释放内存空间。而new/delete出来分配空间还会调用构造函数和析构函数进行初始化与清理。

  4.malloc/free需要手动计算类型大小且会返回void*, new/delete可以自己计算类型的大小,返回对应类型的指针。

  我们在c++中是允许进行重载的,那我们也可以重载一下new和delete,我在这就不做了(其实new和delete是不能重载的,即使你进行了重载,也只是重载了operator new和operator delete)。

  

有关operator new/operator delete    operator new[]/operator delete[]

总结:

  1.operator new/operator delete    operator new[]/operator delete[]的用法和malloc/free一样。

  2.它们只负责分配空间/释放空间,不会调用对象构造函数和析构函数来初始化/清理对象。

  3.实际operator new/operator delete  只是malloc和free的一层封装。

五、new和delete在内存中所做的事

new做的事:
1.调用operator new分配空间
2.调用构造函数初始化空间
 
delete做的事:
1.调用析构函数清理对象
2.调用operator delete释放空间
 
new[N]做的事:
1.调用operator new分配空间
2.调用N次构造函数分别初始化每个对象
 
delete做的事:
1.调用N次析构函数清理对象
2.调用operator delete释放空间
 
用一张图来解释:

c++动态内存管理的更多相关文章

  1. C++动态内存管理之shared_ptr、unique_ptr

    C++中的动态内存管理是通过new和delete两个操作符来完成的.new操作符,为对象分配内存并调用对象所属类的构造函数,返回一个指向该对象的指针.delete调用时,销毁对象,并释放对象所在的内存 ...

  2. uCGUI动态内存管理

    动态内存的堆区 /* 堆区共用体定义 */ typedef union { /* 可以以4字节来访问堆区,也可以以1个字节来访问 */ ]; /* required for proper aligne ...

  3. Keil C动态内存管理机制分析及改进(转)

    源:Keil C动态内存管理机制分析及改进 Keil C是常用的嵌入式系统编程工具,它通过init_mempool.mallloe.free等函数,提供了动态存储管理等功能.本文通过对init_mem ...

  4. (原创)动态内存管理练习 C++ std::vector<int> 模拟实现

    今天看了primer C++的 “动态内存管理类”章节,里面的例子是模拟实现std::vector<std::string>的功能. 照抄之后发现编译不通过,有个库函数调用错误,就参考着自 ...

  5. FreeRTOS 动态内存管理

    以下转载自安富莱电子: http://forum.armfly.com/forum.php 本章节为大家讲解 FreeRTOS 动态内存管理,动态内存管理是 FreeRTOS 非常重要的一项功能,前面 ...

  6. C++程序设计入门 引用和动态内存管理学习

    引用: 引用就是另一个变量的别名,通过引用所做的读写操作实际上是作用于原变量上. 由于引用是绑定在一个对象上的,所以定义引用的时候必须初始化. 函数参数:引用传递 1.引用可做函数参数,但调用时只需 ...

  7. 动态内存管理详解:malloc/free/new/delete/brk/mmap

    c++ 内存获取和释放 new/delete,new[]/delete[] c 内存获取和释放 malloc/free, calloc/realloc 上述8个函数/操作符是c/c++语言里常用来做动 ...

  8. oracle结构-内存结构与动态内存管理

    内存结构与动态内存管理 内存是影响数据库性能的重要因素. oracle8i使用静态内存管理,即,SGA内是预先在参数中配置好的,数据库启动时就按这些配置来进行内在分配,oracle10g引入了动态内存 ...

  9. 字符串输出输入函数,const修饰符,内存分区,动态内存管理,指针和函数,结构体

    1.字符串输出输入函数 读入字符串的方法: 1) scanf 特点:不能接收空格 2) gets 特点:可以接受含有空格的字符串 ,不安全 3) fgets(); 特点:可以帮我们自动根据数组的长度截 ...

  10. 动态内存管理---new&amp;delete

    动态内存管理 动态对象(堆对象)是程序在执行过程中在动态内存中用new运算符创建的对象. 因为是用户自己用new运算符创建的.因此也要求用户自己用delete运算符释放,即用户必须自己管理动态内存. ...

随机推荐

  1. AngularJS语法基础及数据绑定——详解各种数据绑定指令、属性应用

    AngularJS简单易学,但是功能强大.特别是在构建单页面应用方面效果显著.而 数据绑定 可以说是他被广泛使用的最主要的优点.他舍弃了对DOM的操作方式,一切都由AngularJS来自动更新视图,我 ...

  2. Unity 打包总结和资源的优化和处理

    1. Texture,都去掉alpha通道,作为背景展示的图片,基本都没有透明要求,有特殊要求的则放到atlas里面 a. Loading图这类需要比较精细的,则把图片设置为Automatic Tru ...

  3. 打开IIS的快捷键

    [windows键+R]→在运行界面输入“inetmgr”→点击回车键,即可以出现IIS界面

  4. 隐马尔科夫模型HMM(三)鲍姆-韦尔奇算法求解HMM参数

    隐马尔科夫模型HMM(一)HMM模型 隐马尔科夫模型HMM(二)前向后向算法评估观察序列概率 隐马尔科夫模型HMM(三)鲍姆-韦尔奇算法求解HMM参数(TODO) 隐马尔科夫模型HMM(四)维特比算法 ...

  5. [译]WPF MVVM 架构 Step By Step(5)(添加actions和INotifyPropertyChanged接口)

    应用不只是包含textboxs和labels,还包含actions,如按钮和鼠标事件等.接下来我们加上一些像按钮这样的UI元素来看MVVM类怎么演变的.与之前的UI相比,这次我们加上一个"C ...

  6. SQLServer 理解copyonly备份操作

    标签:MSSQL/日志截断 概述 Alwayson在添加数据库的过程中如果同步首选项选择的是“完整”,那么就会在主副本上执行copyonly的完整备份和日志备份在辅助副本上执行还原操作,也正是这个操作 ...

  7. jquery 实现滚动条下拉时无限加载的简单实例

    var lastId=0;//记录每一次加载时的最后一条记录id,跟您的排序方式有关.     var isloading = false;   $(window).bind("scroll ...

  8. ionic 项目中使用ngCordova插件$cordovaCamera筛选手机图库图片显示出来并上传

    原文档请看http://www.ncloud.hk/%E6%8A%80%E6%9C%AF%E5%88%86%E4%BA%AB/ionic%E5%9B%BE%E7%89%87%E4%B8%8A%E4%B ...

  9. 学习mysql语法--基础篇(一)

      前  言  mysql  mysql语法--本篇学习都是通过使用Navicat Premium(数据库管理工具),连接mysql数据. 本篇学习主要有两个部分:    一.创建用户,创建数据库,给 ...

  10. VS2017专业版和企业版激活密钥

    VS2017专业版和企业版激活密钥 Professional: KBJFW-NXHK6-W4WJM-CRMQB-G3CDH Enterprise: NJVYC-BMHX2-G77MM-4XJMR-6Q ...