⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 第20章 指针二 为指针分配和释放空间.htm

📁 用非常通俗的语言介绍了C++和C
💻 HTM
📖 第 1 页 / 共 5 页
字号:
          <TD width="52%">//<SPAN lang=zh-cn>释放空间:</SPAN> 
            <P>//<SPAN lang=zh-cn>指向连续空间的指针,必须使用</SPAN>delete [] <SPAN 
            lang=zh-cn>来释放</SPAN></P>
            <P>delete [] p;</P></TD></TR></TBODY></TABLE>
      <P> </P>
      <P>关于指针本身的<SPAN lang=en-us> </SPAN>+ 和 - 操作,请复习上一章相关内容。</P>
      <P> </P>
      <P>接下来的问题也很重要。</P>
      <P> </P>
      <H3><B><A name=20.4>20.4</A><SPAN lang=zh-cn> </SPAN>delete/delete[] <SPAN 
      lang=zh-cn>的两个注意点</SPAN></B></H3>
      <P><SPAN lang=zh-cn>指针通过</SPAN> new <SPAN lang=zh-cn>或</SPAN> new[] <SPAN 
      lang=zh-cn>,向系统“申请”得到一段内存空间,我们说过,这段内存空间必须在不需要将它释放了。有点像人类社会的终极目标“共产主义”下的“按需分配”。需要了就申请,不需要了,则主动归还。</SPAN></P>
      <P> </P>
      <P><SPAN 
      lang=zh-cn>现在问题就在于这个“主动归还”。当然,指针并不存在什么思想觉悟方面的问题,说光想申请不想归还。真正的问题是,指针在某些方面的表现似乎有些像“花心大萝卜”。请看下面代码,演示令人心酸的一幕。</SPAN></P>
      <P> </P>
      <P><SPAN lang=zh-cn>/*</SPAN></P>
      <P><SPAN lang=zh-cn>&nbsp; 初始化 p</SPAN>&nbsp; ----- p <SPAN 
      lang=zh-cn>的新婚</SPAN></P>
      <P><SPAN lang=zh-cn>&nbsp; 通过</SPAN> new <SPAN 
      lang=zh-cn>,将一段新建的内存“嫁给”指针p</SPAN></P>
      <P><SPAN lang=zh-cn>&nbsp; 这一段分配的内存,就是p的原配夫妻</SPAN></P>
      <P>*/</P>
      <P><B>int* p = new int[100];&nbsp; </B></P>
      <P> </P>
      <P> </P>
      <P>/<SPAN lang=zh-cn>*</SPAN></P>
      <P><SPAN lang=zh-cn>&nbsp;&nbsp; 使用 p&nbsp; ----- 恩爱相处</SPAN></P>
      <P><SPAN lang=zh-cn>&nbsp;&nbsp; N 多年恩爱相处,此处略去不表</SPAN></P>
      <P>*/</P>
      <P><SPAN lang=zh-cn>……</SPAN></P>
      <P> </P>
      <P><SPAN lang=zh-cn>/</SPAN>*</P>
      <P>&nbsp;&nbsp; p <SPAN lang=zh-cn>改变指向</SPAN> ---- <SPAN 
      lang=zh-cn>分手</SPAN></P>
      <P>*/</P>
      <P> </P>
      <P><B>int girl [100];&nbsp;&nbsp; //<SPAN lang=zh-cn>第三者出现</SPAN></B></P>
      <P><B>p = girl;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //p <SPAN 
      lang=zh-cn>就这样指向 </SPAN>girl</B></P>
      <P></P>
      <P> </P>
      <P>/*</P>
      <P>&nbsp;&nbsp; delete [] p ---- <SPAN 
      lang=zh-cn>&nbsp;落幕前的灾难</SPAN>&nbsp; </P>
      <P> </P>
      <P>&nbsp;&nbsp; <SPAN lang=zh-cn>终于有一天,p老了,上帝选择在这一时刻</SPAN></P>
      <P><SPAN lang=zh-cn>&nbsp;&nbsp; 惩罚他</SPAN></P>
      <P>*/</P>
      <P> </P>
      <P><B>delete [] p;</B></P>
      <P> </P>
      <P><SPAN 
      lang=zh-cn>扣除注释,上面只有4行代码。这4行代码完全符合程序世界的宪法:语法。也就是说对它们进行编译,编译器会认为它们毫无错误,轻松放行。</SPAN></P>
      <P> </P>
      <P><SPAN lang=zh-cn>但在灾难在</SPAN> delete [] p <SPAN 
      lang=zh-cn>时发生。</SPAN></P>
      <P><SPAN lang=zh-cn>我们原意是要释放 p 最初通过 </SPAN>new int[100]<SPAN 
      lang=zh-cn>而得到的内存空间,但事实上,p那时已经指向</SPAN>girl[100]<SPAN 
      lang=zh-cn>了。结果,第一、最初的空间并没有被释放。第二、</SPAN>girl[100]<SPAN lang=zh-cn> 
      本由系统自行释放,现在我们却要强行释放它。</SPAN></P>
      <P> </P>
      <H4><A name=20.4.1>20.4.1</A> <B><SPAN 
      lang=zh-cn>一个指针被删除时,应指向最初的地址</SPAN></B></H4>
      <P> </P>
      <P>当一个指针通过<SPAN lang=en-us> +,-</SPAN> 等操作而改变了指向;那么在释放之前,应确保其回到原来的指向。</P>
      <P> </P>
      <P>比如:</P>
      <P> </P>
      <P><SPAN lang=en-us>int* p = new int[3];</SPAN></P>
      <P> </P>
      <P><SPAN lang=en-us>*p = 1;</SPAN></P>
      <P><SPAN lang=en-us>cout &lt;&lt; *p &lt;&lt; endl;</SPAN></P>
      <P> </P>
      <P><SPAN lang=en-us>p++;&nbsp;&nbsp;&nbsp; //p</SPAN>的指向改变了,指向了下一元素</P>
      <P><SPAN lang=en-us>*p = 2;</SPAN></P>
      <P><SPAN lang=en-us>cout &lt;&lt; *p &lt;&lt; endl;</SPAN></P>
      <P> </P>
      <P><SPAN lang=en-us>//</SPAN>错误的释放:</P>
      <P><SPAN lang=en-us>delete [] p;</SPAN></P>
      <P> </P>
      <P>在<SPAN lang=en-us> delete [] p 
      </SPAN>时,p指向的是第二个元素,结果该释放将产生错位:第一个元素没有被释放,而在最后多删除了一个元素。相当你盖房时盖的是前3间,可以在拆房时,漏了头一间,从第二间开始拆起,结果把不是你盖的第4房间倒给一并拆了。</P>
      <P> </P>
      <P>如何消除这一严重错误呢?</P>
      <P>第一种方法是把指针正确地<SPAN lang=en-us>"</SPAN>倒<SPAN 
      lang=en-us>"</SPAN>回原始位置:</P>
      <P> </P>
      <P><B><SPAN lang=en-us>p--;</SPAN></B></P>
      <P><SPAN lang=en-us>delete [] p;</SPAN></P>
      <P> </P>
      <P>但当我们的指针指向变化很多次时,在释放前要保证一步不错地一一退回,会比较困难。所以另一方法是在最初时“备份”一份。在释放时,直接释放该指针即可。</P>
      <P> </P>
      <P><SPAN lang=en-us>int* p = new int[3];</SPAN></P>
      <P><B><SPAN lang=en-us>int* pbak = *p;&nbsp;&nbsp;&nbsp; 
      //</SPAN>备份</B></P>
      <P> </P>
      <P>//移动<SPAN lang=en-us> p</SPAN></P>
      <P>……</P>
      <P> </P>
      <P><SPAN lang=en-us>//</SPAN>释放:</P>
      <P><B><SPAN lang=en-us>delete [] pbak; </SPAN></B></P>
      <P> </P>
      <P>由于<SPAN lang=en-us>pba</SPAN>k正是指向p最初分配后的地址,我们删除<SPAN 
      lang=en-us>pbak,</SPAN><SPAN 
      lang=zh-cn>就是删除p最初的指向。此时我们不能再删除一次p。这也就引出</SPAN>new / delete <SPAN 
      lang=zh-cn>及</SPAN> new[] / delete[] <SPAN 
      lang=zh-cn>在本章的最后一个问题。</SPAN></P>
      <P> </P>
      <H4><B><A name=20.4.2>20.4.2</A> <SPAN 
      lang=zh-cn>已释放的空间,不可重复释放</SPAN></B></H4>
      <P> </P>
      <P><SPAN lang=zh-cn>第一种情况,错了最直接:</SPAN></P>
      <P> </P>
      <P>int* p = new int(71);</P>
      <P>cout &lt;&lt; *p &lt;&lt; endl;</P>
      <P> </P>
      <P>delete p; //OK!</P>
      <P>delete p; <FONT color=#ff0000>//ERROR! <SPAN 
      lang=zh-cn>重复删除p</SPAN></FONT></P>
      <P> </P>
      <P><SPAN lang=zh-cn>当然,如果同一指针在</SPAN>delete<SPAN 
      lang=zh-cn>之后,又通过</SPAN>new <SPAN lang=zh-cn>或</SPAN> new[] <SPAN 
      lang=zh-cn>分配了一次内存,则需要再删除一次:</SPAN></P>
      <P> </P>
      <P>int* p = new int(71);</P>
      <P>cout &lt;&lt; *p &lt;&lt; endl;</P>
      <P> </P>
      <P>delete p; //OK!</P>
      <P>...</P>
      <P>p = new int(81);</P>
      <P>delete p; //OK!</P>
      <P>...</P>
      <P> </P>
      <P>p = new int[10];</P>
      <P>for (int i=0; i&lt;10; i++)</P>
      <P>&nbsp; *p = i;</P>
      <P> </P>
      <P>...</P>
      <P>delete [] p; //OK!</P>
      <P> </P>
      <P><SPAN lang=zh-cn>上面代码中,共计三次对p进行</SPAN>delete <SPAN lang=zh-cn>或</SPAN> 
      delete[]<SPAN lang=zh-cn>,但不属于重复删除。因为每次</SPAN>delete<SPAN 
      lang=zh-cn>都对应一次新的</SPAN>new<SPAN lang=zh-cn>。</SPAN></P>
      <P><SPAN lang=zh-cn>我们下面所说的例子,均指一次</SPAN>delete<SPAN 
      lang=zh-cn>之后,没有再次</SPAN>new<SPAN lang=zh-cn>,而重复进行</SPAN>delete<SPAN 
      lang=zh-cn>。</SPAN></P>
      <P> </P>
      <P><SPAN lang=zh-cn>第二种情况,重复删除同一指向的多个指针</SPAN></P>
      <P> </P>
      <P>int* p1 = new int(71);</P>
      <P>int* p2 = p1;&nbsp;&nbsp; //<SPAN lang=zh-cn>p2和p1 
现在指向同一内存地址</SPAN></P>
      <P> </P>
      <P>cout &lt;&lt; *p1 &lt;&lt; endl;</P>
      <P>cout &lt;&lt; *p2 &lt;&lt; endl;</P>
      <P> </P>
      <P>delete p1;&nbsp; //OK</P>
      <P>delete p2;&nbsp; <FONT color=#ff0000>//ERROR! <SPAN 
      lang=zh-cn>p2所指的内存,已通过</SPAN>delete <SPAN 
      lang=zh-cn>p1而被释放,不可再</SPAN>delete<SPAN lang=zh-cn>一次。</SPAN></FONT></P>
      <P> </P>
      <P><SPAN lang=zh-cn>同样的问题,如果你先删除了</SPAN>p2<SPAN 
      lang=zh-cn>,则同样不可再删除p1。</SPAN></P>
      <P> </P>
      <P>...</P>
      <P>delete p2; //OK</P>
      <P>delete p1; <FONT color=#ff0000>//ERROR</FONT></P>
      <P> </P>
      <P><SPAN lang=zh-cn>第三种情况,删除指向某一普通变量的指针</SPAN></P>
      <P> </P>
      <P>int a = 100;</P>
      <P>int* p = &amp;a;</P>
      <P>delete p;&nbsp; //ERROR </P>
      <P> </P>
      <P>p <SPAN lang=zh-cn>不是通过</SPAN>new <SPAN 
      lang=zh-cn>得到新的内存空间,而是直接指向固定变量:a。所以删除</SPAN>p<SPAN 
      lang=zh-cn>等同要强行剥夺a的固有空间</SPAN>,<SPAN lang=zh-cn>会导致出错。</SPAN></P>
      <P> </P>
      <H3><B><A name=20.5>20.5</A> C <SPAN lang=zh-cn>方式的内存管理</SPAN></B></H3>
      <P> </P>
      <P>new/delete<SPAN 
      lang=zh-cn>只在C++里得到支持。在C里,内存管理是通过专门的函数来实现。另外,为了兼容各种编程语言,操作系统提供的接口通常是 C 
      语言写成的函数声明 (</SPAN>Windows <SPAN 
      lang=zh-cn>本身也由C和汇编语言写成)。这样,我们就不得不同时学习C的内存管理函数。</SPAN></P>
      <P> </P>
      <H4><B><A name=20.5.1>20.5.1</A> <SPAN lang=zh-cn>分配内存</SPAN> malloc<SPAN 
      lang=zh-cn> 函数</SPAN></B></H4>
      <P> </P>
      <P><SPAN lang=zh-cn>需要包含头文件:</SPAN></P>
      <P>#include &lt;alloc.h&gt;</P>
      <P><SPAN lang=zh-cn>或</SPAN></P>
      <P>#include &lt;stdlib.h&gt;</P>
      <P> </P>
      <P><SPAN lang=zh-cn>函数声明</SPAN>(<SPAN lang=zh-cn>函数原型</SPAN>)<SPAN 
      lang=zh-cn>:</SPAN></P>
      <P>void *malloc(int size);</P>
      <P> </P>
      <P><SPAN lang=zh-cn>说明:</SPAN>malloc <SPAN 
      lang=zh-cn>向系统申请分配指定</SPAN>size<SPAN lang=zh-cn>个字节的内存空间。返回类型是</SPAN> 
      void* <SPAN lang=zh-cn>类型。</SPAN>void* <SPAN 
      lang=zh-cn>表示未确定类型的指针。C,C++规定,</SPAN>void* <SPAN 
      lang=zh-cn>类型可以强制转换为任何其它类型的指针。</SPAN></P>
      <P> </P>
      <P><SPAN lang=zh-cn>从函数声明上可以看出。</SPAN>malloc <SPAN lang=zh-cn>和</SPAN> new 
      <SPAN lang=zh-cn>至少有两个不同</SPAN>:<SPAN lang=zh-cn> </SPAN>new<SPAN 
      lang=zh-cn> 返回指定类型的指针,并且可以自动计算所需要大小。比如:</SPAN></P>
      <P> </P>
      <P>int *p;</P>
      <P> </P>
      <P>p = new int; //<SPAN lang=zh-cn>返回类型为</SPAN>int* <SPAN 
      lang=zh-cn>类型</SPAN>(<SPAN lang=zh-cn>整数型指针</SPAN>)<SPAN 
      lang=zh-cn>,分配大小为</SPAN> sizeof(int);</P>
      <P><SPAN lang=zh-cn>或:</SPAN></P>
      <P> </P>
      <P>int* parr;</P>
      <P> </P>
      <P>parr = new int [100];&nbsp; //<SPAN lang=zh-cn>返回类型为</SPAN> int* <SPAN 
      lang=zh-cn>类型</SPAN>(<SPAN lang=zh-cn>整数型指针</SPAN>)<SPAN 
      lang=zh-cn>,分配大小为</SPAN> sizeof(int) * 100;</P>
      <P> </P>
      <P><SPAN lang=zh-cn>而</SPAN> malloc <SPAN 
      lang=zh-cn>则必须由我们计算要字节数,并且在返回后强行转换为实际类型的指针。</SPAN></P>
      <P> </P>
      <P>int* p;</P>
      <P> </P>
      <P>p = <B>(int *)</B>&nbsp; malloc (<B>sizeof(int)</B>);</P>
      <P> </P>
      <P><SPAN lang=zh-cn>第一、</SPAN>malloc <SPAN lang=zh-cn>函数返回的是</SPAN> void 
      *<SPAN lang=zh-cn> 类型,如果你写成:</SPAN>p = malloc (sizeof(int)); <SPAN 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -