CSDN首页 空间 新闻 论坛 Blog 下载 读书 网摘 搜索 .NET Java 视频 接项目 求职 在线学习 买书 程序员 通知
可用分押宝游戏火热进行中... 专题改版:Java Web 专题
CSDN社区
搜索 收藏 打印 关闭
CSDN社区 >  C/C++ >  C++ 语言

使用delete[]时,编译器是怎么知道需要回收的内存单元的长度的?

楼主leonlux(堂郎)2004-12-01 19:42:20 在 C/C++ / C++ 语言 提问

int   main    
  {  
          int   *p   =   new   int[10];  
          //do   some   work   with   p  
          delete   []p;  
  }  
  指针p应该没有保存任何其所指内存单元长度的信息,  
  但编译器怎么保证运行时要   delete   10个int单元呢? 问题点数:100、回复次数:20Top

1 楼insulator(天外来客)回复于 2004-12-01 19:45:14 得分 5

编译器内部自己保存了Top

2 楼iicup(双杯献酒)回复于 2004-12-01 19:47:01 得分 0

在new的时候把长度记下了。  
  这意味着,每次new对应一次delete  
  而不能一次new,分几次delete  
   
  //   正确  
  char*   p   =   new   char[3];  
  delete[]   p;    
   
  //   错误(不能分三次delete)  
  char*   p   =   new   char[3];  
  delete   p+0;  
  delete   p+1;  
  delete   p+2;  
   
  Top

3 楼snow810211(阳光)回复于 2004-12-01 19:47:44 得分 0

指针p应该没有保存任何其所指内存单元长度的信息,  
  但编译器怎么保证运行时要   delete   10个int单元呢?  
   
  回复  
  ------我觉得指针p,已经指向了一个大小为10个int的单元,能记录,所以delete的时候,你用delete[]p能够释放10个int的单元,而delete   p仅仅是释放p所指的单元。Top

4 楼ufool()回复于 2004-12-01 19:49:35 得分 5

是的,在内存中保存了“10”,其位置大概在p所指向地址前几个字节处。我是以前在VC6中试的。Top

5 楼autoegg(哲学指引生活 && (动心忍性,增益其所不能))回复于 2004-12-01 20:04:48 得分 0

很明显是在分配的时候记录下了所分配的长度。Top

6 楼carylin(林石)回复于 2004-12-01 20:07:01 得分 0

编译器有记录.Top

7 楼leonlux(堂郎)回复于 2004-12-01 20:07:58 得分 0

那么这样呢?  
  int   main    
  {  
          int   *p   =   new   int[10];  
          int   *q   =   p;  
          //do   some   work   with   p  
          delete   []q;//这里改成q了  
  }  
  q是不是也保存了长度信息?  
  Top

8 楼leonlux(堂郎)回复于 2004-12-01 21:19:27 得分 0

还有就是,如果p真的保存了长度,那么编译器是怎么来实现的呢?Top

9 楼cenlmmx(学海无涯苦作舟)回复于 2004-12-01 22:52:07 得分 0

就在分配空间的前面的几个字节里记录了分配的长度Top

10 楼lbaby(春天来了...)回复于 2004-12-01 23:08:20 得分 0

 
  好像它的记录的名字叫cookieTop

11 楼notdefined(未定义)回复于 2004-12-01 23:18:39 得分 0

印象中,这个东西好象和编译器没多大关系。  
  内存的分配和回收,由操作系统进行的。每分配一块内存,就有一个叫做内存块描述符(好象是这名字)的结构定义内存的大小、位置、访问控制等一系列的东西。这样当应用程序将该内存块标记为废弃的时候,操作系统就会根据块描述符的描述对内存进行回收。  
  大体意思是这样吧,操作系统原理一类的书应该都有阐述,我是看过就忘,寒……Top

12 楼oo(为了名副其实,努力学习oo技术ing)回复于 2004-12-01 23:22:59 得分 10

分配内存的时候会记录分配内存的长度的,  
  这个信息在哪里跟实现有关,  
  vc6里,是放在p前的8个字节里。Top

13 楼OverlordBlind(OverlordBlind)回复于 2004-12-01 23:53:37 得分 10

个编译器做法不尽相同,原理差不多  
  你要n字节,编译器替你在   q   位置分了   n   +   a   字节  
  在a个字节中记录n,然后返回p   =   q   +   a作为给你的首地址  
  delete   p时到q   =   p   -   a位置读取块大小,销毁之Top

14 楼whoho(在北方流浪)回复于 2004-12-02 02:53:36 得分 0

编译器不会知道的  
  操作系统知道,动态内存由系统进行记录Top

15 楼pandengzhe(无为之为 之 混迹苍生)回复于 2004-12-02 08:20:24 得分 0

编译器分配回收Top

16 楼redjackwong(隔壁老汪)回复于 2004-12-02 08:55:22 得分 0

运行的时候自动记录了,因为delete   [   ]只能回收new   sign[int]分配的内存,所以运行时遇到new就保存那个int就行了。  
   
  另外,new是动态分配内存,编译器不可能在编译时知道大小,所以不是在编译时记录下的。Top

17 楼beyondtkl(大龙驹<*好久没来了,兄弟们好吧。*>)回复于 2004-12-02 09:06:09 得分 10

是呀    
  new的时候   在最前头的内存有保存本次分配的字节数   这就是所谓的   memory   cookie...  
   
  ^_^Top

18 楼kobefly(科比--网络学习中)回复于 2004-12-02 09:14:50 得分 30

这里是delete底层实现的一些知识  
  看一下对你的理解会有帮助  
  一般说,如果      
  int     *tmp1     =     new     int(2);      
  int     *tmp2     =     new     int[10];      
     
  那么:      
  delete     tmp1;      
  delete     []tmp2;      
     
  问题是:      
  1.delete     如何识别     tmp2     指向的数组有10个元素?      
  2.如果     delete     []tmp1     会导致“不可预测的结果”(Effective     C++语),但是这个不可预测一般会是什么结果?是与     tmp     的物理地址相邻的内存对应的对象的破坏吗?      
     
     
  ---------------------------------------------------------------      
     
  说一下我的猜测,期待高手。第一个问题,系统在分配内存时都有一个叫做“小甜饼”的东东,是不是可以通过它来确认内存是怎么使用的。第二个问题,既然是不可预测的那就肯定不一定是与它相邻的对象被破坏,是不是会把与它相邻的内存的内容作为指针去释放啊。      
  ---------------------------------------------------------------      
     
  To:楼上      
  不一定。:)      
  因为有些东西C++标准并没有规定编译器怎么实现,C++标准只规定了在正确使用语法时应该达到的效果。所以对于没有规定的东西,由于各家编译器厂商可以尝试不同的处理方案,导致了结果“不可预测”。      
  所以,我认为“不可预测”的意思就是这个意思。在同一种编译器上,比如说VC,我想许多东西应该还是可以预测的,比如说没有任何异常或者跳出一个错误对话框,这种“预测”也只是经验性的。      
  ---------------------------------------------------------------      
     
  以下来自http://www.csdn.net/develop/read_article.asp?id=19569      
     
  VC6下面,就会在分配的内存块其实位置放一个Cookie,,当进行delete的时候,指针前移4个字节,读出内存大小size,然后释放size+4的空间,我们可以用下面的小程序进行简单的测试:      
  #include     <iostream>          
      using     namespace     std;          
      class     A          
      {          
      public:          
         A()     {cout<<"A"<<endl;}      
             int     i;      
              ~A()     {cout<<"~A"<<endl;}      
              };      
              int     main()          
              {          
       A*     pA=new     A[5];      
               int*     p=(int*)pA;          
       *(p-1)=1;          
       delete     []pA;          
       return     0;          
              }          
     
  ---------------------------------------------------------------      
     
  好像是在分配内存时,在分配的内存块前面保留了几个字节的空间用来保存其分配大小的      
  ---------------------------------------------------------------      
     
  Top

19 楼kobefly(科比--网络学习中)回复于 2004-12-02 09:15:00 得分 30

寄件者:     "Peng     Chunhua"     <chpeng@psh.com.cn>      
  传送日期:     2002年9月12日     AM     11:00      
  主旨:     关於     [池内春秋]     文章的一个问题      
     
  To:侯先生      
     
  您好,一直对先生的文章很感兴趣,经常于谈笑中分析问题,让人茅塞顿开,获益非浅。谢谢。      
     
  先生在最近一期的《程序员》杂志(2002年9期)上发表了一篇文章[池内春秋]介绍了Memory     Pool的技术。不过,不知道什麽原因,在《程序员》上一定要分上下篇发表。所以目前还不太清楚Memory     Pool的实质内容。不过,在上篇中关於[空间上的额外开销]和[速度上的额外开销]的说法,本人有点疑问,望先生指教。      
     
  一,空间上的额外开销      
  先生认为在C++平台提供的内存配置工具中会带来额外的配置,即Cookie。并进行了明证。      
     
  其实我认为这个证明是有误的,首先VC6和C++Builder在内存管理上就是不一样的机制。通过分析,我发现new在底层肯定是调用malloc的(在C++平台上)。所以,只要分析一下malloc的实现机制就可以了。在VC6.0上,如果用Debug版的话,就和先生说的一样,有额外开销,基本上是32个字节,用来记录内存链表和分配内存的源文件名,行号,字节数,第几次分配的一些信息。这也就是VC6可以在Debug版可以检查内存泄漏的原因。具体叁考一下malloc的Debug版实现的代码就可以分析出来的。在VC6.0的Release版上,就没有这些额外记录了,malloc的实现是直接调用HeapAlloc的,释放也是直接调用HeapFree的,VC本身并没有对内存进行任何多馀的操作。所以在Release版中malloc的返回值等於HeapAlloc的值,而Debug中malloc的值就等於HeapAlloc()     +     0x20     的值。至於内存的大小,在Release版中也是直接通过HeapSize得到的。并且在VC中同一个模块中所有的malloc,     new都是在一个Heap上操作的,在VC的运行代码中是_crtheap。这也就是为什麽在申请10000000个C1的对象时VC比C++Builder中花费的时间比较长的一个原因。不过,在Release版中,申请内存的头部确实有32个字节记录了内存的一些信息,比如大小。但该Cookie和C++平台无关,也就是说,所有用HeapAlloc分配的内存在头部都有32个字节的额外开销。(好像不止32个字节,并且在申请内存的末尾也有一些标记)      
     
  在C++Builder中管理内存和VC中是不一样的。C++Buider中不用Heap进行内存的分配,而是自身通过TMemoryManager进行内存管理。具体方式是通过VirtualAlloc一次分配16KByte字节,当程序通过malloc分配内存时,C++Builder就遍历自身管理的内存,将空闲的内存进行分配,并在头部记录内存的大小。当内存不足时,再一次调用VirtualAlloc向系统申请内存,由C++Builder进行分配管理。所以,先生在空间之明证上说明C++Builder表现很好。      
     
  二,速度之明证      
  前面比较了VC6.0和C++Builder的内存管理上的不同,那麽,对於在速度上VC和C++Builder的差异也就不难理解了。VC中的内存分配在一个模块中都是在_crtheap上分配的,当再次申请内存时,VC中必须由系统Heap遍历整个Heap区进行申请。而C++Builder只需首先遍历自身管理的VirtualAlloc链表,发现VirtualAlloc中有空闲内存时再遍历该VirtualAlloc的内存区域。这样由於一个VirtualAlloc对应了很多malloc的内存,在遍历整个内存的时间上就比VC快了。比如:一次VirtualAlloc的内存可以管理m个malloc,那麽对已经分配了n*m个内存而言,再分配一个内存的花费为:      
  VC     =     n*m      
  C++Builder=n+m      
  同样可以推断:如果C++Builder在管理内存上一次VirtualAlloc的大小不是16K(0x4000)而是更大,在这种分配1000000个内存时速度应该更快一点。(不过,效果估计不明显)根据上面对C++Builder的内存管理分析,也就可以理解为什麽在C++Builder的程序中会出现经过一系列内存申请和释放後,通过TaskManager观察内存使用量并不一定回到执行这些操作之前了。因为VirtualAlloc中的某一块内存被使用的话,即使其他内存都被释放了,C++Builder也不能将该内存提交给系统释放。      
     
  对於GCC的编译器我没有研究过,在此不敢发表看法。      
   
     
  #include     <iostream>      
  #include     <string>      
  #include     <cmath>      
     
  using     namespace     std;      
     
  const     double     PRECISION     =     1E-6;      
  const     int     COUNT_OF_NUMBER         =     4;      
  const     int     NUMBER_TO_BE_CAL     =     24;      
     
  double     number[COUNT_OF_NUMBER];      
  string     expression[COUNT_OF_NUMBER];      
     
  bool     Search(int     n)      
  {      
                if     (n     ==     1)     {      
                                if     (     fabs(number[0]     -     NUMBER_TO_BE_CAL)     <     PRECISION     )     {      
                                                cout     <<     expression[0]     <<     endl;      
                                                return     true;      
                                }     else     {      
                                                return     false;      
                                }      
                }      
     
                for     (int     i     =     0;     i     <     n;     i++)     {      
                                for     (int     j     =     i     +     1;     j     <     n;     j++)     {      
                                                double     a,     b;      
                                                string     expa,     expb;      
     
                                                a     =     number[i];      
                                                b     =     number[j];      
                                                number[j]     =     number[n     -     1];      
     
                                                expa     =     expression[i];      
                                                expb     =     expression[j];      
                                                expression[j]     =     expression[n     -     1];      
     
                                                expression[i]     =     '('     +     expa     +     '+'     +     expb     +     ')';      
                                                number[i]     =     a     +     b;      
                                                if     (     Search(n     -     1)     )     return     true;      
                                                     
                                                expression[i]     =     '('     +     expa     +     '-'     +     expb     +     ')';      
                                                number[i]     =     a     -     b;      
                                                if     (     Search(n     -     1)     )     return     true;      
                                                     
                                                expression[i]     =     '('     +     expb     +     '-'     +     expa     +     ')';      
                                                number[i]     =     b     -     a;      
                                                if     (     Search(n     -     1)     )     return     true;      
                                                                                                     
     
                                                expression[i]     =     '('     +     expa     +     '*'     +     expb     +     ')';      
                                                number[i]     =     a     *     b;      
                                                if     (     Search(n     -     1)     )     return     true;      
     
                                                if     (b     !=     0)     {      
                                                                expression[i]     =     '('     +     expa     +     '/'     +     expb     +     ')';      
                                                                number[i]     =     a     /     b;      
                                                                if     (     Search(n     -     1)     )     return     true;      
                                                }          
                                                if     (a     !=     0)     {      
                                                                expression[i]     =     '('     +     expb     +     '/'     +     expa     +     ')';      
                                                                number[i]     =     b     /     a;      
                                                                if     (     Search(n     -     1)     )     return     true;      
                                                }      
     
                                                number[i]     =     a;      
                                                number[j]     =     b;      
                                                expression[i]     =     expa;      
                                                expression[j]     =     expb;      
                                }      
                }      
                return     false;      
  }      
     
  void     main()      
  {      
                for     (int     i     =     0;     i     <     COUNT_OF_NUMBER;     i++)     {      
                                char     buffer[20];      
                                int         x;      
                                cin     >>     x;      
                                number[i]     =     x;      
                                itoa(x,     buffer,     10);      
                                expression[i]     =     buffer;      
                }      
     
                if     (     Search(COUNT_OF_NUMBER)     )     {      
                                cout     <<     "Success."     <<     endl;      
                }     else     {      
                                cout     <<     "Fail."     <<     endl;      
                }                                      
  }      
  Top

20 楼kobefly(科比--网络学习中)回复于 2004-12-02 09:15:47 得分 0

对了,上边的知识摘自FAQ,   只是转载,呵呵Top

相关问题

  • Unix下的编译器,支持c++吗,比如new,delete?
  • MIB编译器!
  • java编译器
  • java编译器
  • c编译器
  • jsp编译器
  • VB编译器
  • 编译器
  • CSC编译器
  • 编译器开发!

关键词

  • c++
  • c++builder
  • 《程序员》
  • 内存
  • 编译器
  • 指针
  • vc
  • 管理
  • 先生
  • 操作系统

得分解答快速导航

  • 帖主:leonlux
  • insulator
  • ufool
  • oo
  • OverlordBlind
  • beyondtkl
  • kobefly
  • kobefly

相关链接

  • C/C++ Blog
  • C/C++类图书
  • C/C++类源码下载

广告也精彩

反馈

请通过下述方式给我们反馈
反馈
提问
网站简介|广告服务|VIP资费标准|银行汇款帐号|网站地图|帮助|联系方式|诚聘英才|English|问题报告
世纪乐知(北京)网络技术有限公司 版权所有, 京 ICP 证 020026 号
北京创新乐知广告有限公司 提供技术支持
Copyright © 2000-2007, CSDN.NET, All Rights Reserved
GongshangLogo