C++内存分布及用法

目录
  • 一、内存基础
    • 1、内存分布
    • 2、栈区与堆区的区别
  • 二、内存分配
    • 1、内存分配方式
    • 2、new的用法
    • 3、delete用法
    • 4、new与malloc的区别
    • 5、内存泄漏
  • 三、内存拓展
    • 1、内存概念
    • 2、虚拟内存
  • 四、思考
    • 1、代码中的b属于栈区还是堆区?

一、内存基础

1、内存分布

通过下面一张图看看C++的内存分布:

栈区:由编译器自动分配与释放,存放为程序运行时函数分配的局部变量、函数参数;栈内存分配运算内置于处理器的指令集中,效率很高,但是分配内存的容量有限;

堆区:由newmalloc分配的内存块,释放由应用程序控制,不需要编译器释放;如果程序员没有对该内存进行释放,程序结束后系统自动回收,堆的地方比栈大很多;

静态区:存放的是static的静态变量和一些全局变量,特点是只读、大小固定;静态变量和全局变量的存储期是一起的,一旦静态区的内存被分配,要一直等到程序全部结束后才释放;

2、栈区与堆区的区别

1、分配方式不同:栈区系统分配系统回收;堆区由程序员手动申请,需要求程序员自行回收,如果没有回收,系统在程序结束后会进行回收,这种情况会造成内存泄漏;

2、生命周期不同:栈区生命周期是系统分配到系统回收,也就是在大括号内;堆区是从申请到释放;

3、效率不同:主要原因是地址空间是否连续,栈区地址空间是连续的,效率会高一些;堆区地址空间不连续,需要遍历链表才能找到最近的地址,效率会低一些;

4、内存碎片:堆区容易产生内存碎片,栈区不会;

5、生长方向不同:栈区申请空间的地址(表示地址的八个十六进制数)是从大到小的,堆区申请空间地址是从小到大的。栈区是先进后出的原则,类比栈结构的特点;

  • 栈区特点:更好的局部性,对象自动销毁;
  • 堆区特点:运行期动态扩展,需要显示释放;

注意点:申请的空间是在堆区,变量本身是在栈区!

二、内存分配

1、内存分配方式

可操作的内存分配:

  • 静态存储区分配:static静态变量、全局变量;
  • 栈上分配:局部变量;
  • 堆上分配:newmalloc进行内存分配;

不可操作的内存分配:

内核区、代码区、局部变量的分配也属于系统分配;

2、new的用法

C++中通常使用newdelete来构造和销毁对象;

使用new创建对象,返回的是对象的首地址,需要用指针接收:

int *y = new int(2);
std::cout << *y << std::endl;

对象的构建和销毁分为两步:分配内存、所分配内存上构造对象(销毁与之类似);

new的几种常见形式:

  • new int(2) :构造单一对象、new int[5]:构造数组;
  • nothrow new:标准库定义,解决内存分配失败异常的问题;
  • placement new:使用已经创建的内存,跳过分配内存;
  • new auto

3、delete用法

根据分配的是单一对象还是数组,采用相应的方式销毁;

int *y = new int[3];
delete[] y;

不能delete一个非new返回的内存(也就是栈内存);
delete nullptr是可被允许的;
同一块内存不能delete多次;

4、new与malloc的区别

  new不需要指定分配多大,malloc使用的时候必须指定大小;new的底层实现就是malloc,两者都必须释放内存,不否则容易造成野指针或内存泄漏。需要注意一点,释放内存后需设置相关指针为空指针;

总结:

  • 属性:new为关键字(编译器),malloc是库函数(需引入头文件);
  • 参数:new无需指定大小,malloc需指定大小;
  • 返回类型:new返回对象指针,malloc返回void*;
  • 对于自定义的类:new会调用构造和析构函数,malloc不会调用构造和析构函数;
  • 分配失败:new会抛出异常,malloc会返回空;

5、内存泄漏

是指由于疏忽或错误造成程序未能释放掉不再使用的内存的情况,内存泄漏并非指内存在物理上的小时,而是应用程序分配某段内存后,由于设计错误,失去该段内存的控制从而造成内存浪费;

可能的原因:

  • 1、申请后未释放(最常见)
  • 2、void* 指针的释放
  • 3、new[] 回收时没有用delete[] ,数组的回收要注意

三、内存拓展

1、内存概念

  计算机重要部件之一,是外存与CPU进行沟通的桥梁。计算机所有程序都是在内存运行的,因此内存的性能对计算机的影响非常大。内存也称为内存储器和主存储器,作用是暂时存放CPU的运算数据,以及与硬盘等外部存储器交换的数据;

寻址空间:保存内存地址的多少,通常我们说的4G内存,就表示计算机能保存2的32次方个地址,也就是能找到这些地址上的二进制信息;

寻址能力:每个地址里能存多少个bit,现在的计算机大多数是16位机器了;

2、虚拟内存

使得系统运行实际的内存空间比想象的大得多,虚拟内存是可以远大于物理内存的,同时主要为了使程序运行的时候可以不限制于只访问内存大小,可以通过虚拟内存地址去访问磁盘空间;

每一个进程虚拟内存都是独立的,独立的享有计算机的内存。虚拟内存地址的大小是与地址总线位数相关,物理内存地址的大小是与物理内存条的容量与磁盘容量相关。

四、思考

1、代码中的b属于栈区还是堆区?

void fun()
{
 int *b = new int[14];
}

b是在栈区的变量,由于b是一个局部变量,随着函数域 的结束被释放,不需要程序员自行释放,尽管b使用new进行初始化,还是可以认为分配在栈区;

总结:

本次系统的从内存的基础概念到内存分配进行了讲解,内存是我们开发中最重要的一部分,往往逻辑上的错误就会造成内存泄漏,导致程序无法运行。或者一些分配内存的方式不够细心,也会造成冗余内存的使用。在目前的很多嵌入式板子上,针对内存的接口是必备的,往往也都是基于malloc修改;
还有一点需要注意,不管任何机器上运行程序,操作的都是虚拟内存,内部通过页表定位到对应的物理内存。关于硬件方面的本质,如果做嵌入式端的话需要深入研究。

到此这篇关于C++内存分布及用法的文章就介绍到这了,更多相关C++内存内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 浅谈C++对象的内存分布和虚函数表

    c++中一个类中无非有四种成员:静态数据成员和非静态数据成员,静态函数和非静态函数. 1.非静态数据成员被放在每一个对象体内作为对象专有的数据成员. 2.静态数据成员被提取出来放在程序的静态数据区内,为该类所有对象共享,因此只存在一份. 3.静态和非静态成员函数最终都被提取出来放在程序的代码段中并为该类所有对象共享,因此每一个成员函数也只能存在一份代码实体.在c++中类的成员函数都是保存在静态存储区中的 ,那静态函数也是保存在静态存储区中的,他们都是在类中保存同一个惫份. 因此,构成对象本身的只

  • C++ 类中有虚函数(虚函数表)时 内存分布详解

    虚函数表 对C++ 了解的人都应该知道虚函数(Virtual Function)是通过一张虚函数表(Virtual Table)来实现的.简称为V-Table.在这个表中,主是要一个类的虚函数的地址表,这张表解决了继承.覆盖的问题,保证其容真实反应实际的函数.这样,在有虚函数的类的实例中这个表被分配在了这个实例的内存中,所以,当我们用父类的指针来操作一个子类的时候,这张虚函数表就显得由为重要了,它就像一个地图一样,指明了实际所应该调用的函数. 这里我们着重看一下这张虚函数表.C++的编译器应该是

  • 解析C++类内存分布

    工欲善其事,必先利其器,我们先用好Visual Studio工具,像下面这样一步一步来: 先选择左侧的C/C++->命令行,然后在其他选项这里写上/d1 reportAllClassLayout,它可以看到所有相关类的内存布局,如果写上/d1 reportSingleClassLayoutXXX(XXX为类名),则只会打出指定类XXX的内存布局.近期的VS版本都支持这样配置. 下面可以定义一个类,像下面这样: class Base { int a; int b; public: void Com

  • 浅析C/C++变量在内存中的分布

    C/C++变量在内存中的分布在笔试时经常考到,虽然简单,但也容易忘记,因此在这作个总结,以加深印象. 先写一个测试程序: 复制代码 代码如下: #include <stdio.h>  #include <malloc.h>  int g_i = 100;  int g_j = 200;  int g_k, g_h;  int main()  {      const int MAXN = 100;      int *p = (int*)malloc(MAXN * sizeof(i

  • C++内存管理详细解析

    目录 一.C++内存管理 1. new/delete表达式 2.new/delete重载 3.类内自定义allocator(per-class allocator) 二.多线程内存分配器 1.malloc/free 2.brk和mmap 三.补充知识 1.内存泄漏 2.malloc/free和new/delete的比较 3.RAII规则 一.C++内存管理 C++中有四种内存分配.释放方式: 最高级的是std::allocator,对应的释放方式是std::deallocate,可以自由设计来搭

  • C++对象内存分布详解(包括字节对齐和虚函数表)

    1.C++对象的内存分布和虚函数表: C++对象的内存分布和虚函数表注意,对象中保存的是虚函数表指针,而不是虚函数表,虚函数表在编译阶段就已经生成,同类的不同对象中的虚函数指针指向同一个虚函数表,不同类对象的虚函数指针指向不同虚函数表. 2.何时进行动态绑定: (1)每个类对象在被构造时不用去关心是否有其他类从自己派生,也不需要关心自己是否从其他类派生,只要按照一个统一的流程:在自身的构造函数执行之前把自己所属类(即当前构造函数所属的类)的虚函数表的地址绑定到当前对象上(一般是保存在对象内存空间

  • C++内存分布及用法

    目录 一.内存基础 1.内存分布 2.栈区与堆区的区别 二.内存分配 1.内存分配方式 2.new的用法 3.delete用法 4.new与malloc的区别 5.内存泄漏 三.内存拓展 1.内存概念 2.虚拟内存 四.思考 1.代码中的b属于栈区还是堆区? 一.内存基础 1.内存分布 通过下面一张图看看C++的内存分布: 栈区:由编译器自动分配与释放,存放为程序运行时函数分配的局部变量.函数参数:栈内存分配运算内置于处理器的指令集中,效率很高,但是分配内存的容量有限: 堆区:由new.mall

  • Java内存分布归纳整理详解

    Java内存分布:Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域:方法区.虚拟机栈.本地方法栈.堆.程序计数器. 1.程序计数器 程序计数器是一块较小的内存空间,它可以看做是当前线程所执行的字节码的行号指示器.在虚拟机的概念模型中,字节码解释器工作时就是通过改变这个计数器的值来选取吓一条需要执行的字节码指令. 分支.循环.跳转.异常处理.线程恢复等基础功能都需要依赖这个计数器来完成. 为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器

  • 再也不用怕! 让你彻底搞明白Java内存分布

    一.堆内内存 堆内内存分为三大部分,年轻代 , 老年代 和 元空间,所以 堆内内存 = 年轻代 + 老年代 + 元空间,下面细聊下三部分 1.1 年轻代-Young Generation 存放的是new 生成的对象 年轻代是为了尽可能快速的回收掉那些生命周期短的对象 Eden 大部分对象在Eden区中生成 当Eden区满时,会做一次young gc, 依然存活的对象将被复制到Survivor区, 当一个Survivor 区满时, 此区的存活对象将被复制到另外一个Survivor区 Survivo

  • C++虚函数表与类的内存分布深入分析理解

    目录 不可定义为虚函数的函数 将析构函数定义为虚函数的作用 虚函数表原理 继承关系中虚函数表结构 多重继承的虚函数表 多态调用原理 对齐和补齐规则 为什么要有对齐和补齐 资源链接 不可定义为虚函数的函数 类的静态函数和构造函数不可以定义为虚函数: 静态函数的目的是通过类名+函数名访问类的static变量,或者通过对象调用staic函数实现对static成员变量的读写,要求内存中只有一份数据.而虚函数在子类中重写,并且通过多态机制实现动态调用,在内存中需要保存不同的重写版本. 构造函数的作用是构造

  • java内存分布实现代码

    目录 一.堆内内存 1.1 年轻代-Young Generation 1.2 老年代 (Old Generation) 1.3 元数据(Meta space) 1.4 小结 二.堆外内存 2.1 java中在堆外开辟内存的方法有两种 2.2 使用堆外内存的优点 2.3堆外内存的缺点 三.垃圾回收 3.1 垃圾回收(GC) 3.2 GC root 3.3常用垃圾回收器 四.总结 一.堆内内存 堆内内存分为三大部分,年轻代 , 老年代 和 元空间,所以 堆内内存 = 年轻代 + 老年代 + 元空间,

  • C++浅析程序中内存的分布

    C++之程序的内存分布 最近在复习C++相关的知识,整理一下. C++的存储区主要有以下几类: 栈区:就是那些由编译器在需要的时候分配,在不需要的时候自动清楚的变量的存储区.里面的变量通常是局部变量.函数参数等. 堆区:就是那些由new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个delete.如果程序员没有释放掉, 那么在程序结束后,操作系统会自动回收.只new不delete会造成内存泄漏. 全局/静态存储区:全局变量和静态变量(static修饰的变量

随机推荐