c语言动态分配内存,c++的动态内存分配

  c语言动态分配内存,c++的动态内存分配

  Yyds干货库存

  在写之前,我们知道C支持C语言,也就是说C语言中的malloc等函数都可以在C中使用,但是C支持另外两个关键字,非常有用。我们需要看看C的动态内存。

  C/C内存分配我记得,我刚认识C的时候,跟大家分享过程序虚拟地址空间的概念。无论是C的nalloc函数,还是我们现在要分享的new,都是在堆区打开空间,这是我们首先要记住的。

  C语言内存管理模式C语言是通过函数动态内存开发的。标准库中有三个函数,这里就不赘述了。大家应该都知道吧。我就看看用法吧。

  #包含stdio.h

  #include assert.h

  int main()

  {

  //malloc开放空间未初始化

  int * P1=(int *)malloc(sizeof(int)* 4);

  断言(P1);

  //calloc开放空间初始化为0

  int* p2=(int*)calloc(4,sizeof(int));

  断言(p2);

  //附加空间

  p1=(int*)relloc(p1,sizeof(int)* 8);

  免费(P1);

  免费(p2);

  返回0;

  }

  C内存管理模式C支持C语言,也就是说C可以使用这些函数。不过除了这些函数,C还增加了两个关键字,new和delete,分别针对malloc/calloc和free,C的模式比C的好。

  为什么C增加了新的又删除了?众所周知,C语言的结构中是不支持函数的,于是大佬们提出了类的概念,类出现了,怕自己有时候会忘记初始化和清空内存,于是出现了构造函数和析构函数,让编译器自动调用。可以说,所有事物的出现都是为了让我们更好地使用语言,新增和删除也是如此。C语言开辟动态内存比较麻烦,对于自动类型来说很重要。

  我们还发现了一个很直接的问题。我们每打开一个空间,都要强制进行类型转换,还需要判断内存是否打开。这个太麻烦了,不过这种事情在new不会发生。如果没有打开,编译器会抛出异常,所以我们不需要自己手动检查。

  新对象。这样我先给你看内置类型和自定义类型,我准备和malloc对比一下。

  #包括iostream

  使用命名空间std

  int main()

  {

  int * p1=new int

  * p1=10

  cout * p1 endl

  返回0;

  }

  我们知道,在C中,内置的类行也是作为一个类使用的,我们可以在它是新的时候初始化它。

  int main()

  {

  int * p=new int(0);

  cout * p endl

  返回0;

  }

  数组更简单,我们可以直接写。

  int main()

  {

  int * p=new int[10];//新建10个int类行的空间

  返回0;

  }

  我们也可以在新的空间中实例化,但是实例化应该被显示。

  int main()

  {

  int* p=new int[10]{1,2,3 };

  返回0;

  }

  您可能会发现,删除不会释放任何空间,这将导致内存泄漏。这里我们用另一个关键字,delete,更简单。

  您可能对delete[]感到困惑。在这里,我们可以记住它。如果要清空数组的空间,最好用这个方法。也许对于内置的类行,您也可以使用delete,但是对于自定义的类行,您可能会报告一个错误。这里,我以后再说。

  int main()

  {

  int * p1=new int

  int* p2=new int[10]{1,2,3 };

  删除P1;

  删除[]p2;

  返回0;

  }

  Malloc new我们需要对比一下Malloc和new的区别,这样才能知道C为什么这么喜欢new。

  内置类型,先下结论吧。除了错误报告之外,这两个内置类行之间没有任何区别,它们都不会被该行初始化。这里就不说报错、异常、失败的信息和大家分享了。

  int main()

  {

  int * P1=new int[10];

  int * p2=(int *)malloc(sizeof(int)* 10);

  断言(p2);

  删除[]P1;

  免费(p2);

  返回0;

  }

  自定义类型对于自定义类型来说,两者有很大的区别。

  先备课吧。

  A级

  {

  公共:

  A(int a=0,int b=0)

  :_a(一)

  ,_b(b)

  {

  cout“constructor”endl;

  }

  ~A()

  {

  Cout“析构函数”endl

  }

  私人:

  int _ a;

  int _ b;

  };

  Malloc直接打开空间,里面的构造函数不会被调用,析构函数在空闲的时候也不会被调用。

  int main()

  {

  a* aa=(a*)malloc(sizeof(A));

  免费(aa);

  返回0;

  }

  而new和delete分别调用构造函数和析构函数来完成初始化。

  int main()

  {

  A* aa=新A;

  删除aa;

  返回0;

  }

  操作符new和操作符delete函数在这里看起来像new和delete的重载。记住,这不是,或者名字有点怪。这是C中的一个全局函数,使用方法和malloc一样,功能一样。它不会调用构造函数和析构函数。

  和新增删除是用户申请和释放动态内存的操作符,新增操作符和删除操作符是系统提供的全局功能。New调用底部的操作符new全局函数申请空间,delete通过底部的操作符delete全局函数释放空间。

  int main()

  {

  A* aa=(A*)算子new(sizeof(A));

  操作员删除(aa);

  返回0;

  }

  根据原理源代码可以发现,其实运算符new和运算符delete函数本质上都是malloc和free的封装,就是错误信息有点不一样,封装的错误信息是异常的。

  运算符new的原理是malloc

  void *__CRTDECL运算符new(size _ t size)_ throw 1(_ STD bad _ alloc)

  {

  //尝试分配大小字节

  void * p;

  while ((p=malloc(size))==0)

  if (_callnewh(size)==0)

  {

  //报告没有内存

  //如果内存应用失败,这里会抛出bad_alloc类型异常。

  static const STD:bad _ alloc nomem;

  _RAISE(诺姆);

  }

  返回(p);

  }

  运算符删除原则

  void运算符删除(void *pUserData)

  {

  _ CrtMemBlockHeader * pHead

  RTCCALLBACK(_RTC_Free_hook,(pUserData,0));

  if (pUserData==NULL)

  返回;

  _ mlock(_ HEAP _ LOCK);/*阻止其他线程*/

  _ _尝试

  /*获取指向内存块头的指针*/

  pHead=pHdr(puser data);

  /*验证块类型*/

  _ ASSERTE(_ BLOCK _ TYPE _ IS _ VALID(pHead-nBlockUse));

  _free_dbg(pUserData,pHead-nBlockUse);//注意,C语言中的free就是这个函数

  _ _最后

  _ munlock(_ HEAP _ LOCK);/*释放其他线程*/

  _ _ END _ TRY _最终

  返回;

  }

  为什么会出现这两种功能?这两个函数不是为我们调用的,而是为new的底层调用的。我们的new对象相当于call operator new和call object的构造函数,这也是它们出现的原因。

  可以拆解一下看看。

  删除[]我们可以这样理解。对于内置类型,不需要讨论,功能都差不多。但是对于自定义类型,有一个很大的问题。

  在释放的对象空间上执行n次析构函数,完成n个对象中的资源清理,调用operator delete[]释放空间,在operator delete[]中实际调用operator delete释放空间。先看结果吧。

  Delete[]析构相应的次数

  int main()

  {

  A* aa=新A[3];

  删除[]aa;

  返回0;

  }

  删除一次析构函数,就会报错。

  int main()

  {

  A* aa=新A[3];

  删除aa;

  返回0;

  }

  在这里,我想介绍一个内存池的概念。我们都知道malloc和new正在堆上创建空间。如果我们多次创造空间,效率会不会有点慢?想想吧。我们创建一次,打开几千次,每次都申请。我们在想,是否可以单独划分出一个区域,提供我们想要创造空间的物件。这就是内存池的最初想法。你可能会困惑。我们可以做这样的类比。堆积就像你在学校吃的每一吨饭,每一餐都要向你父亲要钱。然后内存池就像月初直接问你爸要这个月的生活费,一个月一次,肯定是后者效率更高。

  那么我们如何使用内存池呢?标准库中也提供了一个。这里,我们需要在类中重写operator new和operator delete函数。先来了解一下用法。我们先不细说。后面可能会有高并发内存池项目给大家分享,不过这个时间有点长。

  结构列表节点

  {

  ListNode * _ next

  ListNode * _ prev

  int _ data

  //申请空间的是回内存池。

  void*运算符new(size_t n)

  {

  void * p=nullptr

  p=分配器列表节点()。分配(1);

  cout 内存池分配 endl

  返回p;

  }

  void运算符删除(void* p)

  {

  分配器列表节点()。解除分配((ListNode*)p,1);

  cout 内存池解除分配 endl

  }

  };

  根据考试成绩分等级排列的投考者的名单

  {

  公共:

  列表()

  {

  _head=新列表节点;

  _ head-_ next=_ head;

  _ head-_ prev=_ head;

  }

  ~列表()

  {

  ListNode * cur=_ head-_ next;

  而(cur!=_head)

  {

  ListNode * next=cur-_ next;

  删除cur

  cur=next

  }

  delete _ head

  _ head=nullptr

  }

  私人:

  ListNode * _ head

  };

  int main()

  {

  列表L1;

  返回0;

  }

  定位new我们已经知道使用运算符new创建的空间不会被初始化,现在也不能通过对象显式调用构造函数,也就是说如果把成员变量修改为,肯定会破包。不过,C在这里也提供了一种定位new的技术来帮助我们再次实例化。我们先来看看用法。

  A级

  {

  公共:

  A(int a=0)

  :_a(一)

  {

  }

  私人:

  int _ a;

  };

  int main()

  {

  A* a=(A*)算子new(sizeof(A));

  //定位新的

  新(一)一(1);

  返回0;

  }

  从这里我们可以知道,定位新有以下两种用法。

  New(要初始化的指针)引用对应的类行,直接调用默认构造函数new(要初始化的指针)。指针引用对应的类行(构造函数要传递的参数),调用对应的构造函数。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

相关文章阅读

  • c语言调用退出函数 c语言退出整个程序怎么写
  • c语言中怎么给函数初始化 c语言的初始化语句
  • c语言编写函数计算平均值 c语言求平均函数
  • 详解c语言中的字符串数组是什么,详解c语言中的字符串数组结构,详解C语言中的字符串数组
  • 表达式求值c++实现,c语言实现表达式求值
  • 看懂c语言基本语法,C语言详解,C语言的基本语法详解
  • 用c语言实现快速排序算法,排序算法设计与实现快速排序C语言,C语言实现快速排序算法实例
  • 深入解析c语言中函数指针的定义与使用方法,深入解析c语言中函数指针的定义与使用情况,深入解析C语言中函数指针的定义与使用
  • 描述E-R图,E-R图举例,关于C语言中E-R图的详解
  • 折半查找法C语言,折半查找算法(算法设计题)
  • 折半查找法C语言,c语言折半法查找数据,C语言实现折半查找法(二分法)
  • 扫雷小游戏c++代码设计,c语言扫雷游戏源代码,C语言实现扫雷小游戏详细代码
  • 怎样统计程序代码行数,C语言统计行数,C#程序员统计自己的代码行数
  • 基于c语言的贪吃蛇游戏程序设计,用c语言编写贪吃蛇游戏程序,C语言实现简单的贪吃蛇游戏
  • 图的两种遍历算法,图的遍历算法代码c语言,Python算法之图的遍历
  • 留言与评论(共有 条评论)
       
    验证码: