﻿<?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++博客-&lt;font style="position:absolute; top:10px; left:20px; color: #c0c0c0;  "&gt;幽幽&lt;/font&gt;-随笔分类-杂集</title><link>http://www.cppblog.com/justin-shi/category/6151.html</link><description>&lt;font&gt;&amp;nbsp;&lt;/font&gt;   </description><language>zh-cn</language><lastBuildDate>Wed, 20 May 2009 18:20:12 GMT</lastBuildDate><pubDate>Wed, 20 May 2009 18:20:12 GMT</pubDate><ttl>60</ttl><item><title>再谈异常――谈C++与Object Pascal中的构造函数与异常 </title><link>http://www.cppblog.com/justin-shi/archive/2009/05/20/83429.html</link><dc:creator>幽幽</dc:creator><author>幽幽</author><pubDate>Tue, 19 May 2009 16:42:00 GMT</pubDate><guid>http://www.cppblog.com/justin-shi/archive/2009/05/20/83429.html</guid><wfw:comment>http://www.cppblog.com/justin-shi/comments/83429.html</wfw:comment><comments>http://www.cppblog.com/justin-shi/archive/2009/05/20/83429.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/justin-shi/comments/commentRss/83429.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/justin-shi/services/trackbacks/83429.html</trackback:ping><description><![CDATA[<p>再谈异常――谈C++与Object Pascal中的构造函数与异常</p>
<p>作者：Nicrosoft(<a  href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#110;&#105;&#99;&#114;&#111;&#115;&#111;&#102;&#116;&#64;&#115;&#117;&#110;&#105;&#115;&#116;&#117;&#100;&#105;&#111;&#46;&#99;&#111;&#109;">nicrosoft@sunistudio.com</a>) 2001.9.15<br>个人主页：http://www.sunistudio.com/nicrosoft/<br>东日文档：http://www.sunistudio.com/asp/sunidoc.asp</p>
<p>　　我们知道，类的构造函数是没有返回值的，如果构造函数构造对象失败，不可能依靠返回错误代码。那么，在程序中如何标识构造函数的失败呢？最&#8220;标准&#8221;的方法就是：抛出一个异常。</p>
<p>　　构造函数失败，意味着对象的构造失败，那么抛出异常之后，这个&#8220;半死不活&#8221;的对象会被如何处理呢？这就是本文的主题。</p>
<p>
在C++中，构造函数抛出异常后，析构函数不会被调用。这是合理的，因为此时对象并没有被完整构造。也就是说，如果构造函数已经做了一些诸如分配内存、
打开文件等操作的话，那么类需要有自己的成员来记住做过哪些动作。在C++中，经典的解决方案是使用STL的标准类auto_ptr，这在每一本经典
C++著作中都有介绍，我在这里就不多说了。在这里，我想再介绍一种&#8220;非常规&#8221;的方式，其思想就是避免在构造函数中抛出异常。我们可以在类中增加一个
Init(); 以及 UnInit();成员函数用于进行容易产生错误的资源分配工作，而真正的构造函数中先将所有成员置为NULL，然后调用
Init(); 并判断其返回值（或者捕捉 Init()抛出的异常），如果Init();失败了，则在构造函数中调用 UnInit();
并设置一个标志位表明构造失败。UnInit()中按照成员是否为NULL进行资源的释放工作。示例代码如下：<br>class A<br>{<br>private:<br>&nbsp;char* str;<br>&nbsp;int failed;</p>
<p>public:<br>&nbsp;A();<br>&nbsp;~A();<br>&nbsp;int Init();<br>&nbsp;int UnInit();<br>&nbsp;int Failed();<br>};</p>
<p>A::A()<br>{<br>&nbsp;str = NULL;<br>&nbsp;try<br>&nbsp;{<br>&nbsp;&nbsp;Init();<br>&nbsp;&nbsp;failed = 0;<br>&nbsp;}<br>&nbsp;catch(...)<br>&nbsp;{<br>&nbsp;&nbsp;failed = 1;<br>&nbsp;&nbsp;UnInit();<br>&nbsp;}<br>}</p>
<p>A::~A()<br>{<br>&nbsp;UnInit();<br>}</p>
<p>int A::Init()<br>{<br>&nbsp;str = new char[10];<br>&nbsp;strcpy(str, "ABCDEFGHI");<br>&nbsp;throw 10;</p>
<p>&nbsp;return 1;<br>}</p>
<p>int A::UnInit()<br>{<br>&nbsp;if (!str)<br>&nbsp;{<br>&nbsp;&nbsp;delete []str;<br>&nbsp;&nbsp;str = NULL;<br>&nbsp;}</p>
<p>&nbsp;printf("Free Resource
</p>
<p style="text-indent: 2em;">");<br>&nbsp;return 1;<br>}</p>
<p>int A::Failed()<br>{<br>&nbsp;return failed;<br>}</p>
<p>int main(int argc, char* argv[])<br>{<br>&nbsp;A* a = new A;<br>&nbsp;if ( a-&gt;Failed() )<br>&nbsp;&nbsp;printf("failed
</p>
<p style="text-indent: 2em;">");<br>&nbsp;else<br>&nbsp;&nbsp;printf("succeeded
</p>
<p style="text-indent: 2em;">");</p>
<p>&nbsp;delete a;</p>
<p>&nbsp;getchar();<br>&nbsp;return 0;<br>}</p>
<p>　　你会发现，在int A::Init()中包含了throw 10;的代码（产生一个异常，模拟错误的发生），执行结果是：<br>　　Free Resource<br>　　failed<br>　　Free Resource<br>　　虽然 UnInit();被调用了两次，但是由于UnInit();中做了判断（if (!str)），因此不会发生错误。而如果没有发生异常（去掉 int A::Init()中的throw 10;代码），执行结果是：<br>　　Succeeded<br>　　Free Resource<br>　　和正常的流程没有任何区别。</p>
<p>
在Object Pascal（Delphi/VCL）中，这个问题就变得非常的简单了，因为 OP
对构造函数的异常的处理与C++不同，在Create时抛出异常后，编译器会自动调用析构函数Destroy，并且会判断哪些资源被分配了，实行自动回
收。因此，其代码也变得非常简洁，如下：<br>type<br>&nbsp; A = class<br>&nbsp; private<br>　　str : PChar;<br>&nbsp; public<br>　　constructor Create();<br>　　destructor Destroy(); override;<br>&nbsp; end;</p>
<p>constructor A.Create();<br>begin<br>　　str := StrAlloc(10);<br>　　StrCopy(str, 'ABCDEFGHI');<br>　　raise Exception.Create('error');<br>end;</p>
<p>destructor A.Destroy();<br>begin<br>　　StrDispose(str);<br>　　WriteLn('Free Resource');<br>end;</p>
<p>var oa : A;<br>　　i : integer;<br>begin<br>　　try<br>　　&nbsp;&nbsp;&nbsp; oa := A.Create();<br>　　&nbsp;&nbsp;&nbsp; WriteLn('Succeeded');<br>　　&nbsp;&nbsp;&nbsp; oa.Free();<br>　　except<br>　　&nbsp;&nbsp;&nbsp; oa := nil;<br>　　&nbsp;&nbsp;&nbsp; WriteLn('Failed');<br>　　end;</p>
<p>　　Read(i);<br>end.</p>
<p>　　在这段代码中，如果构造函数抛出异常（即Create中含有raise Exception.Create('error');），执行的结果是：<br>　　Free Resource<br>　　Failed<br>　　此时的&#8220;Free Resource&#8221;输出是由编译器自动调用析构函数所产生的。而如果构造函数正常返回（即不抛出异常），则执行结果是：<br>　　Succeeded<br>　　Free Resource<br>　　此时的&#8220;Free Resource&#8221;输出是由 oa.Free()的调用产生的。</p>
<p>
综上，C++与Object
Pascal对于构造函数抛出异常后的不同处理方式，其实正是两种语言的设计思想的体现。C++秉承C的风格，注重效率，一切交给程序员来掌握，编译器不
作多余动作。Object
Pascal继承Pascal的风格，注重程序的美学意义（不可否认，Pascal代码是全世界最优美的代码），编译器帮助程序员完成复杂的工作。两种语
言都有存在的理由，都有存在的必要！而掌握它们之间的差别，能让你更好地控制它们，达到自由的理想王国。</p>
<p><br></p><img src ="http://www.cppblog.com/justin-shi/aggbug/83429.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/justin-shi/" target="_blank">幽幽</a> 2009-05-20 00:42 <a href="http://www.cppblog.com/justin-shi/archive/2009/05/20/83429.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[GDI+]如何将一个彩色图像转换成黑白图像</title><link>http://www.cppblog.com/justin-shi/archive/2008/10/20/64484.html</link><dc:creator>幽幽</dc:creator><author>幽幽</author><pubDate>Mon, 20 Oct 2008 05:10:00 GMT</pubDate><guid>http://www.cppblog.com/justin-shi/archive/2008/10/20/64484.html</guid><wfw:comment>http://www.cppblog.com/justin-shi/comments/64484.html</wfw:comment><comments>http://www.cppblog.com/justin-shi/archive/2008/10/20/64484.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/justin-shi/comments/commentRss/64484.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/justin-shi/services/trackbacks/64484.html</trackback:ping><description><![CDATA[<p>彩色图像转换为黑白图像时需要计算图像中每像素有效的亮度值，通过匹配像素</p>
<p>亮度值可以轻松转换为黑白图像。</p>
<p>计算像素有效的亮度值可以使用下面的公式：</p>
<p>Y=0.3RED+0.59GREEN+0.11Blue</p>
<p>然后使用 Color.FromArgb(Y,Y,Y) 来把计算后的值转换</p>
<p>转换代码可以使用下面的方法来实现：<br>C#<br></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #008080">&nbsp;1</span><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">&nbsp;Bitmap&nbsp;ConvertToGrayscale(Bitmap&nbsp;source)<br></span><span style="COLOR: #008080">&nbsp;2</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;3</span><span style="COLOR: #000000"><img id=Codehighlighter1_49_358_Open_Image onclick="this.style.display='none'; Codehighlighter1_49_358_Open_Text.style.display='none'; Codehighlighter1_49_358_Closed_Image.style.display='inline'; Codehighlighter1_49_358_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_49_358_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_49_358_Closed_Text.style.display='none'; Codehighlighter1_49_358_Open_Image.style.display='inline'; Codehighlighter1_49_358_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_49_358_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_49_358_Open_Text><span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">&nbsp;4</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;5</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;Bitmap&nbsp;bm&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000">&nbsp;Bitmap(source.Width,source.Height);<br></span><span style="COLOR: #008080">&nbsp;6</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;7</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;</span><span style="COLOR: #0000ff">for</span><span style="COLOR: #000000">(</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;y</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;y</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">bm.Height;y</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">)<br></span><span style="COLOR: #008080">&nbsp;8</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;9</span><span style="COLOR: #000000"><img id=Codehighlighter1_141_341_Open_Image onclick="this.style.display='none'; Codehighlighter1_141_341_Open_Text.style.display='none'; Codehighlighter1_141_341_Closed_Image.style.display='inline'; Codehighlighter1_141_341_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_141_341_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_141_341_Closed_Text.style.display='none'; Codehighlighter1_141_341_Open_Image.style.display='inline'; Codehighlighter1_141_341_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;</span><span id=Codehighlighter1_141_341_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_141_341_Open_Text><span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">10</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">11</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">for</span><span style="COLOR: #000000">(</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;x</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;x</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">bm.Width;x</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">)<br></span><span style="COLOR: #008080">12</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">13</span><span style="COLOR: #000000"><img id=Codehighlighter1_181_336_Open_Image onclick="this.style.display='none'; Codehighlighter1_181_336_Open_Text.style.display='none'; Codehighlighter1_181_336_Closed_Image.style.display='inline'; Codehighlighter1_181_336_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_181_336_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_181_336_Closed_Text.style.display='none'; Codehighlighter1_181_336_Open_Image.style.display='inline'; Codehighlighter1_181_336_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_181_336_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_181_336_Open_Text><span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">14</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">15</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Color&nbsp;c</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">source.GetPixel(x,y);<br></span><span style="COLOR: #008080">16</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">17</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;luma&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">)(c.R</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">0.3</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;c.G</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">0.59</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;c.B</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">0.11</span><span style="COLOR: #000000">);<br></span><span style="COLOR: #008080">18</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">19</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;bm.SetPixel(x,y,Color.FromArgb(luma,luma,luma));<br></span><span style="COLOR: #008080">20</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">21</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">22</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">23</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">24</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">25</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;bm;<br></span><span style="COLOR: #008080">26</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">27</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span></div>
<br>VB<br>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #008080">&nbsp;1</span><img id=Codehighlighter1_7_425_Open_Image onclick="this.style.display='none'; Codehighlighter1_7_425_Open_Text.style.display='none'; Codehighlighter1_7_425_Closed_Image.style.display='inline'; Codehighlighter1_7_425_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_7_425_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_7_425_Closed_Text.style.display='none'; Codehighlighter1_7_425_Open_Image.style.display='inline'; Codehighlighter1_7_425_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top><span style="COLOR: #0000ff">Public</span><span style="COLOR: #000000">&nbsp;</span><span id=Codehighlighter1_7_425_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">Function&nbsp;ConvertToGrayscale()</span><span id=Codehighlighter1_7_425_Open_Text><span style="COLOR: #0000ff">Function</span><span style="COLOR: #000000">&nbsp;ConvertToGrayscale()</span><span id=Codehighlighter1_36_424_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">Function&nbsp;ConvertToGrayscale()</span><span id=Codehighlighter1_36_424_Open_Text><span style="COLOR: #0000ff">Function</span><span style="COLOR: #000000">&nbsp;ConvertToGrayscale(</span><span style="COLOR: #0000ff">ByVal</span><span style="COLOR: #000000">&nbsp;source&nbsp;</span><span style="COLOR: #0000ff">As</span><span style="COLOR: #000000">&nbsp;Bitmap)&nbsp;</span><span style="COLOR: #0000ff">as</span><span style="COLOR: #000000">&nbsp;Bitmap<br></span><span style="COLOR: #008080">&nbsp;2</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;3</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;</span><span style="COLOR: #0000ff">Dim</span><span style="COLOR: #000000">&nbsp;bm&nbsp;</span><span style="COLOR: #0000ff">as</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000">&nbsp;Bitmap(source.Width,source.Height)<br></span><span style="COLOR: #008080">&nbsp;4</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;5</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;</span><span style="COLOR: #0000ff">Dim</span><span style="COLOR: #000000">&nbsp;x<br></span><span style="COLOR: #008080">&nbsp;6</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;7</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;</span><span style="COLOR: #0000ff">Dim</span><span style="COLOR: #000000">&nbsp;y<br></span><span style="COLOR: #008080">&nbsp;8</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;9</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;</span><span style="COLOR: #0000ff">For</span><span style="COLOR: #000000">&nbsp;y</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">To</span><span style="COLOR: #000000">&nbsp;bm.Height<br></span><span style="COLOR: #008080">10</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">11</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">For</span><span style="COLOR: #000000">&nbsp;x</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">To</span><span style="COLOR: #000000">&nbsp;bm.Width<br></span><span style="COLOR: #008080">12</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">13</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">Dim</span><span style="COLOR: #000000">&nbsp;c&nbsp;</span><span style="COLOR: #0000ff">as</span><span style="COLOR: #000000">&nbsp;Color&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;source.GetPixel(x,y)<br></span><span style="COLOR: #008080">14</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">15</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">Dim</span><span style="COLOR: #000000">&nbsp;luma&nbsp;</span><span style="COLOR: #0000ff">as</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">Integer</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">CInt</span><span style="COLOR: #000000">(c.R</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">0.3</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;c.G</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">0.59</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;c.B</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">0.11</span><span style="COLOR: #000000">)<br></span><span style="COLOR: #008080">16</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">17</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;bm.SetPixel(x,y,Color.FromArgb(luma,luma,luma)<br></span><span style="COLOR: #008080">18</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">19</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">Next</span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">20</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">21</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;</span><span style="COLOR: #0000ff">Next</span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">22</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">23</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;</span><span style="COLOR: #0000ff">Return</span><span style="COLOR: #000000">&nbsp;bm<br></span><span style="COLOR: #008080">24</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">25</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top></span><span style="COLOR: #0000ff">End&nbsp;Function</span></span></div>
</span>
<img src ="http://www.cppblog.com/justin-shi/aggbug/64484.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/justin-shi/" target="_blank">幽幽</a> 2008-10-20 13:10 <a href="http://www.cppblog.com/justin-shi/archive/2008/10/20/64484.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>解析#pragma指令</title><link>http://www.cppblog.com/justin-shi/archive/2008/08/19/59319.html</link><dc:creator>幽幽</dc:creator><author>幽幽</author><pubDate>Tue, 19 Aug 2008 03:01:00 GMT</pubDate><guid>http://www.cppblog.com/justin-shi/archive/2008/08/19/59319.html</guid><wfw:comment>http://www.cppblog.com/justin-shi/comments/59319.html</wfw:comment><comments>http://www.cppblog.com/justin-shi/archive/2008/08/19/59319.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/justin-shi/comments/commentRss/59319.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/justin-shi/services/trackbacks/59319.html</trackback:ping><description><![CDATA[转自CSDN<br><br>在所有的预处理指令中，#Pragma 指令可能是最复杂的了，它的作用是设定编译器的状态或者是指示编译器完成一些特定的动作。#pragma指令对每个编译器给出了一个方法,在保持与C和C++语言完全兼容的情况下,给出主机或操作系统专有的特征。依据定义,编译指示是机器或操作系统专有的,且对于每个编译器都是不同的。<br>其格式一般为: #Pragma Para<br>其中Para 为参数，下面来看一些常用的参数。<br><br>(1)message 参数。 Message 参数是我最喜欢的一个参数，它能够在编译信息输出窗<br>口中输出相应的信息，这对于源代码信息的控制是非常重要的。其使用方法为：<br>#Pragma message(&#8220;消息文本&#8221;)<br>当编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来。<br>当我们在程序中定义了许多宏来控制源代码版本的时候，我们自己有可能都会忘记有没有正确的设置这些宏，此时我们可以用这条指令在编译的时候就进行检查。假设我们希望判断自己有没有在源代码的什么地方定义了_X86这个宏可以用下面的方法<br>#ifdef _X86<br>#Pragma message(&#8220;_X86 macro activated!&#8221;)<br>#endif<br>当我们定义了_X86这个宏以后，应用程序在编译时就会在编译输出窗口里显示&#8220;_<br>X86 macro activated!&#8221;。我们就不会因为不记得自己定义的一些特定的宏而抓耳挠腮了<br>。<br><br>(2)另一个使用得比较多的pragma参数是code_seg。格式如：<br>#pragma code_seg( ["section-name"[,"section-class"] ] )<br>它能够设置程序中函数代码存放的代码段，当我们开发驱动程序的时候就会使用到它。<br><br>(3)#pragma once (比较常用）<br>只要在头文件的最开始加入这条指令就能够保证头文件被编译一次，这条指令实际上在VC6中就已经有了，但是考虑到兼容性并没有太多的使用它。<br><br>(4)#pragma hdrstop表示预编译头文件到此为止，后面的头文件不进行预编译。BCB可以预编译头文件以加快链接的速度，但如果所有头文件都进行预编译又可能占太多磁盘空间，所以使用这个选项排除一些头文件。 <br>有时单元之间有依赖关系，比如单元A依赖单元B，所以单元B要先于单元A编译。你可以用#pragma startup指定编译优先级，如果使用了#pragma package(smart_init) ，BCB就会根据优先级的大小先后编译。 <br><br>(5)#pragma resource "*.dfm"表示把*.dfm文件中的资源加入工程。*.dfm中包括窗体<br>外观的定义。 <br><br>(6)#pragma warning( disable : 4507 34; once : 4385; error : 164 )<br>等价于：<br>#pragma warning(disable:4507 34) // 不显示4507和34号警告信息<br>#pragma warning(once:4385) // 4385号警告信息仅报告一次<br>#pragma warning(error:164) // 把164号警告信息作为一个错误。<br>同时这个pragma warning 也支持如下格式：<br>#pragma warning( push [ ,n ] )<br>#pragma warning( pop )<br>这里n代表一个警告等级(1---4)。<br>#pragma warning( push )保存所有警告信息的现有的警告状态。<br>#pragma warning( push, n)保存所有警告信息的现有的警告状态，并且把全局警告<br>等级设定为n。 <br>#pragma warning( pop )向栈中弹出最后一个警告信息，在入栈和出栈之间所作的<br>一切改动取消。例如：<br>#pragma warning( push )<br>#pragma warning( disable : 4705 )<br>#pragma warning( disable : 4706 )<br>#pragma warning( disable : 4707 )<br>//.......<br>#pragma warning( pop ) <br>在这段代码的最后，重新保存所有的警告信息(包括4705，4706和4707)。<br>（7）pragma comment(...)<br>该指令将一个注释记录放入一个对象文件或可执行文件中。<br>常用的lib关键字，可以帮我们连入一个库文件。 <br><br><br>
<img src ="http://www.cppblog.com/justin-shi/aggbug/59319.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/justin-shi/" target="_blank">幽幽</a> 2008-08-19 11:01 <a href="http://www.cppblog.com/justin-shi/archive/2008/08/19/59319.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>pe的格式(翻译)</title><link>http://www.cppblog.com/justin-shi/archive/2008/07/28/57323.html</link><dc:creator>幽幽</dc:creator><author>幽幽</author><pubDate>Sun, 27 Jul 2008 20:04:00 GMT</pubDate><guid>http://www.cppblog.com/justin-shi/archive/2008/07/28/57323.html</guid><wfw:comment>http://www.cppblog.com/justin-shi/comments/57323.html</wfw:comment><comments>http://www.cppblog.com/justin-shi/archive/2008/07/28/57323.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/justin-shi/comments/commentRss/57323.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/justin-shi/services/trackbacks/57323.html</trackback:ping><description><![CDATA[PE格式，是Windows的可執行檔的格式。 Windows中的 exe檔，dll檔，都是PE格式。 PE 就是Portable Executable 的縮寫。 Portable 是指對於不同的Windows版本和不同的CPU類型上PE檔的格式是一樣的，當然CPU不一樣了，CPU指令的二進位編碼是不一樣的。只是檔中各種東西的佈局是一樣的。 能告示根底嗎！ 摘要 Matt Pietrek（姜慶東譯） 對可執行檔的深入認識將帶你深入到系統深處。如果你知道你的exe/dll裏是些什麼東東，你就是一個更有知識的程式師。作為系列文章的第一章，將關注這幾年來PE格式的變化，同時也簡單介紹一下PE格式。經過這次更新，作者加入了PE格式是如何與.NET協作的及PE檔表格（PE FILE SECTIONS），RVA,The DataDirectory,函數的輸入等內容。 ＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝ 很久以前，我給Microsoft Systems Journal（現在的MSDN）寫了一篇名為&#8220;Peering Inside the PE: A Tour of the Win32 Portable Executable File format&#8221;的文章。後來比我期望的還流行，到現在我還聽說有人在用它（它還在MSDN裏）。不幸的是，那篇文章的問題依舊存在，WIN32的世界靜悄悄地變了好多，那篇文章已顯得過期了。從這個月開始我將用這兩篇文章來彌補。 你可能會問為什麼我應當瞭解PE格式，答案依舊：作業系統的可執行檔格式和資料結構暴露出系統的底層細節。通過瞭解這些，你的程式將編的更出色。 當然，你可以閱讀微軟的文檔來瞭解我將要告訴你的。但是，像很多文檔一樣，&#8216;寧可晦澀，但為瓦全&#8217;。 我把焦點放在提供一些不適合放在正式文檔裏的內容。另外，這篇文章裏的一些知識不見得能在官方文檔裏找到。 1. 裂縫的撕開 讓我給你一些從1994年我寫那篇文章來PE格式變化的例子。WIN16已經成為歷史，也就沒有必要作什麼比較和說明了。另外一個可憎的東西就是用在WINDOWS 3.1 中的WIN32S，在它上面運行程式是那麼的不穩定。 那時候，WINDOWS 95（也叫Chicago）還沒有發行。NT還是3.5版。微軟的連接器還沒開始大規模的優化，儘管如此，there were MIPS and DEC Alpha implementations of Windows NT that added to the story. 那麼究竟，這麼些年來，有些什麼新的東西出來呢？64位的WINDOWS有了它自己的PE變種，WINDOWS CE 支持各種CPU了，各種優化如DLL的延遲載入，節表的合併，動態捆綁等也已出臺。 有很多類似的東西發生了。 讓我們最好忘了.NET。它是如何與系統切入的呢？對於作業系統，.NET的可執行檔格式是與舊的PE格式相容的。雖然這麼說，在運行時期，.NET還是按元資料和中間語言來組織資料的，這畢竟是它的核心。這篇文章當中，我將打開.NET元資料這扇門，但不做深入討論。 如果WIN32的這些變化都不足以讓我重寫這篇文章，就是原來的那些錯誤也讓我汗顏。比如我對TLS的描述只是一帶而過，我對時間戳的描述只有你生活在美國西部才行等等。還有，一些東西已是今是作非了，我曾說過.RDATA幾乎沒排上用場，今天也是，我還說過.IDATA節是可讀可寫的，但是一些搞API攔截的人發現好像是錯的。 在更新這篇文章的過程當中，我也檢查了PEDUMP這個用來傾印PE檔的程式.這個程式能夠在0X86和IA-64平臺下編譯和運行。 2. PE格式概覽 微軟的可執行檔格式，也就是大家熟悉的PE 格式，是官方文檔的一部分。但是，它是從VAX/VMS上的COFF派生出來的，就WINDOWS NT小組的大部分是從DEC轉過來的看來，這是可以理解的。很自然，這些人在NT的開發上會用他們以往的代碼。 採用術語&#8220;PORTABLE EXECUTABLE&#8221;是因為微軟希望有一個通用在所有WINDOWS平臺上和所有CPU上的檔格式。從大的方面講，這個目標已經實現。它適用于NT及其後代，95及其後代，和CE. 微軟產生的OBJ檔是用COFF格式的。當你看到它的很多域都是用八進制的編碼的，你會發現她是多麼古老了。COFF OBJ檔用到了很多和PE一樣的資料結構和枚舉，我馬上會提到一些。 64位的WINDOWS只對PE格式作了一點點改變。這個新的格式叫做PE32+。沒有增加一個欄位，且只刪了一個欄位。其他的改變就是把以前的32位欄位擴展成64位。對於C++代碼，通過巨集定義WINDOWS的頭檔已經遮罩了這些差別。 EXE與DLL的差別完全是語義上的。它們用的都是同樣一種檔格式-PE。唯一的區別就是其中有一個欄位標識出是EXE還是DLL.還有很多DLL的擴展比如OCX,CPL等都是DLL.它們有一樣的實體。 你首先要知道的關於PE的知識就是磁片中的資料結構佈局和記憶體中的資料結構佈局是一樣的。載入可執行檔（比如LOADLIBARY）的首要任務就是把磁片中的檔映射到進程的位址空間.因此像IMAGE_NT_HEADER（下面解釋）在磁片和記憶體中是一樣的。關鍵的是你要懂得你怎樣在磁片中獲得PE檔某些資訊的，當它載入記憶體時你可以一樣獲得，基本上是沒什麼不同的（即記憶體映射檔）。但是知道與映射普通的記憶體映射檔不同是很重要的。WINDOWS載入器察看PE檔才決定映射到哪里，然後從檔的開始處往更高的位址映射，但是有的東西在檔中的偏移和在記憶體中的偏移會不一樣。儘管如此，你也有了足夠的資訊把檔偏移轉化成記憶體偏移。見圖一： 當Windows載入器把PE載入記憶體，在記憶體中它稱作模組（MODULE），檔從HMODULE這個位址開始映射。記住這點：給你個HMODULE，從那你可以知道一個資料結構（IMAGE_DOS_HEADER），然後你還可以知道所有得資料結構。這個強大的功能對於API攔截特別有意義。（準確地說：對於WINDOWS CE，這是不成立的，不過這是後話）。 記憶體中的模組代表著進程從這個可執行檔中所需要的所有代碼，資料，資源。其他部分可以被讀入，但是可能不映射（如，重定位節）。還有一些部分根本就不映射，比如當調試資訊放到檔的尾部的時候。有一個欄位告訴系統把檔映射到記憶體需要多少記憶體。不需要的資料放在檔的尾部，而在過去，所有部分都映射。 在WINNT.H描述了PE 格式。在這個檔中，幾乎有所有的關於PE的資料結構，枚舉，#DEFINE。當然，其他地方也有相關文檔，但是還是WINNT.H說了算。 有很多檢測PE文件的工具，有VISUAL STUDIO的DUMPBIN,SDK中的DEPENDS，我比較喜歡DEPENDS，因為它以一種簡潔的方式檢測出檔的引入引出。一個免費的PE察看器，PEBrowse,來自smidgenosoft。我的pedump也是很有用的，它和dumpbin有一樣的功能。 從api的立場看，imagehlp.dll提供了讀寫pe檔的機制。 在開始討論pe檔前，回顧一下pe檔的一些基本概念是有意義的。在下面幾節，我將討論：pe 節，相對虛擬位址（rva），資料目錄，函數的引入。 3. PE節 PE節以某鍾順序表示代碼或資料。代碼就是代碼了，但是卻有多種類型的資料，可讀寫的程式資料（如總體變數），其他的節包含API的引入引出表，資源，重定位。每個節有自己的屬性，包括是否是代碼節，是否唯讀還是可讀可寫，節的資料是否全局共用。 通常，節中的資料邏輯上是關聯的。PE檔一般至少要有兩個節，一個是代碼，另一個為資料。一般還有一個其他類型的資料的節。後面我將描述各種類型的節。 每個節都有一個獨特的名字。這個名字是用來傳達這個節的用途的。比如，.RDATA表示一個唯讀節，節的名字對於作業系統毫無意義，只是為了人們便於理解。把一個節命名為FOOBAR和.TEXT是一樣有用的。微軟給他們的節命名了個有特色的名字，但是這不是必需的。Borland的連接器用的是code和data 一般編譯器將產生一系列標準的節，但這沒有什麼不可思議的。你可以建立和命名自己的節，連接器會自動在程式檔中包含它們。在visual c++中，你能用#pragma指令讓編譯器插入資料到一個節中。像下面這樣： 　#pragma data_seg("MY_DATA") 　...有必要初始化 　#pragma data_seg() 你也可以對.data做同樣的事。大部分的程式都只用編譯器產生的節，但是有時候你卻需要這樣。比如建立一個全局共用節。 節並不是全部由連接器確定的，他們可以在編譯階段由編譯器放入obj檔。連接器的工作就是合併所有obj和庫中需要的節成一個最終的合適的節。比如，你的工程中的所有obj可能都有一個包含代碼的.text節，連接器把這些節合併成一個.text節。同樣對於.data等。這些主題超出了這篇文章的範圍了。還有更多的規則關於連接器的。在obj文件中是專門給linker用的，並不放入到pe檔中，這種節是用來給連接器傳遞資訊的。 節有兩個關於對齊的欄位，一個對應磁片檔，另一個對應記憶體中的檔。Pe檔頭指出了這兩個值，他們可以不一樣。每個節的偏移從對齊值的倍數開始。比如，典型的對齊值是0x200，那麼每個節的的偏移必須是0x200的倍數。一旦載入記憶體，節的起始位址總是以頁對齊。X86cpu的頁大小為4k，al-64為8k。 下麵是pedump傾印出的Windows XP KERNEL32.DLL.的.text .data節的信息： 　Section Table 　01 .text VirtSize: 00074658 VirtAddr: 00001000 　raw data offs: 00000400 raw data size: 00074800 　... 　02 .data VirtSize: 000028CA VirtAddr: 00076000 　raw data offs: 00074C00 raw data size: 00002400 建立一個節在檔中的偏移和它相對於載入位址的偏移相同的pe檔是可能的。在98/me中，這會加速大檔的載入。Visual studio 6.0 的默認選項 /opt:win98j就是這樣產生檔的。在Visual studio.net中是否用/opt:nowin98取決於檔是否夠小。 一個有趣的連接器特徵是合併節的能力。如果兩個節有相似相容的屬性，連接的時候就可以合併為一個節。這取決於是否用/merger開關。像下麵就把.rdata和.text合併為一個節.text 　/MERGE:.rdata=.text 合併節的優點就是對於磁片和記憶體節省空間。每個節至少佔用一頁記憶體，如果你可以把可執行檔的節數從4減到3，很可能就可以少用一頁記憶體。當然，這取決於兩個節的空餘空間加起來是否達到一頁。 當你合併節事情會變得有意思，因為這沒有什麼硬性和容易的規則。比如你可以合併.rdata到.text， 但是你不可以把.rsrc.reloc.pdata合併到別的節。先前Visual Studio .NET允許把.idata合併，後來又不允許了。但是當發行的時候，連接器還是可以把.idata合併到別的節。 因為引入節的一部分在載入器載入時將被寫入，你可能驚奇它是如何被放入一個唯讀節的。是這樣的，在載入的時候系統會臨時改變那些包含引入節的頁為可讀可寫，初始化完成後，又恢復原來屬性。 4. 相對虛擬位址 在可執行檔中，有很多地方需要指定記憶體位址，比如，引用總體變數時，需要指定它的位址。Pe檔儘管有一個首選的載入位址，但是他們可以載入到進程空間的任何地方，所以你不能依賴於pe的載入點。由於這點，必須有一個方法來指定位址而不依賴於pe載入點的地址。為了避免把記憶體位址硬編碼進pe檔，提出了RVA。RVA是一個簡單的相對於PE載入點的記憶體偏移。比如，PE載入點為0X400000,那麼代碼節中的地址0X401000的RVA為(target address) 0x401000 - (load address)0x400000 = (RVA)0x1000。把RVA加上PE的載入點的實際位址就可以把RVA轉化實際位址。順便說一下，按PE的說法，記憶體中的實際位址稱為VA(VIRTUAL ADDRESS).不要忘了早點我說的PE的載入點就是HMODULE。 想對探索記憶體中的任意DLL嗎？用GetModuleHanle(LPCTSTR)取得載入點，用你的PE知識來幹活吧 5. 資料目錄 PE檔中有很多資料結構需要快速定位。顯然的例子有引入函數，引出函數，資源，重定位。這些東西是以一致的方式來定位的，這就是資料目錄。 資料目錄是一個結構陣列，包含16個結構。每個元素有一個定義好的標識，如下： 　// Export Directory 　#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 　// Import Directory 　#define IMAGE_DIRECTORY_ENTRY_IMPORT 1 　// Resource Directory 　#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 　// Exception Directory 　#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 　// Security Directory 　#define IMAGE_DIRECTORY_ENTRY_SECURITY 4 　// Base Relocation Table 　#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 　// Debug Directory 　#define IMAGE_DIRECTORY_ENTRY_DEBUG 6 　// Description String 　#define IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 　// Machine value (MIPS GP) 　#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 　// TLS Directory 　#define IMAGE_DIRECTORY_ENTRY_TLS 9 　// Load Configuration Directory 　#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 　typedef struct _IMAGE_DATA_DIRECTORY { 　　　ULONG VirtualAddress; 　　　ULONG Size; 　} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY; 6. 引入函數 當你使用別的DLL中的代碼或資料，稱為引入。當PE載入時，載入器的工作之一就是定位所有引入函數及資料，使那些位址對於載入的PE可見。具體細節在後面討論，在這裏只是大概講一下。 當你用到了一個DLL中的代碼或資料，你就暗中連接到這個DLL。但是你不必為&#8220;把這些位址變得對你的代碼有效&#8221;做任何事情，載入器為你做這些。方法之一就是顯式連接，這樣你就要確定DLL已被載入，及函數的位址。調用LOADLIBARY和GETPROCADDRESS就可以了。 當你暗式連接DLL，LOADLIBARY和GETPROCADDRESS同樣還是執行了的。只不過載入器為你做了這些。載入器還保證PE檔所需得任何附加的DLL都已被載入。比如，當你連接了KERNEL32.DLL,而它又引入了NTDLL.DLL的函數，又比如當你連接了GDI32.DLL,而它又依賴於USER32, ADVAPI32，NTDLL, 和 KERNEL32 DLLs的函數，載入器會保證這些DLL被載入及函數的決議。 暗式連接時，決議過程在PE檔在載入時就發生了。如果這時有什麼問題（比如這個DLL檔找不到），進程終止。 VISUAL C++ 6.0 加入了DLL的延遲載入的特徵。它是暗式連接和顯式連接的混合。當你延遲載入DLL，連接器做出一些和引入標準規則DLL類似的東西，但是作業系統卻不管這些東西，而是在第一次調用這個DLL中的函數的時候載入（如果還沒載入），然後調用GetProcAddress取得函數的位址。 對於pe檔要引入的dll都有一個對應的結構陣列，每個結構指出這個dll的名字及指向一個函數指標陣列的指標，這個函數指標陣列就是所謂的IAT（IMORT ADDRESS TABLE）。每個輸入函數，在IAT中都有一個保留槽，載入器將在那裏寫入真正的函數位址。最後特別重要一點的是：模組一旦載入，IAT中包含所要調用的引入函數的位址。 把所有輸入函數放在IAT一個地方是很有意義的，這樣無論代碼中多少次調用一個引入函數，都是通過IAT中的一個函數指標。 讓我們看看是怎樣調用一個引入函數的。有兩種情況需要考慮：有效率的和效率差的。最好的情況像下面這樣： 　CALL DWORD PTR [0x00405030] 直接調用[0x405030]中的函數，0x405030位於IAT部分。效率差的方式如下： 　CALL 0x0040100C 　... 　0x0040100C: 　JMP DWORD PTR [0x00405030] 這種情況，CALL把控制權轉到一個子程式，副程式中的JMP指令跳轉到位於IAT中的0x00405030，簡單說，它多用了5位元組和JMP多花的時間。 你可能驚訝引入函數就採用了這種方式，有個很好的解釋，編譯器無法區別引入函數的調用和普通函數調用，對於每個函數調用，編譯器只產生如下指令： 　CALL XXXXXXXX XXXXXXXX是一個由連接器填入的RVA。注意，這條指令不是通過函數指標來的，而是代碼中的實際地址。 為了因果的平衡，連接器必須產生一塊代碼來代替取代XXXXXXXX，簡單的方法就是象上面所示調用一個JMP STUB. 那麼JMP STUB 從那裏來呢？令人驚異的是，它取自輸入函數的引入庫。如果你去察看一個引入庫，在輸入函數名字的關聯處，你會發現與上面JMP STUB相似的指令。 接著，另一個問題就是如何優化這種形式，答案是你給編譯器的修飾符，__declspec(import) 修飾符告訴編譯器，這個函數來自另一個dll，這樣編譯器就會產生第一種指令。另外，編譯器將給函數加上__imp_首碼然後送給連接器決議，這樣可以直接把__imp_xxx送到iat,就不需要jmp stub了。 對於我們這有什麼意義呢，如果你在寫一個引出函數的東西並提供一個頭檔的話，別忘了在函數前加上修飾符__declspec(import) 　__declspec(dllimport) void Foo(void); 在winnt.h等系統頭檔中就是這樣做的。 7. PE 檔結構 現在讓我們開始研究PE檔格式，我將從檔的頭部開始，描述每個PE檔中都有的各種資料結構，然後，我將討論更多的專門的資料結構比如引入表和資源，除非特殊說明，這些結構都定義在WINNT.H中。 一般地，這些結構都有32和64位之分，如IMAGE_NT_HEADERS32 ,IMAGE_NT_HEADER64等，他們基本上是一樣的，除了64位的擴展了某些欄位。通過#DEFINE WINNT.H都遮罩了這些區別，選擇那個資料結構取決於你要如何編譯了（如，是否定義_WIN64） The MS-DOS Header 每個PE檔是以一個DOS程式開始的，這讓人想起WINDOWS在沒有如此可觀的使用者的早期年代。當可執行檔在非WINDOWS平臺上運行的時候至少可以顯示出一條資訊表示它需要WINDOWS。 PE檔的開頭是一個IMAGE_DOS_HEADER結構，結構中只有兩個重要的欄位e_magic and e_lfanew。e_lfanew指出pe file header的偏移，e_magic需要設定位0x5a4d，被#define 成IMAGE_DOS_SIGNATURE 它的ascii為&#8217;MZ&#8217;，Mark Zbikowski的首字母，DOS 的原始構建者之一。 The IMAGE_NT_HEADERS Header 這個結構是PE檔的主要定位資訊的所在。它的偏移由IMAGE_DOS_HEADER的e_lfanew給出 確實有64和32位之分，但我在討論中將不作考慮，他們幾乎沒有區別。 　typedef struct _IMAGE_NT_HEADERS { 　　DWORD Signature; 　　IMAGE_FILE_HEADER FileHeader; 　　IMAGE_OPTIONAL_HEADER32 OptionalHeader; 　} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32; 在一個有效的pe檔裏，Signture被設為0x00004500，ascii 為&#8217;PE00&#8217;，#define IMAGE_NT_SIGNTURE 0X00004500;第二個欄位是一個IMAGE_FILE_HEADER結構，它包含檔的基本資訊，特別重要的是它指出了IMAGE_OPTIONAL_HEADER的大小（重要嗎？）；在PE文件中，IMAGE_OPTIONAL_HEADER是非常重要的，但是仍稱作IMAGE_OPTIONAL_HEADER。 IMAGE_OPTIONAL_HEADER結構的末尾就是用來定位pe檔中重要資訊的位址簿-資料目錄，它的定義如下： 　typedef struct _IMAGE_DATA_DIRECTORY { 　　DWORD VirtualAddress; // RVA of the data 　　DWORD Size; // Size of the data 　}; The Section Table 緊接著IMAGE_NT_HEADERS後的就是節表，節表就是IMAGE_SECTION_HEADER的陣列。IMAGE_SECTION_HEADER包含了它所關聯的節的資訊，如位置，長度，特徵；該陣列的數目由IMAGE_NT_HEADERS.FileHeader.NumberOfSections指出。具體見下圖 PE中的節的大小的總和最後是要對齊的，Visual Studio 6.0中的預設值是4k，除非你使用/OPT:NOWIN98 或/ALIGN開關；在.NET中，依然用了默認的/OPT:WIN98，但是如果檔小於一特定大小時，就會採用0X200為對齊值。 .NET文檔中有關於對齊的另一件有趣的事。.NET檔的記憶體對齊值為8K而不是普通X86平臺上的4K，這樣就保證了在X86平臺編譯的程式可以在IA-64平臺上運行。如果記憶體對齊值為4K，那麼IA-64的載入器就不能載入這個程式，因為它的頁為8K 
<img src ="http://www.cppblog.com/justin-shi/aggbug/57323.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/justin-shi/" target="_blank">幽幽</a> 2008-07-28 04:04 <a href="http://www.cppblog.com/justin-shi/archive/2008/07/28/57323.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>难以忘怀的Watcom（转《Borland传奇》）</title><link>http://www.cppblog.com/justin-shi/archive/2008/07/26/57224.html</link><dc:creator>幽幽</dc:creator><author>幽幽</author><pubDate>Sat, 26 Jul 2008 03:52:00 GMT</pubDate><guid>http://www.cppblog.com/justin-shi/archive/2008/07/26/57224.html</guid><wfw:comment>http://www.cppblog.com/justin-shi/comments/57224.html</wfw:comment><comments>http://www.cppblog.com/justin-shi/archive/2008/07/26/57224.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/justin-shi/comments/commentRss/57224.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/justin-shi/services/trackbacks/57224.html</trackback:ping><description><![CDATA[在看yoda's Protector源代码的时候，发现<br>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;DWORD&nbsp;ALIGN_CORRECTION&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">0x1000</span><span style="COLOR: #000000">;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;this&nbsp;big&nbsp;value&nbsp;is&nbsp;e.g.&nbsp;needed&nbsp;for&nbsp;WATCOM&nbsp;compiled&nbsp;files</span></div>
上网一查，发现WATCOM竟然有这样一段传奇：<br><br><br>一、Watcom的发展史 <br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在编译器混战的时代，一家加拿大的小公司出品了Watcom C/C++编译器，但是以在DOS下能够产生最佳化程序代码闻名于世的，许多写游戏和DOS Extender的厂商都指名要使用Watcom C/C++，因为不论是Borland C/C++还是Visual C/C++，它们产生的最佳化程序代码都比Watcom C/C++的最佳化程序代码差上一截。再加上当时最有名的DOS Extender厂商PharLap公司也是使用Watcom C/C++，因此Watcom C/C++在当时专业的C/C++程序员以及系统程序员心中是第一品牌的C/C++开发工具。 <br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Watcom C/C++在DOS市场站稳了脚跟之后，由于Windows已经逐渐成为市场的主流，DOS势必将被逐渐淘汰出局，因此，Watcom C/C++如果要继续生存下去，也就一定要推出Windows平台的C/C++开发工具。大约是在1993、1994年左右，Watcom终于推出第一个Windows下的C/C++开发工具。 <br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 不过，当时Watcom C/C++在Windows推出的C/C++开发工具实在是平淡无奇。其集成开发环境和另外三个对手比较起来简直像是远古的产品，一点特色都没有。不过Watcom C/C++仍然是以它的最佳化编译器作为号召。因此当时发生了一个非常有趣的现象，那就是许多软件公司会同时买Borland C/C++，或是Visual C/C++，Symantec C/C++之一，再搭配一套Watcom C/C++。在开发应用系统时使用其他三套开发工具之一，最后要出货时再使用Watcom C/C++来编译以产生最佳的程序代码。 <br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在Watcom C/C++推出了Windows平台的开发工具之后，也吸引了一群使用者。虽然Watcom C/C++的市场比起其他的三家来说是最小的，但是总算撑起了一片天，成为四大C/C++开发工具之一。稍后Watcom C/C++被Sybase并购，成为Sybase的Optima++的前身。 <br><br>二、石破天惊还是巨星陨落 <br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1996年左右，Sybase并购了Watcom之后终于推出了石破天惊的C/C++开发工具：Optima++。Optima++是当初结合了Watcom的最佳化编译器以及类似Delphi的组件拖曳开发环境的第一个RAD C/C++开发工具。更棒的是Optima++的组件架构（类似Delphi的VCL）完全是以纯正的C/C++程序代码撰写的。这可不得了，因为这代表Optima++是一个融合了Visual C/C++和Delphi两大王者开发工具为一身的超级赛亚人工具。 <br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在我（《Borland传奇》作者李维，下同）知道这个工具、并且尝试实际使用之后，极为震惊。因为对于我这个使用了C/C++ 五六年的人来说，它比Delphi更具有吸引力。因此我立刻在《RUN!PC》上介绍了这个不可置信的工具。果然，Optima++很快开始风靡市场，虽然没有立刻占据很大的市场份额，但是已经造成了一股气势，开始为Visual C/C++和Delphi带来压力。 <br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我记得当时台湾Sybase办的产品发表会也吸引了数百人与会，不可一世。我的文章在《RUN!PC》6上发表之后，台湾的Sybase立刻和我联络，由当时的余协理和我见面，也是希望我继续为Optima++写文章，台湾Sybase也提供额外一字加2元稿费的待遇。但是我告诉余协理，Optima++ 1.0虽然很棒，但是仍然有一些臭虫，而且和中文环境相冲突，无法处理中文，需要立刻解决这个问题才能够在台湾的市场成功。她答应我立刻向总公司反映。我也老实地告诉她，在问题没有解决之前，我无法写一些不确实的东西。后来台湾Borland的总经理方先生也找我去询问有关Optima++的事情，我告诉他Optima++是好东西，但是中文有问题。如果中文问题能够解决，那么将对Borland和Microsoft的产品有很大的影响，当时我还不知道Borland由于Optima++的影响，已经开始准备开发C++ Builder。 <br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在1996年底左右吧，Optima++ 1.5终于进入Beta的阶段。但是在我拿到Beta版时非常失望，因为中文的问题仍然没有解决。后来台湾Sybase又找我去，这次和我见面的是台湾Sybase总经理郭俊男先生，以及Sybase的新加坡技术总裁，不过我忘记这位先生的名字了。见了面之后，我立刻把Optima++ 1.5中文的问题以及许多的臭虫告诉他们，希望他们能够解决，如此Optima++ 1.5才能够在中文市场成功。可是出乎我意料之外的是，他们似乎并不着急这些问题，反而询问我是否有意愿为Sybase工作，做PowerBuilder的产品经理。 <br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 也许是因为我为Delphi写了太多的东西，让PowerBuilder在台湾受了很大的影响，因此他们希望我到Sybase工作，以打击Delphi并且Promote PowerBuilder。当时他们提出的待遇条件实在是非常、非常的诱人，比我当时的薪水高出一倍左右（我当时在资策会工作）。不过由于我对PowerBuilder实在没有什么兴趣，因此我告诉他们，如果是做Optima++的产品经理，那么我将会考虑并且接受。 <br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 没有想到，Sybase的新加坡技术总裁告诉我Optima++在1.5推出之后就可能会停止，因为Sybase要把资源移去为当时愈来愈红的Java研发一个新的Java RAD开发工具，那就是后来的PowerJ。于是他询问我如果不愿意做PowerBuilder的产品经理，那么是不是愿意做PowerJ的产品经理？由于当时我已经知道Borland开始了Open JBuilder的研发，而我对Open JBuilder的兴趣远大于PowerJ，因此没有答应Sybase。果然，在Optima++ 1.5推出之后，不但中文的问题没有解决，Sybase之后也没有继续对Optima++研发下去。 <br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Optima++一个如此有潜力的产品就这样消失了，真是令人遗憾。Optima++应该有很好的机会可以成功。我相信，如果当时Sybase知道C++ Builder后来的成果，可能就不会放弃Optima++了，而C/C++的RAD工具一直要到后来的C++ Builder来完成这个梦。 <br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 至此，和Visual C/C++竞争的只有Borland的编译器了，然而虽然后来Borland继续推出了Borland C/C++ 5.0，但是品质仍然不够好，市场反应也不佳。后来终于在Borland C/C++ 5.02之后宣布停止此条产品线的开发，Borland C/C++的光荣历史也就从此打住，真是令人不胜感叹，而Visual C/C++从此在C/C++开发工具市场中再也没有对手。不过没有竞争的市场的确会让人松懈，后来的Visual C/C++进步的幅度愈来愈小，MFC也数年没有什么大进步，不像当时和Borland C/C++竞争时每一个版本都有大幅的改善。看来寡占的市场的确是不好的，这也让人回想起Visual C/C++、Borland C/C++、Symantec C/C++、Watcom C/C++四雄逐鹿的辉煌时代了。 <br><br>三、开源潮流 <br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Watcom C/C++产生目标程序的质量还是非常让人难忘的，这也是不少程序员（尤其是游戏程序员）青睐于这个编译器的原因，这也促成了OpenWatcom C/C++的诞生，免费、开源，也希望很多的人使用，最新版支持C/C++/Fortran的编译。<br><br>
<img src ="http://www.cppblog.com/justin-shi/aggbug/57224.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/justin-shi/" target="_blank">幽幽</a> 2008-07-26 11:52 <a href="http://www.cppblog.com/justin-shi/archive/2008/07/26/57224.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>如何计算滑块长度</title><link>http://www.cppblog.com/justin-shi/archive/2008/07/13/56050.html</link><dc:creator>幽幽</dc:creator><author>幽幽</author><pubDate>Sun, 13 Jul 2008 14:23:00 GMT</pubDate><guid>http://www.cppblog.com/justin-shi/archive/2008/07/13/56050.html</guid><wfw:comment>http://www.cppblog.com/justin-shi/comments/56050.html</wfw:comment><comments>http://www.cppblog.com/justin-shi/archive/2008/07/13/56050.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/justin-shi/comments/commentRss/56050.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/justin-shi/services/trackbacks/56050.html</trackback:ping><description><![CDATA[滑块长度　＝（int）(GetScrollInfo.nPage*(GetScrollInfo.nPage-32)/GetScrollInfo.nMax)
<img src ="http://www.cppblog.com/justin-shi/aggbug/56050.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/justin-shi/" target="_blank">幽幽</a> 2008-07-13 22:23 <a href="http://www.cppblog.com/justin-shi/archive/2008/07/13/56050.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>常用的工具函数</title><link>http://www.cppblog.com/justin-shi/archive/2008/06/23/54325.html</link><dc:creator>幽幽</dc:creator><author>幽幽</author><pubDate>Sun, 22 Jun 2008 19:59:00 GMT</pubDate><guid>http://www.cppblog.com/justin-shi/archive/2008/06/23/54325.html</guid><wfw:comment>http://www.cppblog.com/justin-shi/comments/54325.html</wfw:comment><comments>http://www.cppblog.com/justin-shi/archive/2008/06/23/54325.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/justin-shi/comments/commentRss/54325.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/justin-shi/services/trackbacks/54325.html</trackback:ping><description><![CDATA[<div class=cnt id=blog_text>
<p>1　将Cstring转成TCHAR*</p>
<p>StrToTchar(CString temString,TCHAR * pTchar)<br>{<br>int tmpLength=0 ;<br>tmpLength = temString.GetLength();<br>TCHAR * ptemTchar = temString.GetBuffer(tmpLength);<br>wcscpy( pTchar,ptemTchar);<br>}</p>
<p>２　获取系统时间</p>
<p>CString GetSysTime()<br>{//得到系统时间<br>CString strTime;<br>SYSTEMTIME st;<br>::GetLocalTime(&amp;st);<br>TCHAR datetime[20];<br>TCHAR times[20];<br>GetDateFormat(LOCALE_USER_DEFAULT,NULL,&amp;st,_T("yyyy-MM-dd"),datetime,20);<br>GetTimeFormat(LOCALE_USER_DEFAULT,NULL,&amp;st,_T("HH:mm:ss"),times,20);<br>strTime = (CString)datetime + _T(" ")+(CString)times;<br>return strTime;<br>}</p>
<p>&#160;</p>
<p>CTime t = CTime::GetCurrentTime();<br>SelfInfo[8] = IntToStr(t.GetYear());<br>SelfInfo[9] = IntToStr(t.GetMonth());<br>SelfInfo[10] = IntToStr(t.GetDay());</p>
<p>&#160;</p>
<p>３　写日志</p>
<p><br>void Writelog(char *str)<br>{<br>FILE *fp;<br>fp = fopen("\\Storage\\LogFile.txt","w+");<br>if(!fp)<br>{<br>&nbsp;&nbsp; return;<br>}<br>fprintf(fp,"%s\n",str);<br>fclose(fp);<br>return;<br>}<br><br><br></p>
</div>
<img src ="http://www.cppblog.com/justin-shi/aggbug/54325.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/justin-shi/" target="_blank">幽幽</a> 2008-06-23 03:59 <a href="http://www.cppblog.com/justin-shi/archive/2008/06/23/54325.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【转】C++ int，char，string，CString类型转换（整理总结）</title><link>http://www.cppblog.com/justin-shi/archive/2008/06/14/53195.html</link><dc:creator>幽幽</dc:creator><author>幽幽</author><pubDate>Fri, 13 Jun 2008 21:11:00 GMT</pubDate><guid>http://www.cppblog.com/justin-shi/archive/2008/06/14/53195.html</guid><wfw:comment>http://www.cppblog.com/justin-shi/comments/53195.html</wfw:comment><comments>http://www.cppblog.com/justin-shi/archive/2008/06/14/53195.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/justin-shi/comments/commentRss/53195.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/justin-shi/services/trackbacks/53195.html</trackback:ping><description><![CDATA[<div>
<p>#include &lt;string&gt; <wbr></wbr>//使用C++标准库的string类时</p>
<p>using namespace std; <wbr></wbr>//同上</p>
<p>#include &lt;sstream&gt; <wbr></wbr><wbr></wbr></p>
<p>#include &lt;iostream&gt;</p>
<p>#include &lt;stdlib.h&gt; <wbr></wbr><wbr></wbr><wbr></wbr>//要将string类和int类型直接转换最好有这些包含，</p>
<p><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr>//因为自己写一个转换函数比较方便，函数定义参考如下</p>
<p>string getstring ( const int n )</p>
<p>{</p>
<p><wbr></wbr><wbr></wbr><wbr></wbr><font face=宋体>std::stringstream newstr;<br><wbr></wbr><wbr></wbr><wbr></wbr>newstr&lt;&lt;n;<br><wbr></wbr><wbr></wbr><wbr></wbr>return newstr.str();</font></p>
<p>}</p>
<p><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr><wbr></wbr></p>
<p>string 转 CString<br>CString.format(&#8221;%s&#8221;, string.c_str());</p>
<p>char 转 CString<br>CString.format(&#8221;%s&#8221;, char*);</p>
<p>char 转 string<br>string s(char *);</p>
<p>string 转 char *<br>char *p = string.c_str();</p>
<p>CString 转 string<br>string s(CString.GetBuffer());</p>
<p>1，string -&gt; CString<br>CString.format(&#8221;%s&#8221;, string.c_str());<br>用c_str()确实比data()要好.<br>2，char -&gt; string<br>string s(char *);<br>只能初始化，在不是初始化的地方最好还是用assign().<br>3,CString -&gt; string<br>string s(CString.GetBuffer());<br>GetBuffer()后一定要ReleaseBuffer(),否则就没有释放缓冲区所占的空间.</p>
<p>《C++标准函数库》中说的<br>有三个函数可以将字符串的内容转换为字符数组和C—string<br>1.data(),返回没有&#8221;\0&#8220;的字符串数组<br>2,c_str()，返回有&#8221;\0&#8220;的字符串数组<br>3，copy()</p>
<p>—————————————————————</p>
<p>CString与int、char*、char[100]之间的转换- -</p>
<p>CString与int、char*、char[100]之间的转换- -</p>
<p>CString互转int</p>
<p>将字符转换为整数，可以使用atoi、_atoi64或atol。<br>而将数字转换为CString变量，可以使用CString的Format函数。如<br>CString s;<br>int i = 64;<br>s.Format(&#8221;%d&#8221;, i)<br>Format函数的功能很强，值得你研究一下。</p>
<p>void CStrDlg::OnButton1()<br>{<br>// TODO: Add your control notification handler code here<br>CString<br>ss=&#8221;1212.12&#8243;;<br>int temp=atoi(ss);<br>CString aa;<br>aa.Format(&#8221;%d&#8221;,temp);<br>AfxMessageBox(&#8221;var is &#8221; + aa);<br>}</p>
<p>sart.Format(&#8221;%s&#8221;,buf);</p>
<p>CString互转char*</p>
<p>///char * TO cstring<br>CString strtest;<br>char * charpoint;<br>charpoint=&#8221;give string a value&#8221;;<br>strtest=charpoint;</p>
<p>///cstring TO char *<br>charpoint=strtest.GetBuffer(strtest.GetLength());</p>
<p>标准C里没有string,char *==char []==string</p>
<p>可以用CString.Format(&#8221;%s&#8221;,char *)这个方法来将char *转成CString。要把CString转成char *，用操作符（LPCSTR）CString就可以了。</p>
<p>CString转换 char[100]</p>
<p>char a[100];<br>CString str(&#8221;aaaaaa&#8221;);<br>strncpy(a,(LPCTSTR)str,sizeof(a));</p>
</div>
<br>
<img src ="http://www.cppblog.com/justin-shi/aggbug/53195.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/justin-shi/" target="_blank">幽幽</a> 2008-06-14 05:11 <a href="http://www.cppblog.com/justin-shi/archive/2008/06/14/53195.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>软件工程师笔试题（C/C++）</title><link>http://www.cppblog.com/justin-shi/archive/2008/06/11/52825.html</link><dc:creator>幽幽</dc:creator><author>幽幽</author><pubDate>Tue, 10 Jun 2008 18:46:00 GMT</pubDate><guid>http://www.cppblog.com/justin-shi/archive/2008/06/11/52825.html</guid><wfw:comment>http://www.cppblog.com/justin-shi/comments/52825.html</wfw:comment><comments>http://www.cppblog.com/justin-shi/archive/2008/06/11/52825.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/justin-shi/comments/commentRss/52825.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/justin-shi/services/trackbacks/52825.html</trackback:ping><description><![CDATA[<p>&nbsp;</p>
<strong>预处理器（Preprocessor）<br></strong>1. 用预处理指令#define 声明一个常数，用以表明1年中有多少秒（忽略闰年问题）<br><br>#define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL <br>我在这想看到几件事情： <br>1). #define 语法的基本知识（例如：不能以分号结束，括号的使用，等等） <br>2). 懂得预处理器将为你计算常数表达式的值，因此，直接写出你是如何计算一年中有多少秒而不是计算出实际的值，是更清晰而没有代价的。 <br>3). 意识到这个表达式将使一个16位机的整型数溢出-因此要用到长整型符号L,告诉编译器这个常数是的长整型数。 <br>4). 如果你在你的表达式中用到UL（表示无符号长整型），那么你有了一个好的起点。记住，第一印象很重要。<br><br><br>2. 写一个&#8220;标准&#8221;宏MIN，这个宏输入两个参数并返回较小的一个。<br><br><br>#define MIN(A,B) ((A) &lt;= (B) (A) : (B)) <br>这个测试是为下面的目的而设的： <br>1). 标识#define在宏中应用的基本知识。这是很重要的，因为直到嵌入(inline)操作符变为标准C的一部分，宏是方便产生嵌入代码的唯一方法，对于嵌入式系统来说，为了能达到要求的性能，嵌入代码经常是必须的方法。 <br>2). 三重条件操作符的知识。这个操作符存在C语言中的原因是它使得编译器能产生比if-then-else更优化的代码，了解这个用法是很重要的。 <br>3). 懂得在宏中小心地把参数用括号括起来 <br>4). 我也用这个问题开始讨论宏的副作用，例如：当你写下面的代码时会发生什么事？ <br>least = MIN(*p++, b);<br><br><br>3. 预处理器标识#error的目的是什么？<br><br>如果你不知道答案，请看参考文献1。这问题对区分一个正常的伙计和一个书呆子是很有用的。只有书呆子才会读C语言课本的附录去找出象这种 <br>问题的答案。当然如果你不是在找一个书呆子，那么应试者最好希望自己不要知道答案。<br><br><br><strong>死循环（Infinite loops）</strong><br><br><br>4. 嵌入式系统中经常要用到无限循环，你怎么样用C编写死循环呢？<br><br>这个问题用几个解决方案。我首选的方案是： <br>while(1) { } <br>一些程序员更喜欢如下方案： <br>for(;;) { } <br>这个实现方式让我为难，因为这个语法没有确切表达到底怎么回事。如果一个应试者给出这个作为方案，我将用这个作为一个机会去探究他们这样做的 <br>基本原理。如果他们的基本答案是：&#8220;我被教着这样做，但从没有想到过为什么。&#8221;这会给我留下一个坏印象。 <br>第三个方案是用 goto <br>Loop: <br>... <br>goto Loop; <br>应试者如给出上面的方案，这说明或者他是一个汇编语言程序员（这也许是好事）或者他是一个想进入新领域的BASIC/FORTRAN程序员。<br><br><strong>数据声明（Data declarations）</strong> <br><br>5. 用变量a给出下面的定义<br>a) 一个整型数（An integer） <br>b) 一个指向整型数的指针（A pointer to an integer） <br>c) 一个指向指针的的指针，它指向的指针是指向一个整型数（A pointer to a pointer to an integer） <br>d) 一个有10个整型数的数组（An array of 10 integers） <br>e) 一个有10个指针的数组，该指针是指向一个整型数的（An array of 10 pointers to integers） <br>f) 一个指向有10个整型数数组的指针（A pointer to an array of 10 integers） <br>g) 一个指向函数的指针，该函数有一个整型参数并返回一个整型数（A pointer to a function that takes an integer as an argument and returns an integer） <br>h) 一个有10个指针的数组，该指针指向一个函数，该函数有一个整型参数并返回一个整型数（ An array of ten pointers to functions that take an integer argument and return an integer ）<br><br>答案是： <br>a) int a; // An integer <br>b) int *a; // A pointer to an integer <br>c) int **a; // A pointer to a pointer to an integer <br>d) int a[10]; // An array of 10 integers <br>e) int *a[10]; // An array of 10 pointers to integers <br>f) int (*a)[10]; // A pointer to an array of 10 integers <br>g) int (*a)(int); // A pointer to a function a that takes an integer argument and returns an integer <br>h) int (*a[10])(int); // An array of 10 pointers to functions that take an integer argument and return an integer <br><br><br>人们经常声称这里有几个问题是那种要翻一下书才能回答的问题，我同意这种说法。当我写这篇文章时，为了确定语法的正确性，我的确查了一下书。 <br>但是当我被面试的时候，我期望被问到这个问题（或者相近的问题）。因为在被面试的这段时间里，我确定我知道这个问题的答案。应试者如果不知道 <br>所有的答案（或至少大部分答案），那么也就没有为这次面试做准备，如果该面试者没有为这次面试做准备，那么他又能为什么出准备呢？<br><br><br><strong>Static</strong><br><br>6. 关键字static的作用是什么？<br><br>这个简单的问题很少有人能回答完全。在C语言中，关键字static有三个明显的作用： <br>1). 在函数体，一个被声明为静态的变量在这一函数被调用过程中维持其值不变。 <br>2). 在模块内（但在函数体外），一个被声明为静态的变量可以被模块内所用函数访问，但不能被模块外其它函数访问。它是一个本地的全局变量。 <br>3). 在模块内，一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是，这个函数被限制在声明它的模块的本地范围内使用。 <br>大多数应试者能正确回答第一部分，一部分能正确回答第二部分，同是很少的人能懂得第三部分。这是一个应试者的严重的缺点，因为他显然不懂得本地化数据和代码范围的好处和重要性。<br><br><br><strong>Const </strong><br><br>7．关键字const是什么含意？<br>我只要一听到被面试者说：&#8220;const意味着常数&#8221;，我就知道我正在和一个业余者打交道。去年Dan Saks已经在他的文章里完全概括了const的所有用法，因此ESP(译者：Embedded Systems Programming)的每一位读者应该非常熟悉const能做什么和不能做什么.如果你从没有读到那篇文章，只要能说出const意味着&#8220;只读&#8221;就可以了。尽管这个答案不是完全的答案，但我接受它作为一个正确的答案。（如果你想知道更详细的答案，仔细读一下Saks的文章吧。）如果应试者能正确回答这个问题，我将问他一个附加的问题：下面的声明都是什么意思？<br><br>const int a; <br>int const a; <br>const int *a; <br>int * const a; <br>int const * a const;<br><br>前两个的作用是一样，a是一个常整型数。第三个意味着a是一个指向常整型数的指针（也就是，整型数是不可修改的，但指针可以）。第四个意思a是一个指向整型数的常指针（也就是说，指针指向的整型数是可以修改的，但指针是不可修改的）。最后一个意味着a是一个指向常整型数的常指针（也就是说，指针指向的整型数是不可修改的，同时指针也是不可修改的）。如果应试者能正确回答这些问题，那么他就给我留下了一个好印象。顺带提一句，也许你可能会问，即使不用关键字const，也还是能很容易写出功能正确的程序，那么我为什么还要如此看重关键字const呢？我也如下的几下理由： <br>1). 关键字const的作用是为给读你代码的人传达非常有用的信息，实际上，声明一个参数为常量是为了告诉了用户这个参数的应用目的。如果你曾花很多时间清理其它人留下的垃圾，你就会很快学会感谢这点多余的信息。（当然，懂得用const的程序员很少会留下的垃圾让别人来清理的。） <br>2). 通过给优化器一些附加的信息，使用关键字const也许能产生更紧凑的代码。 <br>3). 合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数，防止其被无意的代码修改。简而言之，这样可以减少bug的出现。<br><br><strong>Volatile </strong><br><br>8. 关键字volatile有什么含意 并给出三个不同的例子。<br><br>一个定义为volatile的变量是说这变量可能会被意想不到地改变，这样，编译器就不会去假设这个变量的值了。精确地说就是，优化器在用到这个变量时必须每次都小心地重新读取这个变量的值，而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子： <br>1). 并行设备的硬件寄存器（如：状态寄存器） <br>2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables) <br>3). 多线程应用中被几个任务共享的变量 <br>回答不出这个问题的人是不会被雇佣的。我认为这是区分C程序员和嵌入式系统程序员的最基本的问题。嵌入式系统程序员经常同硬件、中断、RTOS等等打交道，所用这些都要求volatile变量。不懂得volatile内容将会带来灾难。 <br>假设被面试者正确地回答了这是问题（嗯，怀疑这否会是这样），我将稍微深究一下，看一下这家伙是不是直正懂得volatile完全的重要性。 <br>1). 一个参数既可以是const还可以是volatile吗？解释为什么。 <br>2). 一个指针可以是volatile 吗？解释为什么。 <br>3). 下面的函数有什么错误： <br>int square(volatile int *ptr) <br>{ <br>return *ptr * *ptr; <br>} <br>下面是答案： <br>1). 是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。 <br>2). 是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。 <br>3). 这段代码的有个恶作剧。这段代码的目的是用来返指针*ptr指向值的平方，但是，由于*ptr指向一个volatile型参数，编译器将产生类似下面的代码： <br>int square(volatile int *ptr) <br>{ <br>int a,b; <br>a = *ptr; <br>b = *ptr; <br>return a * b; <br>} <br>由于*ptr的值可能被意想不到地该变，因此a和b可能是不同的。结果，这段代码可能返不是你所期望的平方值！正确的代码如下： <br>long square(volatile int *ptr) <br>{ <br>int a; <br>a = *ptr; <br>return a * a; <br>}<br><br><strong>位操作（Bit manipulation）</strong><br><br>9. 嵌入式系统总是要用户对变量或寄存器进行位操作。给定一个整型变量a，写两段代码，第一个设置a的bit 3，第二个清除a 的bit 3。在以上两个操作中，要保持其它位不变。<br><br>对这个问题有三种基本的反应 <br>1). 不知道如何下手。该被面者从没做过任何嵌入式系统的工作。 <br>2). 用bit fields。Bit fields是被扔到C语言死角的东西，它保证你的代码在不同编译器之间是不可移植的，同时也保证了的你的代码是不可重用的。我最近不幸看到Infineon为其较复杂的通信芯片写的驱动程序，它用到了bit fields因此完全对我无用，因为我的编译器用其它的方式来实现bit fields的。从道德讲：永远不要让一个非嵌入式的家伙粘实际硬件的边。 <br>3). 用 #defines 和 bit masks 操作。这是一个有极高可移植性的方法，是应该被用到的方法。最佳的解决方案如下： <br>#define BIT3 (0x1&lt;&lt;3) <br>static int a; <br>void set_bit3(void) <br>{ <br>a |= BIT3; <br>} <br>void clear_bit3(void) <br>{ <br>a &amp;= ~BIT3; <br>} <br>一些人喜欢为设置和清除值而定义一个掩码同时定义一些说明常数，这也是可以接受的。我希望看到几个要点：说明常数、|=和&amp;=~操作。<br><br><strong>访问固定的内存位置（Accessing fixed memory locations）</strong> <br><br>10. 嵌入式系统经常具有要求程序员去访问某特定的内存位置的特点。在某工程中，要求设置一绝对地址为0x67a9的整型变量的值为0xaa66。编译器是一个纯粹的ANSI编译器。写代码去完成这一任务。<br><br>这一问题测试你是否知道为了访问一绝对地址把一个整型数强制转换（typecast）为一指针是合法的。这一问题的实现方式随着个人风格不同而不同。典型的类似代码如下： <br>int *ptr; <br>ptr = (int *)0x67a9; <br>*ptr = 0xaa55;<br><br>一个较晦涩的方法是： <br>*(int * const)(0x67a9) = 0xaa55;<br><br>即使你的品味更接近第二种方案，但我建议你在面试时使用第一种方案。<br><br><strong>中断（Interrupts） </strong><br><br>11. 中断是嵌入式系统中重要的组成部分，这导致了很多编译开发商提供一种扩展—让标准C支持中断。具代表事实是，产生了一个新的关键字__interrupt。下面的代码就使用了__interrupt关键字去定义了一个中断服务子程序(ISR)，请评论一下这段代码的。<br><br>__interrupt double compute_area (double radius) <br>{ <br>&nbsp;&nbsp;&nbsp;&nbsp; double area = PI * radius * radius; <br>&nbsp;&nbsp;&nbsp;&nbsp; printf(" Area = %f", area); <br>&nbsp;&nbsp;&nbsp;&nbsp; return area; <br>}<br><br>这个函数有太多的错误了，以至让人不知从何说起了： <br>1). ISR 不能返回一个值。如果你不懂这个，那么你不会被雇用的。 <br>2). ISR 不能传递参数。如果你没有看到这一点，你被雇用的机会等同第一项。 <br>3). 在许多的处理器/编译器中，浮点一般都是不可重入的。有些处理器/编译器需要让额处的寄存器入栈，有些处理器/编译器就是不允许在ISR中做浮点运算。此外，ISR应该是短而有效率的，在ISR中做浮点运算是不明智的。 <br>4). 与第三点一脉相承，printf()经常有重入和性能上的问题。如果你丢掉了第三和第四点，我不会太为难你的。不用说，如果你能得到后两点，那么你的被雇用前景越来越光明了。<br><br><strong>代码例子（Code examples）</strong><br><br>12 . 下面的代码输出是什么，为什么？<br><br>void foo(void) <br>{ <br>&nbsp;&nbsp;&nbsp;&nbsp; unsigned int a = 6; <br>&nbsp;&nbsp;&nbsp;&nbsp; int b = -20; <br>&nbsp;&nbsp;&nbsp;&nbsp; (a+b &gt; 6) puts("&gt; 6") : puts("&lt;= 6"); <br>}<br><br>这个问题测试你是否懂得C语言中的整数自动转换原则，我发现有些开发者懂得极少这些东西。不管如何，这无符号整型问题的答案是输出是&#8220;&gt;6&#8221;。原因是当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型。 因此-20变成了一个非常大的正整数，所以该表达式计算出的结果大于6。这一点对于应当频繁用到无符号数据类型的嵌入式系统来说是丰常重要的。如果你答错了这个问题，你也就到了得不到这份工作的边缘。<br><br>13. 评价下面的代码片断：<br><br>unsigned int zero = 0; <br>unsigned int compzero = 0xFFFF; <br>/*1's complement of zero */<br><br>对于一个int型不是16位的处理器为说，上面的代码是不正确的。应编写如下：<br><br>unsigned int compzero = ~0;<br><br>这一问题真正能揭露出应试者是否懂得处理器字长的重要性。在我的经验里，好的嵌入式程序员非常准确地明白硬件的细节和它的局限，然而PC机程序往往把硬件作为一个无法避免的烦恼。 <br>到了这个阶段，应试者或者完全垂头丧气了或者信心满满志在必得。如果显然应试者不是很好，那么这个测试就在这里结束了。但如果显然应试者做得不错，那么我就扔出下面的追加问题，这些问题是比较难的，我想仅仅非常优秀的应试者能做得不错。提出这些问题，我希望更多看到应试者应付问题的方法，而不是答案。不管如何，你就当是这个娱乐吧&#8230;<br><br><strong>动态内存分配（Dynamic memory allocation）</strong><br><br>14. 尽管不像非嵌入式计算机那么常见，嵌入式系统还是有从堆（heap）中动态分配内存的过程的。那么嵌入式系统中，动态分配内存可能发生的问题是什么？<br><br>这里，我期望应试者能提到内存碎片，碎片收集的问题，变量的持行时间等等。这个主题已经在ESP杂志中被广泛地讨论过了（主要是 P.J. Plauger, 他的解释远远超过我这里能提到的任何解释），所有回过头看一下这些杂志吧！让应试者进入一种虚假的安全感觉后，我拿出这么一个小节目：下面的代码片段的输出是什么，为什么？<br><br>char *ptr; <br>if ((ptr = (char *)malloc(0)) == NULL) <br>puts("Got a null pointer"); <br>else <br>puts("Got a valid pointer"); <br><br>这是一个有趣的问题。最近在我的一个同事不经意把0值传给了函数malloc，得到了一个合法的指针之后，我才想到这个问题。这就是上面的代码，该代码的输出是&#8220;Got a valid pointer&#8221;。我用这个来开始讨论这样的一问题，看看被面试者是否想到库例程这样做是正确。得到正确的答案固然重要，但解决问题的方法和你做决定的基本原理更重要些。<br><br>Typedef <br><br>15. Typedef 在C语言中频繁用以声明一个已经存在的数据类型的同义字。也可以用预处理器做类似的事。例如，思考一下下面的例子：<br>#define dPS struct s * <br>typedef struct s * tPS; <br><br>以上两种情况的意图都是要定义dPS 和 tPS 作为一个指向结构s指针。哪种方法更好呢？（如果有的话）为什么？ <br>这是一个非常微妙的问题，任何人答对这个问题（正当的原因）是应当被恭喜的。答案是：typedef更好。思考下面的例子： <br>dPS p1,p2; <br>tPS p3,p4;<br><br>第一个扩展为 <br>struct s * p1, p2;<br><br>上面的代码定义p1为一个指向结构的指，p2为一个实际的结构，这也许不是你想要的。第二个例子正确地定义了p3 和p4 两个指针。<br><br><br>16. C语言同意一些令人震惊的结构,下面的结构是合法的吗，如果是它做些什么？ <br>int a = 5, b = 7, c; <br>c = a+++b;<br><br>这个问题将做为这个测验的一个愉快的结尾。不管你相不相信，上面的例子是完全合乎语法的。问题是编译器如何处理它？水平不高的编译作者实际上会争论这个问题，根据最处理原则，编译器应当能处理尽可能所有合法的用法。因此，上面的代码被处理成： <br>c = a++ + b; <br>因此, 这段代码持行后a = 6, b = 7, c = 12。 <br><br>
<img src ="http://www.cppblog.com/justin-shi/aggbug/52825.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/justin-shi/" target="_blank">幽幽</a> 2008-06-11 02:46 <a href="http://www.cppblog.com/justin-shi/archive/2008/06/11/52825.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>抨击匈牙利命名法</title><link>http://www.cppblog.com/justin-shi/archive/2008/05/02/48615.html</link><dc:creator>幽幽</dc:creator><author>幽幽</author><pubDate>Thu, 01 May 2008 18:26:00 GMT</pubDate><guid>http://www.cppblog.com/justin-shi/archive/2008/05/02/48615.html</guid><wfw:comment>http://www.cppblog.com/justin-shi/comments/48615.html</wfw:comment><comments>http://www.cppblog.com/justin-shi/archive/2008/05/02/48615.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/justin-shi/comments/commentRss/48615.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/justin-shi/services/trackbacks/48615.html</trackback:ping><description><![CDATA[抨击匈牙利命名法<br><br>匈牙利命名法是一种编程时的命名规范。命名规范是程序书写规范中最重要也是最富争议的地方，自古乃兵家必争之地。命名规范有何用？四个字：名正言顺。用二分法，命名规范分为好的命名规范和坏的命名规范，也就是说名正言顺的命名规范和名不正言不顺的命名规范。好的舞鞋是让舞者感觉不到其存在的舞鞋，坏的舞鞋是让舞者带着镣铐起舞。一个坏的命名规范具有的破坏力比一个好的命名规范具有的创造力要大得多。<br><br>本文要证明的是：匈牙利命名法是一个坏的命名规范。本文的作用范围为静态强类型编程语言。本文的分析范本为C语言和C++语言。下文中的匈法为匈牙利命名法的简称。<br><br>一 匈牙利命名法的成本<br><br>匈法的表现形式为给变量名附加上类型名前缀，例如：nFoo,szFoo,pFoo,cpFoo分别表示整型变量，字符串型变量，指针型变量和常指针型变量。可以看出，匈法将变量的类型信息从单一地点（声明变量处）复制到了多个地点（使用变量处），这是冗余法。冗余法的成本之一是要维护副本的一致性。这个成本在编写和维护代码的过程中需要改变变量的类型时付出。冗余法的成本之二是占用了额外的空间。一个优秀的书写者会自觉地遵从一个法则：代码最小组织单位的长度以30个自然行以下为宜，如果超过50行就应该重新组织。一个变量的书写空间会给这一法则添加不必要的难度。<br><br>二 匈牙利命名法的收益<br><br>这里要证明匈牙利命名法的收益是含糊的，无法预期的。<br><br>范本1：strcpy(pstrFoo,pcstrFoo2) Vs strcpy(foo,foo2)<br>匈法在这里有什么收益呢？我看不到。没有一个程序员会承认自己不知道strcpy函数的参数类型吧。<br><br>范本2：unknown_function(nFoo) Vs unknown_function(foo)<br>匈法在这里有什么收益呢？我看不到。对于一个不知道确定类型的函数，程序员应该去查看该函数的文档，这是一种成本。使用匈法的唯一好处是看代码的人知道这个函数要求一个整型参数，这又有什么用处呢？函数是一种接口，参数的类型仅仅是接口中的一小部分。诸如函数的功能、出口信息、线程安全性、异常安全性、参数合法性等重要信息还是必须查阅文档。<br><br>范本3：nFoo=nBar Vs foo=bar<br>匈法在这里有什么收益呢？我看不到。使用匈法的唯一好处是看代码的人知道这里发生了一个整型变量的复制动作，听起来没什么问题，可以安心睡大觉了。如果他看到的是nFoo=szBar，可能会从美梦中惊醒。且慢，事情真的会是这样吗？我想首先被惊醒的应该是编译器。另一方面，nFoo=nBar只是在语法上合法而已，看代码的人真正关心的是语义的合法性，匈法对此毫无帮助。另一方面，一个优秀的书写者会自觉地遵从一个法则：代码最小组织单位中的临时变量以一两个为宜，如果超过三个就应该重新组织。结合前述第一个法则，可以得出这样的结论：易于理解的代码本身就应该是易于理解的，这是代码的内建高质量。好的命名规范对内建高质量的助益相当有限，而坏的命名规范对内建高质量的损害比人们想象的要大。<br><br>三 匈牙利命名法的实施<br><br>这里要证明匈牙利命名法在C语言是难以实施的，在C++语言中是无法实施的。从逻辑上讲，对匈法的收益做出否定的结论以后，再来论证匈法的可行性，是画蛇添足。不过有鉴于小马哥曾让已射杀之敌死灰复燃，我还是再踏上一支脚为妙。<br><br>前面讲过，匈法是类型系统的冗余，所以实施匈法的关键是我们是否能够精确地对类型系统进行复制。这取决于类型系统的复杂性。<br><br>先来看看C语言：<br><br>1.内置类型：int,char,float,double 复制为 n,ch,f,d？好像没有什么问题。不过谁来告诉我void应该怎么表示？<br>2.组合类型：array,union,enum,struct 复制为 a,u,e,s？好象比较别扭。<br>这里的难点不是为主类型取名，而是为副类型取名。an表示整型数组？sfoo,sbar表示结构foo，结构bar？ausfoo表示联合结构foo数组？累不累啊。<br>3.特殊类型：pointer。pointer在理论上应该是组合类型，但是在C语言中可以认为是内置类型，因为C语言并没有非常严格地区分不同的指针类型。下面开始表演：pausfoo表示联合结构foo数组指针？ppp表示指针的指针的指针？<br><br>噩梦还没有结束，再来看看类型系统更阿为丰富的C++语言：<br><br>1.class：如果说C语言中的struct还可以用stru搪塞过去的话，不要梦想用cls来搪塞C++中的class。严格地讲，class根本就并不是一个类型，而是创造类型的工具，在C++中，语言内置类型的数量和class创造的用户自定义类型的数量相比完全可以忽略不计。stdvectorFoo表示标准库向量类型变量Foo？疯狂的念头。<br>2.命名空间：boostfilesystemiteratorFoo，表示boost空间filesystem子空间遍历目录类型变量Foo？程序员要崩溃了。<br>3.模板：你记得std::map&lt;std::string,std::string&gt;类型的确切名字吗？我是记不得了，好像超过255个字符，还是饶了我吧。<br>4.模板参数：template &lt;class T, class BinaryPredicate&gt;const T&amp; max(const T&amp; a, const T&amp; b, BinaryPredicate comp) 聪明的你，请用匈法为T命名。上帝在发笑。<br>5.类型修饰：static,extern,mutable,register,volatile,const,short,long,unsigned 噩梦加上修饰是什么？还是噩梦。 
<img src ="http://www.cppblog.com/justin-shi/aggbug/48615.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/justin-shi/" target="_blank">幽幽</a> 2008-05-02 02:26 <a href="http://www.cppblog.com/justin-shi/archive/2008/05/02/48615.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>这个空间先留着，等大二能上网了再写！</title><link>http://www.cppblog.com/justin-shi/archive/2008/02/05/42544.html</link><dc:creator>幽幽</dc:creator><author>幽幽</author><pubDate>Tue, 05 Feb 2008 12:26:00 GMT</pubDate><guid>http://www.cppblog.com/justin-shi/archive/2008/02/05/42544.html</guid><wfw:comment>http://www.cppblog.com/justin-shi/comments/42544.html</wfw:comment><comments>http://www.cppblog.com/justin-shi/archive/2008/02/05/42544.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/justin-shi/comments/commentRss/42544.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/justin-shi/services/trackbacks/42544.html</trackback:ping><description><![CDATA[这个空间先留着，等大二能上网了再写！<br>这个空间先留着，等大二能上网了再写！<br><img src="http://www.cppblog.com/images/cppblog_com/justin-shi/1_135440G2.gif" border=0> 
<img src ="http://www.cppblog.com/justin-shi/aggbug/42544.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/justin-shi/" target="_blank">幽幽</a> 2008-02-05 20:26 <a href="http://www.cppblog.com/justin-shi/archive/2008/02/05/42544.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>