c++内存管理

1、静态全局变量、全局变量、静态局部变量、局部变量的区别

  • 静态全局变量、全局变量的区别

    静态全局变量和全局变量都存储在全局/静态存储区

    静态全局区只在本⽂件有效,不可extern到其他⽂件,⽽全局变量可以extern到别的⽂件中调⽤。

    如果别的⽂件中定义了⼀个和该全局变量相同的变量名,会报错。

  • 静态局部变量(⽐如函数内部的变量⽤ static 修饰)、局部变量的区别

    静态局部变量是属于常量区的,⽽函数内部的局部变量属于栈区;

    静态局部变量在该函数调⽤结束时,不会销毁,⽽是随整个程序结束⽽结束,但是别的函数调⽤不了该变量,局部变量随该函数 的结束⽽结束;

    如果定义这两个变量的时候没有初始值时,静态局部变量会⾃动定义为0,⽽局部变量就是⼀个随机值;

    静态局部变量在编译期间只赋值⼀次,以后每次函数调⽤时,不在赋值,调⽤上次的函数调⽤结束时的值。局部变量在调⽤期 间,每调⽤⼀次,赋⼀次值。

  1. 在c++中,内存分为 5 个区,

    • 堆区heap:分配堆区内存也叫动态内存分配。程序在运⾏的时候使⽤ malloc 申请任意多少的内存,程序员⾃⼰负责在何时⽤ free 释放内存。动态内存的⽣命周期由程序员决定,使⽤⾮常灵活,但如果在堆上分配了空间,既有责任回收它,否则运⾏的程序会出现内存泄 漏,频繁的分配和释放不同⼤⼩的堆空间将会产⽣内存碎⽚,如果程序员没有释放掉,那么在程序结束后,操作系统会⾃动回收。

    • 栈区:在执⾏函数时,函数内局部变量的存储单元可以在栈上创建,函数执⾏结束时,这些内存单元会⾃动被释放。栈内存分配运算内 置于处理器的指令集,效率⾼,但是分配的内存容量有限。⾥⾯的变量通常是局部变量、函数参数、返回数据、返回地址等。由编译器⾃动分配释放。

    • ⾃由存储区:就是那些由 new/delete 等分配的内存块,他和堆是⼗分相似的,也是⽤户来申请释放内存。

    • 全局/静态存储区:内存在程序编译的时候已经分配好,这块内存在程序的整个运⾏期间都存在。例如全局变量,static静态全局变量。 程序结束后由系统释放。

    • 常量存储区:存放常量、字符串、静态局部变量,程序结束后有系统释放。

      在所有函数体外定义的是全局量,加了static修饰符后不管在哪⾥都存放在全局区(静态区),⽽且表⽰在该⽂件中有效,不能extern到 别的⽂件⽤。在函数体内定义的static(静态局部变量)表⽰只在该函数体内有效。另外,函数中像 “adgfdf” 这样的字符串存放在常量存储区

    char *p1; //全局未初始化区,全局存储区
    void main(){int b; //栈,局部变量
    char s[] = "abc"; //栈,局部变量
    char *p2; //栈,局部变量
    char *p3 = "123456"; // “123456” 在常量区,p3在栈上
    static int c = 0; //全局(静态)初始化区
    p1 = (char *)malloc(10); //分配得来得10字节的区域在堆区
    p2 = (char *)malloc(20); //分配得来得20字节的区域在堆区
    strcpy(p1, "123456"); // “123456” 放在常量区,编译器可能会将它与p3所指向的"123456"优化成⼀块
    }
    
  2. 堆与栈的关系和区别

    • 分配效率上。现代计算机有专门的寄存器指向栈所在的地址,有专门的机器指令(push,pop)完成数据⼊栈出栈的操作,这种机制 的特点是效率⾼,⽀持的数据有限,⼀般是整数,指针,浮点数等系统直接⽀持的数据类型,并不直接⽀持其他的数据结构。因为栈的这种 特点,对栈的使⽤在程序中是⾮常频繁的。⽽堆的数据结构并不是由系统(⽆论是机器系统还是操作系统)⽀持的,⽽是由函数库提供的。基 本的malloc/realloc/free 函数维护了⼀套内部的堆数据结构。从以上知识可知,栈是系统提供的功能,特点是快速⾼效,缺点是有限制,数据不灵活;⽽堆是函数库提供的功能,特点是灵活⽅便,数据适应⾯⼴泛,但是效率有⼀定降低。栈空间分静态分配和动态分配两种。静态分 配是编译器完成的,⽐如⾃动变量(auto)的分配。动态分配由alloca函数完成。栈的动态分配⽆需释放(是⾃动的),也就没有释放函数。为 可移植的程序起见,栈的动态分配操作是不被⿎励的!堆空间的分配总是动态的,虽然程序结束时所有的数据空间都会被释放回系统,但是 精确的申请内存/ 释放内存匹配是良好程序的基本要素。
    • 碎⽚问题:对于堆来讲,频繁的new/delete 或者 malloc/free 势必会造成内存空间的不连续,从⽽造成⼤量的碎⽚,使程序效率降低。 对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,以⾄于永远都不可能有⼀个内存块从栈中间弹出,在他弹出之前,在他上⾯ 的后进的栈内容已经被弹出。
    • ⽣长⽅向:对于堆来讲,⽣长⽅向是向上的,也就是向着内存地址增加的⽅向;对于栈来讲,它的⽣长⽅向是向下的,是向着内存地 址减⼩的⽅向增长。
    • 分配⽅式:堆都是动态分配的,没有静态分配的堆。栈有 2 种分配⽅式:静态分配和动态分配。静态分配是编译器完成的,⽐如局部变量的分配。动态分配由alloca函数进⾏分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进⾏释放,⽆需我们⼿⼯实现。
    int* p=new int[5];  //  new 分配了⼀块堆内存(也可以叫⾃由存储区),指针p分配的是⼀块栈内存,所以这句话的意思就是:在栈内存中存放了⼀个指向⼀块堆内存的指针p。delete []p; // 删除不是⽤ delete p,应该是delete [] p,这是为了告诉编译器删除的是⼀个数组
    

newmalloc 的区别

参考: 前⾔。

malloc 只是简单的申请内存, new 还会检查是否申请成功,并且负责调⽤构造函数(如果有的话), newmalloc 要好的多,建议使⽤。其实,在 new 中,也是调⽤ malloc 申请内存的,只不过封装的更好,更安全。

  • 申请时的内存所在位置

    new 操作从⾃由存储区上为对象动态分配内存空间,⽽ malloc 函数从堆上为对象动态地分配内存空间。⾃由存储区是C++基于 new 操作符的⼀个抽象概念,凡是通过 new 操作符进⾏内存申请,该内存即为⾃由存储区。⽽堆是操作系统中的术语,是操作系统所维护的⼀块特殊内存,⽤于程序的内存动态分配,C语⾔使⽤ malloc 从堆上分配内存,使⽤ free 释放已分配的对应内存。

  • 那么⾃由存储区和堆区的区别?或者说那么⾃由存储区是否能够是堆(问题等价于new是否能在堆上动态分配内存)?

    这取决于 new 操作符的实现细节。⾃由存储区不仅可以是堆,还可以是静态存储区,这得看 new 操作符在哪⾥为对象分配内存。特别的, new 甚⾄可以不为对象分配内存,⽐如定位new —⼈为找到⼀块分配内存的地址,⽽不让系统随机分配地址。

  • new operatoroperator new 的区别?调⽤new的时候编译器做了什么?

    new 就是 new operator ,调⽤ new 的时候编译器做了三件事:

    1. operator new( ) 分配内存。new的底层是调⽤operator new( )分配内存的。该函数调⽤malloc申请内存。
    2. 是调⽤构造函数(就是new 的类类型或者 string 等类型的构造函数)。这⼀步是使⽤ placement new(定位new) 来实现的, 即在取得了⼀块可以容纳指定类型对象的内存之后,在这块内存上构造⼀个对象
    3. 返回相应数据类型的指针。

new(new operator) 和 malloc 的主要区别。

  1. 返回类型的安全性

    new 操作符内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,⽆须进⾏类型转换,故 new 是符合类型安全性的操作符。⽽ malloc 内存分配成功则是返回 *void *** ,需要通过强制类型转换将 void 指针转换成我们需要的类型。类型安全很⼤程度上可以等价于内存安全,类型安全的代码不会试图分配⾃⼰没被授权的内存区域。

  2. 内存分配失败时的返回值

    new 内存分配失败时,会抛出 bac_alloc异常 ,它不会返回 NULLmalloc 分配内存失败时返回 NULL

  3. 是否需要指定内存⼤⼩

    使⽤ new 操作符申请内存分配时⽆须指定内存块的⼤⼩,编译器会根据类型信息⾃⾏计算,⽽ malloc 则需要显式地指出所需内存的尺⼨。

class A{...}
A * ptr = new A;
A * ptr = (A *) malloc(sizeof(A)); //需要显式指定所需内存⼤⼩sizeof(A);⽽且 malloc(sizeof(A)) 只是返回⼀个空类型的指针,需要经过 (A*)强制转换成 A 类型
  1. 是否调⽤构造函数/析构函数

    使⽤new操作符来分配对象内存时会经历三个步骤:

    • 调⽤ operatornew 函数(对于数组是operator new[])分配⼀块⾜够⼤的,原始的,未命名的内存空间以便存储特定类型的对象。
    • 编译器运⾏相应的构造函数以构造对象,并为其传⼊初值。
    • 对象构造完成后,返回⼀个指向该对象的指针。

    使⽤delete操作符来释放对象内存时会经历两个步骤:

    • 调⽤对象的析构函数。
    • 编译器调⽤operatordelete(对于数组是 operator delete[])函数释放内存空间。

    总之来说, new/delete 会调⽤对象的构造函数/析构函数以完成对象的构造/析构,⽽ malloc 则不会。例⼦如下:

    class A{ public:
    A() :a(1), b(1.11){}
    private:
    int a; double b;
    };
    int main(){A * ptr = (A*)malloc(sizeof(A));
    // A * ptr = new A;
    return 0;
    }
    

    return 处设置断点,观看 ptr 所指内存的内容:

    可以看出 A 的默认构造函数并没有被调⽤,因为数据成员 a,b 的值并没有得到初始化。⽽⽤ new 来分配对象是,可以看到 A 的默认构造函数被调⽤了。

  2. 对数组的处理

    C++提供了 new[]delete[] 来专门处理数组类型:

    A * ptr = new A[10];//分配10个A对象
    delete [] ptr; // 使⽤new[]分配的内存必须使⽤delete[]进⾏释放
    delete ptr; // 这种做法并不会出现内存泄漏,但是不建议⽤
    

    new 对数组的⽀持体现在它会分别调⽤构造函数函数初始化每⼀个数组元素,释放对象时为每个对象调⽤析构函数。注意 delete[] 要与 new[] 配套使⽤,不然会出现数组对象部分释放的现象,造成内存泄漏。

    ⾄于 malloc ,它并知道你在这块内存上要放的数组还是啥别的东西,反正它就给你⼀块原始的内存,在给你个内存的地址就完事。所以如果要动态分配⼀个数组的内存,还需要我们⼿动⾃定数组的⼤⼩:

    int * ptr = (int *) malloc( sizeof(int)* 10 );//分配⼀个10个int元素的数组
    
  3. new与malloc是否可以相互调⽤

    new 的实现(operator new /operator delete)可以基于 malloc,只是封装的更好,更安全。⽽malloc的实现不可以去调⽤new。

    void * operator new (sieze_t size){ // 可以看出 new 的实现依靠 malloc
    if(void * mem = malloc(size) return mem;
    else
    throw bad_alloc();
    }
    void operator delete(void *mem) noexcept{ free(mem);
    }
    
  4. 是否可以被重载

    opeartor new /operator delete 可以被重载。标准库是定义了operator new函数和operator delete函数的8个重载版本,我们有⾜够的⾃由去重载 operator new /operator delete ,以决定我们的 new/delete 如何为对象分配内存,如何回收对象。⽽ malloc/free 并不允许重载。

  5. 能否直观地重新分配内存

    使⽤ malloc 分配的内存后,如果在使⽤过程中发现内存不⾜,可以使⽤ realloc 函数进⾏内存重新分配实现内存的扩充。 realloc 先判断当前的指针所指内存是否有⾜够的连续空间,如果有,原地扩⼤可分配的内存地址,并且返回原来的地址指针;如果空间不够,先按 照新指定的⼤⼩分配空间,将原有数据从头到尾拷贝到新分配的内存区域,⽽后释放原来的内存区域。(类似于 vector 的扩容)。

    new 没有这样直观的配套设施来扩充内存。

  6. 客户处理内存分配不⾜

    operator new 抛出异常以反映⼀个未获得满⾜的需求之前,它会先调⽤⼀个⽤户指定的错误处理函数,这就是 new-handler ,这个函数程序员可以重新编写决定内存不⾜以分配时要⼲什么事。⽽ malloc ,客户并不能够去⾃定义编程决定内存不⾜以分配时要⼲什么事, 只能看着 malloc 返回 NULL

总结:malloc给你的就好像⼀块原始的⼟地,你要种什么需要⾃⼰在⼟地上来播种。⽽new帮你划好了⽥地的分块(数组),帮你播 了种(构造函数),还提供其他的设施供⽤户使⽤。在C++这种偏重OOP的语⾔,使⽤new/delete是更合适的。

new 的⽤法**

C++ 算法岗面经(未完待续)相关推荐

  1. 使用 Sharding-Jdbc 实现分库分表、读写分离(未完待续)

    先贴代码, 回头讲解.未完待续 规划 实现分库分表.读写分离. 准备两张表:svc_order, svc_order_item 结构如下,可以想象成是未实施分库分表之前的数据库结构: CREATE D ...

  2. 二叉树学习笔记(未完待续)

    摘要 二叉树学习笔记(未完待续). 博客 IT老兵驿站. 前言 昨天(2019-11-07)复习红黑树,发现红黑树和二叉树密不可分,所以这里再复习一下二叉树. 在大学的时候,这块我很认真地学习了一遍. ...

  3. 总结2019,规划2020,未完待续

    在这么一个生活富足.世界和平的年代时间总是那么短暂,2019也成为了过去式,2020新的一年也到来了,所以这是一篇总结文,写给迷茫的我自己,不知道自己要干什么的时候看看这篇博文,总结2019,展望20 ...

  4. 多标签学习之讲座版 (内部讨论, 未完待续)

    摘要: 多标签学习是一种常见的, 而并非小众的机器学习问题. 本贴为专题讲座准备. 1. 基本数据模型 定义1. 多标签数据为一个二元组: S=(X,Y),(1)S = (\mathbf{X}, \m ...

  5. 基础数学知识(未完待续)

    0x10 质数 质数基本定理 质数的定义:只被 1 1 1 和它本身整除的正整数叫做质数.非质数的正整数叫做合数.特别的, 1 1 1 既不是质数也不是合数. 质数的数量很少. 只有 2 2 2 是偶 ...

  6. 《今日简史》读书笔记(未完待续)

    <今日简史>读书笔记(未完待续) 这本书是尤瓦尔·赫拉利的简史三部曲的最后一本,前2本书是<未来简史>和<人类简史>.根据豆瓣上网友的评价,这本书是尤瓦尔·赫拉利写 ...

  7. linux引数列项目过长,Linux 命令个人总结====== 未完待续 个人认为比较重要

    Linux 命令个人总结====== 未完待续 man [功能说明]: 查看帮助 [语法格式]: man [123456789]命令.文件. [选项参数]: 数字"1"表示用户命令 ...

  8. CC2530学习路线-基础实验-串口通讯发送字符串(4 未完待续)

    目录 1. 前期预备知识 1.1 串口通讯电路图 1.2 实验相关寄存器 1.2 常用波特率设置 本章未完待续..... 原来写的文章已经丢失了,只能找到这一小部分,看什么时候有时间再补上. 1. 前 ...

  9. Paper之BigGAN:ICLR 2019最新论文《LARGE SCALE GAN TRAINING FOR HIGH FIDELITY NATURAL IMAGE SYNTHESIS》(未完待续)

    Paper之BigGAN:ICLR 2019最新论文<LARGE SCALE GAN TRAINING FOR HIGH FIDELITY NATURAL IMAGE SYNTHESIS> ...

  10. Windows x64内核学习笔记(五)—— KPTI(未完待续)

    Windows x64内核学习笔记(五)-- KPTI(未完待续) KPTI 实验一:构造IDT后门并读取Cr3 参考资料 KPTI 描述:KPTI(Kernel page-table isolati ...

最新文章

  1. Netty面试题 汇总
  2. python学习之面向对象(三)
  3. Acwing 1089. 烽火传递
  4. python 表情包 gif_几行 python 代码合成 gif / 微信表情~与恶意合成软件说再见【文末附代码】...
  5. Bootstrap两端对齐的按钮组
  6. java编写正则表达式引擎_从0到1打造正则表达式执行引擎(一)
  7. python简单例子lof_Python的净值数据接口调用示例分享
  8. 「拖放」Mac 的底层能力,也是效率神技
  9. 名称不存在或不是目录_大数据从入门到深入:LINUX 04 文档目录管理
  10. 《从0到1:CTFer成长之路》书籍配套题目-[第二章 web进阶]死亡ping命令
  11. PINN物理驱动的深度学习方法入门到详解
  12. java计算机毕业设计小小银动漫网站源代码+数据库+系统+lw文档
  13. IDEA报错:Error:java: JDK isn‘t specified for module ‘work-flow‘
  14. Plant Simulation中的“开机自启”-autoexec
  15. 超强总结,用心分享丨大数据超神之路(三):Linux必备知识
  16. 集成方法,或者叫做组合方法(Ensemble methods)介绍(一)
  17. php对plc的TCP通信,西门子1200PLC进行TCP通讯配置详解
  18. 做人要低调,绝对经典的低调
  19. (二十三)admin-boot项目之captcha验证码整合
  20. Java/Spring/Postman下载文件名称乱码处理

热门文章

  1. 哈希表哪家强?编程语言找你来帮忙!
  2. 如何从 Android 手机上的 SD 卡恢复已删除的照片
  3. python限制输入类型_《计算机二级Python语言程序设计考试》第3章:基本数据类型...
  4. 小程序获取手机号码解密偶尔会失败
  5. 算法交易VWAPTWAP
  6. Pandas数据归一化
  7. GoodNotes 5.7.12 中文版 手写笔记应用
  8. python报错系列一:TypeError: Descriptors cannot not be created directly.
  9. SolidUI的发展:不要身背历史包袱
  10. less命令乱码_less命令