﻿<?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++博客-RTY 实践出真知-随笔分类-需要注意的</title><link>http://www.cppblog.com/lauer3912/category/17376.html</link><description>没有理由不学习</description><language>zh-cn</language><lastBuildDate>Sat, 19 Jul 2014 05:07:17 GMT</lastBuildDate><pubDate>Sat, 19 Jul 2014 05:07:17 GMT</pubDate><ttl>60</ttl><item><title>Dynamic Library Design Guidelines (Xcode)</title><link>http://www.cppblog.com/lauer3912/archive/2012/09/13/190472.html</link><dc:creator>RTY</dc:creator><author>RTY</author><pubDate>Thu, 13 Sep 2012 00:58:00 GMT</pubDate><guid>http://www.cppblog.com/lauer3912/archive/2012/09/13/190472.html</guid><wfw:comment>http://www.cppblog.com/lauer3912/comments/190472.html</wfw:comment><comments>http://www.cppblog.com/lauer3912/archive/2012/09/13/190472.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lauer3912/comments/commentRss/190472.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lauer3912/services/trackbacks/190472.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Dynamic Library Design GuidelinesDynamic libraries, in addition to grouping common functionality, help reduce an app&#8217;s launch time. However, when designed improperly, dynamic libraries can deg...&nbsp;&nbsp;<a href='http://www.cppblog.com/lauer3912/archive/2012/09/13/190472.html'>阅读全文</a><img src ="http://www.cppblog.com/lauer3912/aggbug/190472.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lauer3912/" target="_blank">RTY</a> 2012-09-13 08:58 <a href="http://www.cppblog.com/lauer3912/archive/2012/09/13/190472.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>常见问题：如何在有线无线网络共存的情况下合理配置路由 常见问题：如何在有线无线网络共存的情况下合理配置路由 常见问题：如何在有线无线网络共存的情况下合理配置路由</title><link>http://www.cppblog.com/lauer3912/archive/2012/03/06/167240.html</link><dc:creator>RTY</dc:creator><author>RTY</author><pubDate>Tue, 06 Mar 2012 05:25:00 GMT</pubDate><guid>http://www.cppblog.com/lauer3912/archive/2012/03/06/167240.html</guid><wfw:comment>http://www.cppblog.com/lauer3912/comments/167240.html</wfw:comment><comments>http://www.cppblog.com/lauer3912/archive/2012/03/06/167240.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lauer3912/comments/commentRss/167240.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lauer3912/services/trackbacks/167240.html</trackback:ping><description><![CDATA[<div>http://support1.lenovo.com.cn/lenovo/wsi/htmls/detail_12608716075461918.html<br /><div>文章编号:24625</div> <div>2006-11-28 9:46:54</div><br /> <div> <table border="0" cellspacing="0" cellpadding="3" width="100%"> <tbody> <tr> <td bgcolor="#ffffff">在很多情况下会出现有线与无线双网络共存的情况，此时就需要合理配置路由，否则网络访问时就会出现混乱，无法正常上网。<br />举例而言，公司存在tplink无线网络，为192.168.1.*网段，网关为192.168.1.1；有线网络为10.99.31.*网段，网关为10.99.31.2；要实现访问外网时使用无线，内网使用有线，则配置方法为：<br />1：运行CMD<br />2：route  delete 0.0.0.0 mask 0.0.0.0 10.99.31.2<br />&nbsp;&nbsp;  删除所有网络连接都从网关10.99.31.2走这条路径；<br />3：route add 10.0.0.0 mask 255.0.0.0 10.99.31.2  metric 1<br />&nbsp;&nbsp; 增加内网连接走内网网关路径；如果在该命令后加  －p参数将重新启动后命令有效，无需每次写这条命令了，只需要运行2就可以了。<br />可以将以上命令整合为一个bat文件，保存到所有程序项里面。在双网络都连接成功后，运行该bat文件就可以了。<br />关于Metric：<br />metric是路由算法用以确定到达目的地的最佳路径的计量标准,常用的metric值有:路径长度,可靠性,延迟,带宽,负载,通信代价等.<br />metric值的作用很大,我们常用它来计算路由的优先级,如两条到达相同网络的静态路由,metric小的优先级高;<br />因此，我们可以通过合理设置metric值来达到三个及三个以上多网络存在时的路由配置。<br />路由表示意图：<br /><img src="http://webdoc.lenovo.com.cn/lenovowsi/uploadimages/2006-11-27/5cP136bqpnrU1K2h.jpg"  alt="" /><br />解释：<br />默认网关为无线环境tplink的192.168.1.1，所有的网络数据请求都将通过这个网关出去，也就是说所有的网络请求都从无线出去。<br />persistent  routes列表代表所有10网段的都从有线的网关10.99.31.2出去，也就是说所有属于10网段的请求都从有线出去。通过这个路由来控制内网连接，也就是无线路由配置中所有网络请求走无线环境的例外！<br />另外，如果网络环境中配置有自动配置脚本，那么，配置脚本的优先级最高。网络的数据请求将按照配置脚本的配置实现。在此例中，如果有自动配置脚本proxy.pac，那么优先级别为proxy.pac&gt;192.168.1.1&gt;10.99.31.2；<br />&nbsp; &nbsp;</td></tr></tbody></table></div></div><img src ="http://www.cppblog.com/lauer3912/aggbug/167240.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lauer3912/" target="_blank">RTY</a> 2012-03-06 13:25 <a href="http://www.cppblog.com/lauer3912/archive/2012/03/06/167240.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux 终端中常用的快捷键</title><link>http://www.cppblog.com/lauer3912/archive/2012/03/01/166872.html</link><dc:creator>RTY</dc:creator><author>RTY</author><pubDate>Thu, 01 Mar 2012 07:02:00 GMT</pubDate><guid>http://www.cppblog.com/lauer3912/archive/2012/03/01/166872.html</guid><wfw:comment>http://www.cppblog.com/lauer3912/comments/166872.html</wfw:comment><comments>http://www.cppblog.com/lauer3912/archive/2012/03/01/166872.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lauer3912/comments/commentRss/166872.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lauer3912/services/trackbacks/166872.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 1. 移动光标快捷键ctrl+f 向前移动一个字符ctrl+b 向后移动一个字符alt+f 向前移动一个单词alt+b 向后移动一个单词ctrl+a 移动到当前行首ctrl+e 移动到当前行尾ctrl+l 清屏，并在屏幕最上面开始一个新行 2. 编辑命令行快捷键ctrl+d 删除当前的字符ctrl+t 交换当前字符和前一个字符的位置alt+t 交换当前单词和前一个单词的位置alt+u 把当前单词变...&nbsp;&nbsp;<a href='http://www.cppblog.com/lauer3912/archive/2012/03/01/166872.html'>阅读全文</a><img src ="http://www.cppblog.com/lauer3912/aggbug/166872.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lauer3912/" target="_blank">RTY</a> 2012-03-01 15:02 <a href="http://www.cppblog.com/lauer3912/archive/2012/03/01/166872.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>codesign  CSSMERR_TP_NOT_TRUSTED</title><link>http://www.cppblog.com/lauer3912/archive/2012/03/01/166868.html</link><dc:creator>RTY</dc:creator><author>RTY</author><pubDate>Thu, 01 Mar 2012 06:48:00 GMT</pubDate><guid>http://www.cppblog.com/lauer3912/archive/2012/03/01/166868.html</guid><wfw:comment>http://www.cppblog.com/lauer3912/comments/166868.html</wfw:comment><comments>http://www.cppblog.com/lauer3912/archive/2012/03/01/166868.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lauer3912/comments/commentRss/166868.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lauer3912/services/trackbacks/166868.html</trackback:ping><description><![CDATA[<div>codesign -s "Developer ID Application: Mide Hu" "/Users/QtDev/11/build_trial/list.app"</div><div></div><div></div><div></div><div>Xcode iOS真机调试出现</div><div>Command /usr/bin/codesign failed with exit code 1</div><div></div><div>查看详细信息提示：CSSMERR_TP_NOT_TRUSTED<br /><br />出现这一问题的原因可能是：<br />（1）证书&#8220;Developer ID Application: Mide Hu" 是无效的！<br />&nbsp; (2) 缺少&#8221;Apple Woldwide Developer Relations Certification Authority&#8220;<br /><br /><br /><br />================================================================<br />以下是备注：</div><div></div><div>查了点资料，发现原来之前在keychain(钥匙窜访问)中多删了一个证书：Apple Woldwide Developer Relations Certification Authority，它是iPhone Developer证书的签发者，如果它被删除就会导致iPhone Developer证书被识别为未知颁发机构签名，然后xcode中真机调试就会出现上面的错误。解决方法当然是重新把AppleWWDRCA放回去</div><div>从Apple官网下载一个&nbsp;</div><div>http://www.apple.com/certificateauthority/</div><div>找到 Woldwide Developer Relations 选择 Download certificate</div><div>下载后拖入keychain(钥匙窜访问)－登录目录。</div><img src ="http://www.cppblog.com/lauer3912/aggbug/166868.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lauer3912/" target="_blank">RTY</a> 2012-03-01 14:48 <a href="http://www.cppblog.com/lauer3912/archive/2012/03/01/166868.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>电信速度测试</title><link>http://www.cppblog.com/lauer3912/archive/2012/02/15/165677.html</link><dc:creator>RTY</dc:creator><author>RTY</author><pubDate>Wed, 15 Feb 2012 07:45:00 GMT</pubDate><guid>http://www.cppblog.com/lauer3912/archive/2012/02/15/165677.html</guid><wfw:comment>http://www.cppblog.com/lauer3912/comments/165677.html</wfw:comment><comments>http://www.cppblog.com/lauer3912/archive/2012/02/15/165677.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lauer3912/comments/commentRss/165677.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lauer3912/services/trackbacks/165677.html</trackback:ping><description><![CDATA[<div>http://202.102.26.5/st/</div><div><div id="header"><div><div><img alt="" src="http://www.cppblog.com/images/index_08.jpg" height="54" width="283" /></div></div><div id="menu1"><ul><li><a id="curmenu" href="/st/"><span style="top: 40px;">首　　页</span><span style="top: 0px;">首　　页</span></a></li><li><img src="http://www.cppblog.com/images/ge.jpg"  alt="" /></li><li><a href="/speed/test.do?method=st&amp;t=adsl&amp;ts=1">网速测试网速测试</a></li><li><img src="http://www.cppblog.com/images/ge.jpg"  alt="" /></li><li><a href="/st/exp.jsp">应用体验应用体验</a></li><li><img src="http://www.cppblog.com/images/ge.jpg"  alt="" /></li><li><a href="/speed/test.do?method=tt">系统检测系统检测</a></li><li><img src="http://www.cppblog.com/images/ge.jpg"  alt="" /></li><li><a href="http://srv.js.ct10000.com/NewCS/complaints/preFaultAction.action" target="_blank">建议反馈建议反馈</a></li><li><img src="http://www.cppblog.com/images/ge.jpg"  alt="" /></li><li><a href="/st/faq0.jsp">宽带常识宽带常识</a></li></ul></div></div><div id="main"><div>尊敬的用户，您当前的IP地址为：49.73.128.86 　　来自：苏州</div><div><div>　　测速入口　　			<span style="color: #999999; line-height: 0px; font-size: 12px; vertical-align: middle;">企业用户请选择公众热点测速</span></div><div>　　用户须知　　			<span style="color: #999999; line-height: 0px; font-size: 12px; vertical-align: middle;">测速前请<a href="#">查看用户须知</a></span></div> <br /></div></div><div><a href="/speed/test.do?method=st&amp;t=adsl&amp;ts=1"><img alt="" src="http://www.cppblog.com/images/index_19.jpg" border="0" height="180" width="413" /></a><img alt="" src="http://www.cppblog.com/images/index_21.jpg" height="184" width="1" /><a href="/speed/test.do?method=st&amp;t=other&amp;ts=1"><img alt="" src="http://www.cppblog.com/images/index_23.jpg" border="0" height="179" width="414" /></a></div><div><div><div><div><a href="/speed/test.do?method=tt&amp;cmd=local">　　系统检测</a></div><div><ul><li>&#183;CPU型号:   <span id="cpu">Pentium(R) Dual-Core CPU       T4400  @ 2.20GHz</span></li><li>&#183;CPU频率:   <span id="cpufn">1623MHz</span></li><li>&#183;内存:   <span id="mem">2048M</span></li><li>&#183;内存利用率:   <span id="mem_ul">44%</span></li><li>&#183;操作系统:   <span id="os">Windows 7 (Service Pack 1)</span></li><li>&#183;浏览器:   <span id="browser">Microsoft Internet Explorer 9.0</span></li></ul></div></div><div><div><a href="/speed/test.do?method=tt&amp;cmd=trace">　　网络检测</a></div><div><ul><li>&#183;IP:   49.73.128.86</li><li>&#183;DNS:   <span id="dns">61.177.7.1,221.228.255.1</span></li><li id="dnstip">DNS配置正确</li></ul></div></div><div><div><a href="/st/faq0.jsp">　　宽带常识</a></div><div><ul><li>&#183;<a href="/st/faq.jsp?inx=7">高带宽用户测速优化</a></li><li>&#183;<a href="/st/faq.jsp?inx=4">网站通过什么手段来测试宽带接入性能</a></li><li>&#183;<a href="/st/faq.jsp?inx=1">如何安装测速插件</a></li><li>&#183;<a href="/st/faq.jsp?inx=2">宽带速率测试前应注意事项</a></li><li>&#183;<a href="/st/faq.jsp?inx=3">影响网络传输速率的因素</a></li><li>&#183;<a href="/st/faq.jsp?inx=5">测速结果的含义</a></li></ul></div></div></div></div></div><div id="footer"><div>COPYRIGHT &#169; AKAZAM Communications, Inc. (Build 3.5.530)<br />	[增值电信业务经营许可证A2.B1.B2-20090001] ICP证号：京ICP备09031924号</div></div><img src ="http://www.cppblog.com/lauer3912/aggbug/165677.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lauer3912/" target="_blank">RTY</a> 2012-02-15 15:45 <a href="http://www.cppblog.com/lauer3912/archive/2012/02/15/165677.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>WikiSyntax   (来源Google)</title><link>http://www.cppblog.com/lauer3912/archive/2011/11/09/159848.html</link><dc:creator>RTY</dc:creator><author>RTY</author><pubDate>Tue, 08 Nov 2011 22:52:00 GMT</pubDate><guid>http://www.cppblog.com/lauer3912/archive/2011/11/09/159848.html</guid><wfw:comment>http://www.cppblog.com/lauer3912/comments/159848.html</wfw:comment><comments>http://www.cppblog.com/lauer3912/archive/2011/11/09/159848.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lauer3912/comments/commentRss/159848.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lauer3912/services/trackbacks/159848.html</trackback:ping><description><![CDATA[<div><table><tbody><tr><td style="vertical-align:top; padding-left:5px"><div id="wikiheader"><div>     <em>The reference to the wiki syntax for Google Code projects</em>      <br />    <a style="padding-top: 2px" href="http://code.google.com/p/support/w/list?q=label:Restrict-AddWikiComment-Commit" title="Restrict-AddWikiComment-Commit">Restrict-AddWikiComment-Commit</a>        <div id="wikiauthor" style="float:right">  Updated <span title="Wed Aug 17 15:10:16 2011">  Aug 17, 2011</span>    by <a style="white-space: nowrap" href="http://code.google.com/u/dbentley@google.com/">dbentley@google.com</a>    </div>  </div>  </div>    <div id="wikicontent">  <div id="wikimaincol">  <h1><a name="Wiki_Syntax"></a>Wiki Syntax</h1><ul><li><a href="http://code.google.com/p/support/wiki/WikiSyntax#Wiki_Syntax">Wiki Syntax</a></li><li><a href="http://code.google.com/p/support/wiki/WikiSyntax#Introduction">Introduction</a></li><li><a href="http://code.google.com/p/support/wiki/WikiSyntax#Pragmas">Pragmas</a></li><li><a href="http://code.google.com/p/support/wiki/WikiSyntax#Wiki-style_markup">Wiki-style markup</a></li><ul><li><a href="http://code.google.com/p/support/wiki/WikiSyntax#Paragraphs">Paragraphs</a></li><li><a href="http://code.google.com/p/support/wiki/WikiSyntax#Typeface">Typeface</a></li><li><a href="http://code.google.com/p/support/wiki/WikiSyntax#Code">Code</a></li><li><a href="http://code.google.com/p/support/wiki/WikiSyntax#Headings">Headings</a></li><li><a href="http://code.google.com/p/support/wiki/WikiSyntax#Dividers">Dividers</a></li><li><a href="http://code.google.com/p/support/wiki/WikiSyntax#Lists">Lists</a></li><li><a href="http://code.google.com/p/support/wiki/WikiSyntax#Quoting">Quoting</a></li><li><a href="http://code.google.com/p/support/wiki/WikiSyntax#Links">Links</a></li><li><a href="http://code.google.com/p/support/wiki/WikiSyntax#Tables">Tables</a></li></ul><li><a href="http://code.google.com/p/support/wiki/WikiSyntax#HTML_support">HTML support</a></li><ul><li><a href="http://code.google.com/p/support/wiki/WikiSyntax#Escaping_HTML_Tags">Escaping HTML Tags</a></li></ul><li><a href="http://code.google.com/p/support/wiki/WikiSyntax#Comments">Comments</a></li><li><a href="http://code.google.com/p/support/wiki/WikiSyntax#+1_Button">+1 Button</a></li><li><a href="http://code.google.com/p/support/wiki/WikiSyntax#Gadgets">Gadgets</a></li><li><a href="http://code.google.com/p/support/wiki/WikiSyntax#Videos">Videos</a></li><li><a href="http://code.google.com/p/support/wiki/WikiSyntax#Navigation">Navigation</a></li><ul><li><a href="http://code.google.com/p/support/wiki/WikiSyntax#Table_of_Contents">Table of Contents</a></li><li><a href="http://code.google.com/p/support/wiki/WikiSyntax#Side_navigation">Side navigation</a></li></ul><li><a href="http://code.google.com/p/support/wiki/WikiSyntax#Localizing_Wiki_Content">Localizing Wiki Content</a></li></ul>  <h1><a name="Introduction"></a>Introduction</h1><p>Each  wiki page is stored in a .wiki file under the /wiki directory in a  project's repository.  Each file's name is the same as the wiki page  name.  And, each wiki file consists of optional pragma lines followed by  the content of the page. </p><h1><a name="Pragmas"></a>Pragmas</h1><p>Optional  pragma lines provide metadata about the page and how it should be  displayed. These lines are only processed if they appear at the top of  the file.  Each pragma line begins with a pound-sign (#) and the pragma  name, followed by a value. </p><table><tbody><tr><td style="border: 1px solid #ccc; padding: 5px;"> <strong>Pragma</strong>   </td><td style="border: 1px solid #ccc; padding: 5px;"> <strong>Value</strong>  </td></tr> <tr><td style="border: 1px solid #ccc; padding: 5px;"> #summary   </td><td style="border: 1px solid #ccc; padding: 5px;"> One-line summary of the page </td></tr> <tr><td style="border: 1px solid #ccc; padding: 5px;"> #labels    </td><td style="border: 1px solid #ccc; padding: 5px;"> Comma-separated list of labels (filled in automatically via the web UI) </td></tr> <tr><td style="border: 1px solid #ccc; padding: 5px;"> #sidebar   </td><td style="border: 1px solid #ccc; padding: 5px;"> See <a href="http://code.google.com/p/support/wiki/WikiSyntax#Side_navigation" rel="nofollow">Side navigation</a> </td></tr> </tbody></table><h1><a name="Wiki-style_markup"></a>Wiki-style markup</h1><h2><a name="Paragraphs"></a>Paragraphs</h2><p>Use one or more blank lines to separate paragraphs.  </p><h2><a name="Typeface"></a>Typeface</h2><table><tbody><tr><td style="border: 1px solid #ccc; padding: 5px;"> Name/Sample</td><td style="border: 1px solid #ccc; padding: 5px;"> <strong> Markup </strong>       </td></tr> <tr><td style="border: 1px solid #ccc; padding: 5px;">  <em>italic</em>       </td><td style="border: 1px solid #ccc; padding: 5px;"> <tt>_italic_</tt>       </td></tr> <tr><td style="border: 1px solid #ccc; padding: 5px;">  <strong>bold</strong>         </td><td style="border: 1px solid #ccc; padding: 5px;"> <tt>*bold*</tt>         </td></tr> <tr><td style="border: 1px solid #ccc; padding: 5px;">  <tt>code</tt>         </td><td style="border: 1px solid #ccc; padding: 5px;"> <tt>`code`</tt>     </td></tr> <tr><td style="border: 1px solid #ccc; padding: 5px;">  <tt>code</tt>     </td><td style="border: 1px solid #ccc; padding: 5px;"> <tt>{{{code}}}</tt>     </td></tr> <tr><td style="border: 1px solid #ccc; padding: 5px;">  <sup>super</sup>script  </td><td style="border: 1px solid #ccc; padding: 5px;"> <tt>^super^script</tt>  </td></tr> <tr><td style="border: 1px solid #ccc; padding: 5px;">  <sub>sub</sub>script  </td><td style="border: 1px solid #ccc; padding: 5px;"> <tt>,,sub,,script</tt>  </td></tr> <tr><td style="border: 1px solid #ccc; padding: 5px;"> <span style="text-decoration: line-through">strikeout</span>   </td><td style="border: 1px solid #ccc; padding: 5px;"> <tt>~~strikeout~~</tt>  </td></tr> </tbody></table><p>You can mix these typefaces in some ways: </p><table><tbody><tr><td style="border: 1px solid #ccc; padding: 5px;">       <strong>Markup</strong>                    </td><td style="border: 1px solid #ccc; padding: 5px;">        <strong>Result</strong>                 </td></tr> <tr><td style="border: 1px solid #ccc; padding: 5px;"> <tt>_*bold* in italics_</tt>             </td><td style="border: 1px solid #ccc; padding: 5px;"> <em><strong>bold</strong> in italics</em>             </td></tr> <tr><td style="border: 1px solid #ccc; padding: 5px;"> <tt>*_italics_ in bold*</tt>             </td><td style="border: 1px solid #ccc; padding: 5px;"> <strong><em>italics</em> in bold</strong>             </td></tr> <tr><td style="border: 1px solid #ccc; padding: 5px;"> <tt>*~~strike~~ works too*</tt>          </td><td style="border: 1px solid #ccc; padding: 5px;"> <strong><span style="text-decoration: line-through">strike</span> works too</strong>          </td></tr> <tr><td style="border: 1px solid #ccc; padding: 5px;"> <tt>~~as well as _this_ way round~~</tt> </td><td style="border: 1px solid #ccc; padding: 5px;"> <span style="text-decoration: line-through">as well as <em>this</em> way round</span> </td></tr> </tbody></table><h2><a name="Code"></a>Code</h2><p>If you have a multiline code block that you want to display verbatim, use the multiline code delimiter: </p><pre>{{{<br />def fib(n):<br />&nbsp; if n == 0 or n == 1:<br />&nbsp; &nbsp; return n<br />&nbsp; else:<br />&nbsp; &nbsp; # This recursion is not good for large numbers.<br />&nbsp; &nbsp; return fib(n-1) + fib(n-2)<br />}}}</pre><p>Which results in: </p><pre>def fib(n):<br />&nbsp; if n == 0 or n == 1:<br />&nbsp; &nbsp; return n<br />&nbsp; else:<br />&nbsp; &nbsp; # This recursion is not good for large numbers.<br />&nbsp; &nbsp; return fib(n-1) + fib(n-2)</pre><p>For more control over the syntax higlighting, the <tt>&lt;code&gt;</tt> tag allows you to specify a file extension: </p><pre>&lt;code language="xml"&gt;<br />&lt;hello target="world"/&gt;<br />&lt;/code&gt;</pre><p>To disable highlighting entirely, use the <tt>&lt;pre&gt;</tt> tag. </p><h2><a name="Headings"></a>Headings</h2><pre>= Heading =<br />== Subheading ==<br />=== Level 3 ===<br />==== Level 4 ====<br />===== Level 5 =====<br />====== Level 6 ======</pre><h2><a name="Dividers"></a>Dividers</h2><p>Four or more dashes on a line by themselves results in a horizontal rule. </p><h2><a name="Lists"></a>Lists</h2><p>Google  Code wikis support both bulleted and numbered lists. A list must be  indented at least one space to be recognized as such. You can also nest  lists one within the other by appropriate use of indenting: </p><pre>The following is:<br />&nbsp; * A list<br />&nbsp; * Of bulleted items<br />&nbsp; &nbsp; # This is a numbered sublist<br />&nbsp; &nbsp; # Which is done by indenting further<br />&nbsp; * And back to the main bulleted list<br /><br />&nbsp;* This is also a list<br />&nbsp;* With a single leading space<br />&nbsp;* Notice that it is rendered<br />&nbsp; # At the same levels<br />&nbsp; # As the above lists.<br />&nbsp;* Despite the different indentation levels.</pre><p>The following is: </p><ul><li>A list </li><li>Of bulleted items </li><ol><li>This is a numbered sublist </li><li>Which is done by indenting further </li></ol><li>And back to the main bulleted list </li></ul><ul><li>This is also a list </li><li>With a single leading space </li><li>Notice that it is rendered </li><ol><li>At the same levels </li><li>As the above lists. </li></ol><li>Despite the different indentation levels. </li></ul><h2><a name="Quoting"></a>Quoting</h2><p>Block  quotes place emphasis on a particular extract of text in your page.  Block quotes are created by indenting a paragraph by at least one space:  </p><pre>Someone once said:<br /><br />&nbsp; This sentence will be quoted in the future as the canonical example<br />&nbsp; of a quote that is so important that it should be visually separate<br />&nbsp; from the rest of the text in which it appears.</pre><p>Someone once said: </p><blockquote>This sentence will be quoted in the future as the canonical example  of a quote that is so important that it should be visually separate  from the rest of the text in which it appears.  </blockquote><h2><a name="Links"></a>Links</h2><p>Links are central to  the wiki principle, as they create the web of content. Google Code wiki  permits both internal (within the wiki) and external links, and in some  cases automatically creates a link when it recognizes either a WikiWord  or an URL. </p><h3><a name="Internal_wiki_links"></a>Internal wiki links</h3><p>Internal  links within a wiki are created using the syntax below. If you add a  wiki link to a page that does not exist, the link will appear with a  LittleLink<a href="http://code.google.com/p/support/wiki/WikiSyntax">?</a>  to project committers and owners. Clicking that link will take you to  the page creation form where you can enter content for that page. </p><p>If  you are not logged in, wiki links that point to non-existent pages will  appear as plain text, without the link to the page creation form. When  you create the target page, the link will become a normal hyperlink for  all viewers of the page. </p><pre>WikiSyntax is identified and linked automatically<br /><br />Wikipage is not identified, so if you have a page named [Wikipage] you<br />need to link it explicitly.<br /><br />If the WikiSyntax page is actually about reindeers, you can provide a<br />description, so that people know you are actually linking to a page on<br />[WikiSyntax reindeer flotillas].<br /><br />If you want to mention !WikiSyntax without it being autolinked, use an<br />exclamation mark to prevent linking.</pre><p><a href="http://code.google.com/p/support/wiki/WikiSyntax">WikiSyntax</a> is identified and linked automatically </p><p>Wikipage is not identified, so if you have a page named Wikipage you need to link it explicitly. </p><p>If the <a href="http://code.google.com/p/support/wiki/WikiSyntax">WikiSyntax</a> page is actually about reindeers, you can provide a description, so that people know you are actually linking to a page on <a href="http://code.google.com/p/support/wiki/WikiSyntax">reindeer flotillas</a>. </p><p>If you want to mention WikiSyntax without it being autolinked, use an exclamation mark to prevent linking. </p><h3><a name="Links_to_anchors_within_a_page"></a>Links to anchors within a page</h3><p>Each  heading defines a HTML anchor with the same name as the heading, but  with spaces replaced by underscores. You can create a link to a specific  heading on a page like this: </p><pre>[WikiSyntax#Wiki-style_markup]</pre><p>And it will render as: <a href="http://code.google.com/p/support/wiki/WikiSyntax#Wiki-style_markup">WikiSyntax#Wiki-style_markup</a>. </p><h3><a name="Links_to_issues_and_revisions"></a>Links to issues and revisions</h3><p>You can easily link to issues and revisions using the following syntax. </p><ul><li><tt>issue 123</tt> will be autolinked to issue number 123 in the current project.  You can include a <tt>#</tt>  or not.  If the issue has been closed, the link will appear as a  cross-out rather than an underline.  Hovering your mouse over such a  link shows the issue summary. </li></ul><ul><li><tt>issue PROJECTNAME:122</tt>  will be autolinked to that issue number in the specified project.  This  is useful when your project depends on work being done in related  projects. </li></ul><ul><li><tt>r123</tt> will be autolinked to the revision detail page for that revision in the current project. </li></ul><p>There is currently no way to disable this type of autolinking. See <a title="Disable autolinking to revisions when ! is used" href="http://code.google.com/p/support/issues/detail?id=996">issue 996</a>. </p><pre>For example: Please add a comment on issue 123 rather than adding more review comments to r456. </pre><p>Renders as: Please add a comment on <a title="images and internal wiki links" href="http://code.google.com/p/support/issues/detail?id=123">&nbsp;issue 123&nbsp;</a> rather than adding more review comments to <a href="http://code.google.com/p/support/source/detail?r=456">r456</a>. </p><h3><a name="Links_to_external_pages"></a>Links to external pages</h3><p>You can of course link to external pages from your own wiki pages, using a syntax similar to that for internal links: </p><pre>Plain URLs such as http://www.google.com/ or ftp://ftp.kernel.org/ are<br />automatically made into links.<br /><br />You can also provide some descriptive text. For example, the following<br />link points to the [http://www.google.com Google home page].<br /><br />If your link points to an image, it will get inserted as an image tag<br />into the page:<br /><br />http://code.google.com/images/code_sm.png<br /><br />You can also make the image into a link, by setting the image URL as<br />the description of the URL you want to link:<br /><br />[http://code.google.com/ http://code.google.com/images/code_sm.png]</pre><p>Plain URLs such as <a href="http://www.google.com/" rel="nofollow">http://www.google.com/</a> or <a href="ftp://ftp.kernel.org/" rel="nofollow">ftp://ftp.kernel.org/</a> are automatically made into links. </p><p>You can also provide some descriptive text. For example, the following link points to the <a href="http://www.google.com/" rel="nofollow">Google home page</a>. </p><p>You can also make the image into a link, by setting the image URL as the description of the URL you want to link: </p><pre>[http://code.google.com/ http://code.google.com/images/code_sm.png]</pre><p><a href="http://code.google.com/" rel="nofollow"><img src="http://code.google.com/images/code_sm.png"  alt="" /></a> </p><h3><a name="Links_to_images"></a>Links to images</h3><p>If your link points to an image (that is, if it ends in <tt>.png</tt>, <tt>.gif</tt>, <tt>.jpg</tt> or <tt>.jpeg</tt>), it will get inserted as an image into the page: </p><pre>http://code.google.com/images/code_sm.png</pre><p><img src="http://code.google.com/images/code_sm.png"  alt="" /> </p><p>If  the image is produced by a server-side script, you may need to add a  nonsense query string parameter to the end so that the URL ends with a  supported image filename extension. </p><pre>http://chart.apis.google.com/chart?chs=200x125&amp;chd=t:48.14,33.79,19.77|83.18,18.73,12.04&amp;cht=bvg&amp;nonsense=something_that_ends_with.png</pre><p><img src="http://chart.apis.google.com/chart?chs=200x125&amp;chd=t:48.14,33.79,19.77%7C83.18,18.73,12.04&amp;cht=bvg&amp;nonsense=something_that_ends_with.png"  alt="" /> </p><h2><a name="Tables"></a>Tables</h2><p>Tables are created by entering the content of each cell separated by <tt>||</tt> delimiters. You can insert other inline wiki syntax in table cells, including typeface formatting and links. </p><pre>|| *Year* || *Temperature (low)* || *Temperature (high)* ||<br />|| 1900 || -10 || 25 ||<br />|| 1910 || -15 || 30 ||<br />|| 1920 || -10 || 32 ||<br />|| 1930 || _N/A_ || _N/A_ ||<br />|| 1940 || -2 || 40 ||</pre><table><tbody><tr><td style="border: 1px solid #ccc; padding: 5px;"> <strong>Year</strong> </td><td style="border: 1px solid #ccc; padding: 5px;"> <strong>Temperature (low)</strong> </td><td style="border: 1px solid #ccc; padding: 5px;"> <strong>Temperature (high)</strong> </td></tr> <tr><td style="border: 1px solid #ccc; padding: 5px;"> 1900 </td><td style="border: 1px solid #ccc; padding: 5px;"> -10 </td><td style="border: 1px solid #ccc; padding: 5px;"> 25 </td></tr> <tr><td style="border: 1px solid #ccc; padding: 5px;"> 1910 </td><td style="border: 1px solid #ccc; padding: 5px;"> -15 </td><td style="border: 1px solid #ccc; padding: 5px;"> 30 </td></tr> <tr><td style="border: 1px solid #ccc; padding: 5px;"> 1920 </td><td style="border: 1px solid #ccc; padding: 5px;"> -10 </td><td style="border: 1px solid #ccc; padding: 5px;"> 32 </td></tr> <tr><td style="border: 1px solid #ccc; padding: 5px;"> 1930 </td><td style="border: 1px solid #ccc; padding: 5px;"> <em>N/A</em> </td><td style="border: 1px solid #ccc; padding: 5px;"> <em>N/A</em> </td></tr> <tr><td style="border: 1px solid #ccc; padding: 5px;"> 1940 </td><td style="border: 1px solid #ccc; padding: 5px;"> -2 </td><td style="border: 1px solid #ccc; padding: 5px;"> 40 </td></tr> </tbody></table><h1><a name="HTML_support"></a>HTML support</h1><p>To  aid in the presentation of a wiki page there is some support for HTML.  HTML tags are only supported in wiki pages, not in page comments. </p><p>HTML syntax can be used in conjunction with wiki syntax, but it is recommended against doing so where possible. </p><p>The following HTML tags and attributes are currently supported: </p><table border="1"> <thead><tr><th>HTML Tag</th><th>Supported Attributes</th></tr></thead> <tbody> <tr><td>a</td><td>title dir lang href</td></tr> <tr><td>b</td><td>title dir lang</td></tr> <tr><td>br</td><td>title dir lang</td></tr> <tr><td>blockquote</td><td>title dir lang</td></tr> <tr><td>code</td><td>title dir lang language <tt>[1]</tt></td></tr>       <tr><td>dd</td><td>title dir lang</td></tr> <tr><td>div</td><td>title dir lang</td></tr> <tr><td>dl</td><td>title dir lang</td></tr>       <tr><td>dt</td><td>title dir lang</td></tr>       <tr><td>em</td><td>title dir lang</td></tr>       <tr><td>font</td><td>title dir lang face size color</td></tr>       <tr><td>h1</td><td>title dir lang</td></tr>       <tr><td>h2</td><td>title dir lang</td></tr>       <tr><td>h3</td><td>title dir lang</td></tr>       <tr><td>h4</td><td>title dir lang</td></tr>       <tr><td>h5</td><td>title dir lang</td></tr>       <tr><td>i</td><td>title dir lang</td></tr>       <tr><td>img</td><td>title dir lang src alt border height width align</td></tr>   <tr><td>li</td><td>title dir lang</td></tr>       <tr><td>ol</td><td>title dir lang type start</td></tr>     <tr><td>p</td><td>title dir lang align</td></tr>      <tr><td>pre</td><td>title dir lang</td></tr>       <tr><td>q</td><td>title dir lang</td></tr>       <tr><td>s</td><td>title dir lang</td></tr>       <tr><td>span</td><td>title dir lang</td></tr>      <tr><td>strike</td><td>title dir lang</td></tr>      <tr><td>strong</td><td>title dir lang</td></tr>       <tr><td>sub</td><td>title dir lang</td></tr>       <tr><td>sup</td><td>title dir lang</td></tr>   <tr><td>table</td><td>title dir lang align valign cellspacing cellpadding border width height</td></tr>   <tr><td>tbody</td><td>title dir lang align valign cellspacing cellpadding border width height</td></tr>   <tr><td>td</td><td>title dir lang align valign cellspacing cellpadding border width height</td></tr>   <tr><td>tfoot</td><td>title dir lang align valign cellspacing cellpadding border width height</td></tr>   <tr><td>th</td><td>title dir lang align valign cellspacing cellpadding border width height</td></tr>   <tr><td>thead</td><td>title dir lang align valign cellspacing cellpadding border width height colspan rowspan</td></tr> <tr><td>tr</td><td>title dir lang align valign cellspacing cellpadding border width height colspan rowspan</td></tr> <tr><td>tt</td><td>title dir lang</td></tr>       <tr><td>u</td><td>title dir lang</td></tr>       <tr><td>ul</td><td>title dir lang type</td></tr>      <tr><td>var</td><td>title dir lang</td></tr>      </tbody> </table> <p><tt>[1]</tt>  The language attribute of the code tag is the file extension of the  language used in the code block. It is used as a hint for the syntax  highlighter. </p><h2><a name="Escaping_HTML_Tags"></a>Escaping HTML Tags</h2><p>When you want to display html tags directly on your wiki page (as opposed to rendered), you will need to escape each HTML tag.  </p><p>HTML tags can be escaped as shown in the table below: </p><table border="1"> <thead><tr><th>Markup</th><th>Result</th></tr></thead> <tbody> <tr><td> <tt>`&lt;hr&gt;`</tt></td><td><tt>&lt;hr&gt;</tt></td></tr> <tr><td> <tt>{{{&lt;hr&gt;}}}</tt></td><td><tt>&lt;hr&gt;</tt></td></tr> </tbody> </table> <p><br /><br /> </p><h1><a name="Comments"></a>Comments</h1><p>The  wiki supports embedded comments to help explain the contents of a wiki  page. Anything inside the comment block is removed from the rendered  page, but is visible when editing or viewing the source for that page. </p><pre>&lt;wiki:comment&gt;<br />This text will be removed from the rendered page.<br />&lt;/wiki:comment&gt;</pre><h1><a name="+1_Button"></a>+1 Button</h1><p>Use <tt>&lt;g:plusone&gt;&lt;/g:plusone&gt;</tt> to add a <a href="http://www.google.com/+1/button/" rel="nofollow">+1 button</a> to the page. Example: </p><pre>&lt;g:plusone size="medium"&gt;&lt;/g:plusone&gt;</pre> <p>The count, size, and href parameters are supported; see <a href="http://code.google.com/apis/+1button/" rel="nofollow">http://code.google.com/apis/+1button/</a> for documentation. </p><h1><a name="Gadgets"></a>Gadgets</h1><p>You can embed <a href="http://www.google.com/webmasters/gadgets/" rel="nofollow">Gadgets</a> on your wiki pages with the following syntax: </p><pre>&lt;wiki:gadget url="http://example.com/gadget.xml" height="200" border="0" /&gt; </pre><p>Valid attributes are: </p><ul><li><tt>url</tt>: the URL of the gadget </li><li><tt>width</tt>: the width of the gadget </li><li><tt>height</tt>: the height of the gadget </li><li><tt>title</tt>: a title to display above the gadget </li><li><tt>border</tt>: "0" or "1", whether to draw a border around the gadget </li><li><tt>up_*</tt>: Gadget user preference parameters </li><li><tt>caja</tt>: "0" or "1", whether to use Caja to render the gadget.  <a href="http://code.google.com/p/google-caja" rel="nofollow">Caja</a> helps protect users from malicious or accidental errors in third party gadgets. </li></ul><p><a href="http://code.google.com/p/support/wiki/WorkingWithGoogleGadgets">WorkingWithGoogleGadgets</a>  describes how to create gadgets for Google Code. It also provides  a  few helpful suggestions that can make it easier to publish gadgets and  to integrate with other Google products such as iGoogle. </p><p><a href="http://code.google.com/p/support/wiki/InterestingDeveloperGadgets">InterestingDeveloperGadgets</a> shows some sample gadgets you may want to include on your project pages. </p><h1><a name="Videos"></a>Videos</h1><p>You can embed videos with the following syntax: </p><pre>&lt;wiki:video url="http://www.youtube.com/watch?v=3LkNlTNHZzE"/&gt;</pre><p>Valid attributes are: </p><ul><li><tt>url</tt>: the URL of the video </li><li><tt>width</tt>: the width of the embedded video </li><li><tt>height</tt>: the height of the embedded video </li></ul><p>Right now we support videos from Youtube and Google Video, but other containers can be added to our <a href="http://code.google.com/p/google-code-project-hosting-gadgets/source/browse/#svn/trunk/video" rel="nofollow">open source gadgets project</a>. </p><h1><a name="Navigation"></a>Navigation</h1><h2><a name="Table_of_Contents"></a>Table of Contents</h2><p>An  inline table of contents can be generated from page headers on a wiki  page. Add the following syntax to a page in the location the table of  contents should be displayed: </p><pre>&lt;wiki:toc max_depth="1" /&gt;</pre><p>Valid attributes are: </p><ul><li><tt>max_depth</tt>: the maximum header depth to display in the table of contents </li></ul><h2><a name="Side_navigation"></a>Side navigation</h2><p>You can specify the sidebar for a wiki page by selecting another wiki page that defines your side navigation. The <a href="http://code.google.com/p/doctype/wiki/Articles" rel="nofollow">doctype project</a> uses the sidebar extensively across its wiki. </p><p>One  way of adding a sidebar is by setting the #sidebar pragma, as shown  below. Alternatively, the sidebar pragma can be left blank if no side  navigation is desired.  </p><table><tbody><tr><td style="border: 1px solid #ccc; padding: 5px;"> #sidebar TableOfContents </td></tr> </tbody></table><p>The side navigation that is defined should be in the format of a simple list, as shown below.  </p><pre>&nbsp;* [Articles HOWTO articles]<br />&nbsp; &nbsp; * [ArticlesXSS Web security]<br />&nbsp; &nbsp; * [ArticlesDom DOM manipulation]<br />&nbsp; &nbsp; * [ArticlesStyle CSS and style]<br />&nbsp; &nbsp; * [ArticlesTips Tips and tricks]<br />&nbsp; * [DOMReference DOM reference]<br />&nbsp; * [HTMLElements HTML reference]<br />&nbsp; * [CSSReference CSS reference]</pre><p>A  default sidebar page can also be specified for all wiki pages by  project owners through the Wiki settings in the Administer tab. If a  #sidebar pragma is also specified, it will take precedence on the page. </p><h1><a name="Localizing_Wiki_Content"></a>Localizing Wiki Content</h1><p>Along  with the default language for the wiki, which can be set through the  Wiki settings in the Administer tab, additional languages are also  supported. If more than one language is available, based on a user's  language preference (e.g. browser language), the wiki will try to serve  the page for the appropriate language. If no wiki page exists for that  language, it will fall back to the default language. Comments, however,  are shared amongst all translations of a wiki page. </p><p>New translations for a page cannot be added through the web interface and have to be added through the Subversion repository.  </p><p>To add a translation of a page, first checkout the wiki from Subversion: <br /><br /> <tt>svn checkout https://</tt><strong>yourproject</strong><tt>.googlecode.com/svn/wiki/</tt> <strong>yourdirectory</strong> <tt>-username</tt> <strong>yourusername</strong> </p><p>Then  create a new directory under /wiki/ using the two letter ISO-639 code  as the name of that directory. Place all translated files in the new  directory and submit those changes to the Subversion repository.  </p><p>The following is an example of a valid wiki directory: </p><pre>wiki/<br />&nbsp; &nbsp;ja/<br />&nbsp; &nbsp; &nbsp; ProjectHistory.wiki<br />&nbsp; &nbsp; &nbsp; StyleGuide.wiki<br />&nbsp; &nbsp;zh-Hans/<br />&nbsp; &nbsp; &nbsp; ProjectHistory.wiki<br />&nbsp; &nbsp; &nbsp; StyleGuide.wiki<br />&nbsp; &nbsp;zh-Hant/<br />&nbsp; &nbsp; &nbsp; ProjectHistory.wiki<br />&nbsp; &nbsp; &nbsp; StyleGuide.wiki<br />&nbsp; &nbsp;ProjectHistory.wiki<br />&nbsp; &nbsp;StyleGuide.wiki</pre><p>Once  the files been be submitted to the project's subversion repository,  they can now be edited through the wiki's web editor. The process is the  same for Mercurial or Git projects, except that the wiki lives in the  root directory (not wiki/) of <tt>https://wiki.</tt><strong>yourproject</strong><tt>.googlecode.com/hg/</tt> or <tt>https://wiki.</tt><strong>yourproject</strong><tt>.googlecode.com/git/</tt>. </p><p>Note:  The wiki accepts a subset of ISO-639 two letter language codes, where a  few of the codes (such as zh_Hans) contain locale-specific codes. Such  locale-specific codes should use a hyphen (zh-Hans) separator. A few  example language codes have been specified in the table below. </p><table border="1"> <tbody><tr> <th> Language (Locale)</th> <th> Directory Name </th> </tr> <tr> <td>Arabic</td> <td>ar</td> </tr> <tr> <td>Bulgarian</td> <td>bg</td> </tr> <tr> <td>Chinese (China)</td> <td>zh-Hans</td> </tr> <tr> <td>Chinese (Taiwan)</td> <td>zh-Hant</td> </tr> <tr> <td>Croatian</td> <td>hr</td> </tr> <tr> <td>Czech</td> <td>cs</td> </tr> <tr> <td>Danish</td> <td>da</td> </tr> <tr> <td>Dutch</td> <td>nl</td> </tr> <tr> <td>English (United Kingdom)</td> <td>en-GB</td> </tr> <tr> <td>English (United States)</td> <td>en-US</td> </tr> <tr> <td>Finnish</td> <td>fi</td> </tr> <tr> <td>French</td> <td>fr</td> </tr> <tr> <td>German</td> <td>de</td> </tr> <tr> <td>Greek</td> <td>el</td> </tr> <tr> <td>Hebrew</td> <td>he</td> </tr> <tr> <td>Hungarian</td> <td>hu</td> </tr> <tr> <td>Italian</td> <td>it</td> </tr> <tr> <td>Japanese</td> <td>ja</td> </tr> <tr> <td>Korean</td> <td>ko</td> </tr> <tr> <td>Norwegian</td> <td>no</td> </tr> <tr> <td>Polish</td> <td>pl</td> </tr> <tr> <td>Portuguese (Brazil)</td> <td>pt-BR</td> </tr> <tr> <td>Romanian</td> <td>ro</td> </tr> <tr> <td>Russian</td> <td>ru</td> </tr> <tr> <td>Slovak</td> <td>sk</td> </tr> <tr> <td>Spanish</td> <td>es</td> </tr> <tr> <td>Swedish</td> <td>sv</td> </tr> <tr> <td>Turkish</td> <td>tr</td> </tr> </tbody></table> <p><em>The content on this page created by Google is licensed under the <a href="http://creativecommons.org/licenses/by/3.0/" rel="nofollow">Creative Commons Attribution 3.0 License</a>.  User-generated content is not included in this license.</em> </p>  </div>  </div>  </td></tr><tr> </tr></tbody></table></div><img src ="http://www.cppblog.com/lauer3912/aggbug/159848.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lauer3912/" target="_blank">RTY</a> 2011-11-09 06:52 <a href="http://www.cppblog.com/lauer3912/archive/2011/11/09/159848.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Google Breakpad 完全解析（二） —— Windows前台实现篇</title><link>http://www.cppblog.com/lauer3912/archive/2011/11/07/159785.html</link><dc:creator>RTY</dc:creator><author>RTY</author><pubDate>Mon, 07 Nov 2011 13:36:00 GMT</pubDate><guid>http://www.cppblog.com/lauer3912/archive/2011/11/07/159785.html</guid><wfw:comment>http://www.cppblog.com/lauer3912/comments/159785.html</wfw:comment><comments>http://www.cppblog.com/lauer3912/archive/2011/11/07/159785.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lauer3912/comments/commentRss/159785.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lauer3912/services/trackbacks/159785.html</trackback:ping><description><![CDATA[<div><h2>Google Breakpad 完全解析（二） &#8212;&#8212; Windows前台实现篇</h2> 2011年02月7日 &#8212; Asp J   <div><h3>Table of contents for Google Breakpad 完全解析</h3><ol><li><a href="http://bigasp.com/archives/450" title="Google Breakpad 完全解析（一） &#8212;&#8212; Windows入门篇">Google Breakpad 完全解析（一） &#8212;&#8212; Windows入门篇</a></li><li>Google Breakpad 完全解析（二） &#8212;&#8212; Windows前台实现篇</li></ol></div> <p>原创文章，转载请标明出处：Soul Apogee (<a href="http://bigasp.com/" target="_blank">http://bigasp.com</a>)，谢谢。</p> <p>好，看完了<a href="http://bigasp.com/archives/450" target="_blank">如何使用breakpad</a>，我们现在看看breakpad在Windows下到底是如何实现的呢？</p> <h3><strong>代码结构</strong></h3> <p>在我们来看breakpad是如何实现其强大的功能之前，我们先来看一下他的代码结构吧。</p> <p>Google breakpad的源代码都在src的目录下，他分为如下几个文件夹：<br /> client：这下面包含了前台应用程序中捕捉dump的部分代码，里面按照平台分成各个子文件夹<br /> common：前台后台都会用到的部分基础代码，字符串转换，内存读写，md5神马的<br /> google_breakpad：breakpad中公共的头文件<br /> processor：用于在后台处理崩溃的核心代码<br /> testing：测试工程<br /> third_party：第三方库<br /> tools：一些小工具，用于处理dump文件和符号表</p> <p>我们先来看Windows下前台实现的部分，也就是client文件夹下的代码。</p> <h3><strong>breakpad的崩溃捕获机制</strong></h3> <p>在Windows下捕获崩溃，大家很容易会想到那个捕获结构化异常的Api：<a target="blank" href="http://www.google.com/search?ie=UTF-8&amp;q=SetUnhandledExceptionFilter">SetUnhandledExceptionFilter</a>。</p> <p>breakpad中也使用了这个Api来实现的崩溃捕获，另外，breakpad还捕获了另外两种C++运行库提供的崩溃，一种是使用<a target="blank" href="http://www.google.com/search?ie=UTF-8&amp;q=_set_purecall_handler">_set_purecall_handler</a>捕获纯虚函数调用产生的崩溃，还有一种是使用<a target="blank" href="http://www.google.com/search?ie=UTF-8&amp;q=_set_invalid_parameter_handler">_set_invalid_parameter_handler</a>捕获错误的参数调用产生的崩溃。</p> <div><div id="highlighter_32943"  cpp"=""><div><a href="http://bigasp.com/archives/458#" command_help=""  help"="">?</a></div><table cellpadding="0" cellspacing="0" border="0"><tbody><tr><td><div number1="" index0=""  alt2"="">1</div><div number2="" index1=""  alt1"="">2</div><div number3="" index2=""  alt2"="">3</div><div number4="" index3=""  alt1"="">4</div><div number5="" index4=""  alt2"="">5</div><div number6="" index5=""  alt1"="">6</div><div number7="" index6=""  alt2"="">7</div><div number8="" index7=""  alt1"="">8</div><div number9="" index8=""  alt2"="">9</div><div number10="" index9=""  alt1"="">10</div></td><td><div><div number1="" index0=""  alt2"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;</code><code keyword=""  bold"="">if</code> <code plain"="">(handler_types &amp; HANDLER_EXCEPTION)</code></div><div number2="" index1=""  alt1"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><code plain"="">previous_filter_ = SetUnhandledExceptionFilter(HandleException);</code></div><div number3="" index2=""  alt2"="">&nbsp;</div><div number4="" index3=""  alt1"=""><code preprocessor"="">#if _MSC_VER &gt;= 1400&nbsp;&nbsp;// MSVC 2005/8</code></div><div number5="" index4=""  alt2"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;</code><code keyword=""  bold"="">if</code> <code plain"="">(handler_types &amp; HANDLER_INVALID_PARAMETER)</code></div><div number6="" index5=""  alt1"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><code plain"="">previous_iph_ = _set_invalid_parameter_handler(HandleInvalidParameter);</code></div><div number7="" index6=""  alt2"=""><code preprocessor"="">#endif&nbsp;&nbsp;// _MSC_VER &gt;= 1400</code></div><div number8="" index7=""  alt1"="">&nbsp;</div><div number9="" index8=""  alt2"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;</code><code keyword=""  bold"="">if</code> <code plain"="">(handler_types &amp; HANDLER_PURECALL)</code></div><div number10="" index9=""  alt1"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><code plain"="">previous_pch_ = _set_purecall_handler(HandlePureVirtualCall);</code></div></div></td></tr></tbody></table></div></div> <p>另外由于C++运行库提供的崩溃回调中，并不会提供当前的线程现场和崩溃信息，所以breakpad会自己生成好这些信息，然后请求生成dump。<br /> 这里值得一说的是，在非异常崩溃处理中，breakpad获取线程现场使用的函数是RtlCaptureContext而不是GetThreadContext。<br /> RtlCaptureContext只能捕获当前线程的现场，而GetThreadContext可以捕获任意线程的现场，只要有这个线程的句柄即可。<br /> 但是GetThreadContext有两个不好的地方：不能获取当前线程的现场；获取现场前必须先用SuspendThread暂停目标线程。<br /> 而RtlCaptureContext虽然只能获取当前线程的现场，但是调用他时可以不用暂停线程的运行。<br /> 对于breakpad来说，崩溃发生后越早获取现场就越好，所以breakpad使用RtlCaptureContext函数作为他的线程获取函数。</p> <h3><strong>breakpad中的C/S结构</strong></h3> <p>由于breakpad是在进程外抓取dump，所以breakpad需要实现一个C/S结构来处理崩溃进程抓取dump的请求。</p> <p><strong>1. breakpad跨进程通信的实现<br /> </strong>breakpad中使用了命名管道来实现IPC。</p> <p>在客户端，初始化ExceptionHandler的时候，如果指定了PipeName，也就表示此时需要使用进程外的dump抓 取，ExceptionHandler，会建立一个&nbsp;CrashGenerationClient的对象，由这个对象连接服务端，将自己注册到服务端上 去。<br /> 大家可以参看exception_handler.cc中的ExceptionHandler::Initialize函数。</p> <p>在服务端，初始化CrashGenerationServer的时候，就会建立一个命名管道，并等待客户端来连接。一旦有客户端连接上来，服务端会 为每一个客户端生成一个ClientInfo的对象，之后用这个对象来管理所有的客户端，一旦有崩溃发生，服务端都会从这个对象中取出dump所需要的信 息。<br /> 大家可以参看crash_generation_server.cc中的CrashGenerationServer::HandleReadDoneState函数。</p> <p><strong>2. breakpad捕获崩溃生成dump的流程<br /> </strong>breakpad进程外生成dump的流程大概如下：<br /> <strong>google-breakpad-out-of-process-dump:</strong><br /><a href="http://bigasp.com/wp-content/plugins/download-monitor/download.php?id=138" target="_blank"><img src="http://bigasp.com/wp-content/uploads/downloads/thumbnails/2011/02/google-breakpad-out-of-process-dump.jpg" alt="google-breakpad-out-of-process-dump" style="max-width:500px;" /></a><br /> 这段流程的代码就是crash_generation_client.cc和crash_generation_server.cc。</p> <p>有两个简单的问题，这里说明一下，高手们就请直接忽略吧，咩哈哈：<br /> <strong>在服务端如何为客户端生成事件句柄？</strong><br /> 使用DuplicateHandle，即可把任意一个内核对象的句柄复制到其他进程，并且可以指定产生的句柄的权限。</p> <p><strong>如何异步的等待一个事件？<br /> </strong>使用RegisterWaitForSingleObject，即可异步的等待一个事件，当事件发生的时候，就可以回调到一个指定的回 调函数中，但是要注意的是，RegisterWaitForSingleObject会在一个新的线程中来等待这个事件，此处很容易产生多线程的调用，需 要注意线程问题。</p> <p><strong>3. 服务端关键数据结构：ClientInfo<br /> </strong>ClientInfo是服务端中最重要的数据结构，服务端通过它来管理所有的客户端。客户端注册时，会保存或生成里面所有的信息，在客户端请求生成dump的时候，服务端就会通过ClientInfo获取所有客户端的信息。ClientInfo中保存了如下信息：</p> <ul><li>客户端进程pid和句柄</li><li>生成Minidump的类型</li><li>自定义的客户端信息</li><li>客户端崩溃的线程ID</li><li>客户端崩溃的信息</li><li>客户端请求崩溃所使用的事件句柄</li></ul> <p>这里有一个问题：在客户端发生崩溃时，服务器如何通过ClientInfo获取到客户端的崩溃信息呢？</p> <p>客户端中有几个用于保存崩溃信息的变量，在注册时，客户端会将这几个变量的地址发送至服务端，服务端将其保存在ClientInfo中，然后当崩溃 发生的时候，服务端就可以通过ReadProcessMemory读取客户端中的信息，从而生成dump。这样做就避免了每次发生崩溃，都要通过Pipe 将崩溃信息传递到服务端中去了。</p> <p>这些变量分别是：崩溃的线程ID，EXCEPTION_POINTERS和MDRawAssertionInfo。<br /> EXCEPTION_POINTERS和MDRawAssertionInfo的区别在于，异常崩溃的信息会被写入EXCEPTION_POINTERS，非异常崩溃（非法参数和纯虚函数调用）的信息会被写入MDRawAssertionInfo中。</p> <h3><strong>dump文件的上传</strong></h3> <p>在breakpad的工程中，有一个工程叫做：crash_report_sender，里面是一个上传崩溃文件的类，他的实现很简单，他使用Windows Internet Api来完成dump文件的上传。<br /> 在使用crash_report_sender时，可以为其指定一个checkpoint_file。</p> <div><div id="highlighter_216810"  cpp"=""><div><a href="http://bigasp.com/archives/458#" command_help=""  help"="">?</a></div><table cellpadding="0" cellspacing="0" border="0"><tbody><tr><td><div number1="" index0=""  alt2"="">1</div></td><td><div><div number1="" index0=""  alt2"=""><code keyword=""  bold"="">explicit</code> <code plain"="">CrashReportSender(</code><code keyword=""  bold"="">const</code> <code plain"="">wstring &amp;checkpoint_file);</code></div></div></td></tr></tbody></table></div></div> <p>这个文件只有一个作用，就是用来保存上次上传崩溃的时间和今天上传过的崩溃的次数。通过这个文件，我们就可以来设置每日上传的崩溃的最大数量。</p> <div><div id="highlighter_535960"  cpp"=""><div><a href="http://bigasp.com/archives/458#" command_help=""  help"="">?</a></div><table cellpadding="0" cellspacing="0" border="0"><tbody><tr><td><div number1="" index0=""  alt2"="">1</div><div number2="" index1=""  alt1"="">2</div><div number3="" index2=""  alt2"="">3</div><div number4="" index3=""  alt1"="">4</div><div number5="" index4=""  alt2"="">5</div><div number6="" index5=""  alt1"="">6</div><div number7="" index6=""  alt2"="">7</div><div number8="" index7=""  alt1"="">8</div><div number9="" index8=""  alt2"="">9</div><div number10="" index9=""  alt1"="">10</div><div number11="" index10=""  alt2"="">11</div><div number12="" index11=""  alt1"="">12</div><div number13="" index12=""  alt2"="">13</div><div number14="" index13=""  alt1"="">14</div><div number15="" index14=""  alt2"="">15</div><div number16="" index15=""  alt1"="">16</div><div number17="" index16=""  alt2"="">17</div><div number18="" index17=""  alt1"="">18</div><div number19="" index18=""  alt2"="">19</div><div number20="" index19=""  alt1"="">20</div><div number21="" index20=""  alt2"="">21</div><div number22="" index21=""  alt1"="">22</div><div number23="" index22=""  alt2"="">23</div><div number24="" index23=""  alt1"="">24</div></td><td><div><div number1="" index0=""  alt2"=""><code plain"="">CrashReportSender::CrashReportSender(</code><code keyword=""  bold"="">const</code> <code plain"="">wstring &amp;checkpoint_file)</code></div><div number2="" index1=""  alt1"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;</code><code plain"="">: checkpoint_file_(checkpoint_file),</code></div><div number3="" index2=""  alt2"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><code plain"="">max_reports_per_day_(-1),</code></div><div number4="" index3=""  alt1"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><code plain"="">last_sent_date_(-1),</code></div><div number5="" index4=""  alt2"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><code plain"="">reports_sent_(0) {</code></div><div number6="" index5=""  alt1"=""><code spaces"="">&nbsp;&nbsp;</code><code color1=""  bold"="">FILE</code> <code plain"="">*fd;</code></div><div number7="" index6=""  alt2"=""><code spaces"="">&nbsp;&nbsp;</code><code keyword=""  bold"="">if</code> <code plain"="">(OpenCheckpointFile(L</code><code string"="">"r"</code><code plain"="">, &amp;fd) == 0) {</code></div><div number8="" index7=""  alt1"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;</code><code plain"="">ReadCheckpoint(fd);</code></div><div number9="" index8=""  alt2"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;</code><code functions=""  bold"="">fclose</code><code plain"="">(fd);</code></div><div number10="" index9=""  alt1"=""><code spaces"="">&nbsp;&nbsp;</code><code plain"="">}</code></div><div number11="" index10=""  alt2"=""><code plain"="">}</code></div><div number12="" index11=""  alt1"="">&nbsp;</div><div number13="" index12=""  alt2"=""><code plain"="">ReportResult CrashReportSender::SendCrashReport(</code></div><div number14="" index13=""  alt1"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;</code><code keyword=""  bold"="">const</code> <code plain"="">wstring &amp;url, </code><code keyword=""  bold"="">const</code> <code plain"="">map&lt;wstring, wstring&gt; &amp;parameters,</code></div><div number15="" index14=""  alt2"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;</code><code keyword=""  bold"="">const</code> <code plain"="">wstring &amp;dump_file_name, wstring *report_code) {</code></div><div number16="" index15=""  alt1"=""><code spaces"="">&nbsp;&nbsp;</code><code color1=""  bold"="">int</code> <code plain"="">today = GetCurrentDate();</code></div><div number17="" index16=""  alt2"=""><code spaces"="">&nbsp;&nbsp;</code><code keyword=""  bold"="">if</code> <code plain"="">(today == last_sent_date_ &amp;&amp;</code></div><div number18="" index17=""  alt1"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><code plain"="">max_reports_per_day_ != -1 &amp;&amp;</code></div><div number19="" index18=""  alt2"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><code plain"="">reports_sent_ &gt;= max_reports_per_day_) {</code></div><div number20="" index19=""  alt1"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;</code><code keyword=""  bold"="">return</code> <code plain"="">RESULT_THROTTLED;</code></div><div number21="" index20=""  alt2"=""><code spaces"="">&nbsp;&nbsp;</code><code plain"="">}</code></div><div number22="" index21=""  alt1"="">&nbsp;</div><div number23="" index22=""  alt2"=""><code spaces"="">&nbsp;&nbsp;</code><code comments"="">// 上传文件部分代码，省略</code></div><div number24="" index23=""  alt1"=""><code plain"="">}</code></div></div></td></tr></tbody></table></div></div> <p>调整每日上传崩溃的最大数量的函数是set_max_reports_per_day。</p> <p>需要注意的是：在上传dump文件的时候，crash_report_sender并不会对dump文件进行分析，而是直接上传整个dump文件， 如果你需要上传的dump文件非常大的话，可以考虑把崩溃分析处理的逻辑放入前台，通过去重或者直接上传分析结果，减少上传的文件大小。</p> <h3><strong>breakpad存在的问题</strong></h3> <h3><span style="font-weight: normal; font-size: 13px;">进程外生成dump有很多好处，其中最大的好处就是不会被崩溃进程影响，这样dump的过程就不容易出错，但是这样也有一定的弊端。</span></h3> <p><strong>1. 部分崩溃无法抓取<br /> </strong>在一些极端的崩溃，如堆栈溢出之类的崩溃，进程外抓取dump有时候会失败。</p> <p><strong>2. 无法抓取死锁或者其他原因导致的进程僵死<br /> </strong>breakpad现在没有检测进程死锁的代码，也没有在服务端控制客户端请求dump的代码，所以现在breakpad无法抓取死锁等进程僵死的问题。不过因为breakpad的定位是处理崩溃，如果有这种需要的童鞋，可以自行修改breakpad的代码，添加这些功能。</p> <p><strong>3. 对服务端有依赖<br /> </strong>如果指定了在使用进程外抓取dump，breakpad对服务端就有依赖。主要体现在抓取dump时，如果服务端不存在，客户端将无法正常抓取dump，甚至有时会出现阻塞。</p> <p>当然对于这些问题，随着breakpad的发展肯定会越来越完善。如果，你遇到了了这些问题，而又绕过不了，那就改代码，并且提交给breakpad吧，开源项目就是这么发展的。</p> <p>好，到此breakpad的Windows实现就已经说完了，如果有神马问题，还请多多指教。谢谢大家。</p><p style="margin-bottom:0pt; margin-top:0pt; "><a href="http://www.0531jsk.com/"><span style=" color:#0000ff; text-decoration:underline ;font-family:'宋体'; ">北京德胜门中医院</span></a><a href="http://www.0531jsk.com/"><span style=" color:#0000ff; text-decoration:underline ;font-family:'宋体'; ">http://www.0531jsk.com/</span></a><span style=" font-size:10.5000pt; font-family:'宋体'; ">德胜门中医院</span></p></div><img src ="http://www.cppblog.com/lauer3912/aggbug/159785.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lauer3912/" target="_blank">RTY</a> 2011-11-07 21:36 <a href="http://www.cppblog.com/lauer3912/archive/2011/11/07/159785.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Google Breakpad 完全解析（一） —— Windows入门篇</title><link>http://www.cppblog.com/lauer3912/archive/2011/11/07/159784.html</link><dc:creator>RTY</dc:creator><author>RTY</author><pubDate>Mon, 07 Nov 2011 13:34:00 GMT</pubDate><guid>http://www.cppblog.com/lauer3912/archive/2011/11/07/159784.html</guid><wfw:comment>http://www.cppblog.com/lauer3912/comments/159784.html</wfw:comment><comments>http://www.cppblog.com/lauer3912/archive/2011/11/07/159784.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lauer3912/comments/commentRss/159784.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lauer3912/services/trackbacks/159784.html</trackback:ping><description><![CDATA[<div><h2>Google Breakpad 完全解析（一） &#8212;&#8212; Windows入门篇</h2> 2011年01月23日 &#8212; Asp J   <div><h3>Table of contents for Google Breakpad 完全解析</h3><ol><li>Google Breakpad 完全解析（一） &#8212;&#8212; Windows入门篇</li><li><a href="http://bigasp.com/archives/458" title="Google Breakpad 完全解析（二） &#8212;&#8212; Windows前台实现篇">Google Breakpad 完全解析（二） &#8212;&#8212; Windows前台实现篇</a></li></ol></div> <p>原创文章，转载请标明出处：Soul Apogee (<a href="http://bigasp.com/" target="_blank">http://bigasp.com</a>)，谢谢。  </p> <p><a title="Google Breakpad" href="http://code.google.com/p/google-breakpad/" target="_blank">Google breakpad</a>是 一个非常实用的跨平台的崩溃转储和分析模块，他支持Windows，Linux和Mac和Solaris。由于他本身跨平台，所以很大的减少我们在平台移 植时的工作，毕竟崩溃转储，每个平台下都不同，使用起来很难统一，而Google  breakpad就帮我们做到了这一点，不管是哪个平台下的崩溃，都能够进行统一的分析。现在很多工程都在使用他：最著名的几个如 Chrome，Firefox，Picasa和Google  Earth。另外他的License是BSD的，也就是说，我们即便是在商业软件中使用，也是合法的，哈哈，这么好的东西，我们能放过么？现在就让我们来 看看这个神奇的软件吧。</p> <h3><strong>原理简介</strong></h3> <p>breakpad抓取dump的方式和一般我们抓取dump的方式不一样。在breakpad的wiki上有一幅图可以很好的概括他的原理。</p> <p><img title="breakpad" src="http://google-breakpad.googlecode.com/svn/wiki/breakpad.png" alt="" height="464" width="425" /></p> <p>breakpad把应用程序分成三个部分，代码，breakpad客户端和调试信息。</p> <p>1. 在build system中，通过symbol dumper用平台相关的调试信息生成平台无关的symbol文件。这样做的好处很明显，一旦平台无关了，所有平台的崩溃就可以做统一的分析了。<br /> 2. breakpad采取进程外转储和分析崩溃的方式，他使用C/S结构，客户端用来捕获当前进程中发生的崩溃，并通知服务端崩溃发生。服务端用来响应客户端，抓取dump文件。这样做的目的是为了减少崩溃进程对dump的影响。<br /> 3. Dump生成后转发到崩溃分析器中，这个部分可以在本地也可以在服务器上，他对Dump文件进行解析，生成可读的堆栈信息。</p> <p>这就是breakpad处理dump大概的流程。</p> <p>对于原理的介绍google写的已经相当好了。更多的详细信息，可以直接移步到<a href="http://code.google.com/p/google-breakpad/w/list" target="_blank">breakpad的wiki</a>。</p> <h3><strong>安装和编译</strong></h3> <p>breakpad的编译比较曲折，所以在此记录一下。</p> <p>编译breakpad，请确认你的机器上装有以下的软件：<br /> 1. <a href="http://www.python.org/download/releases/2.4.3/" target="_blank">python 2.4.3</a><a href="http://www.python.org/download/releases/2.4.3/" target="_blank"><br /> </a>请不要使用python3，会报错。另外python2中推荐这个版本，使用新的版本在编译其他google的工程时有时会报错</p> <p>2. <a href="http://www.google.com/search?q=windows+sdk+7" target="_blank">Windows SDK 7</a><a href="http://www.google.com/search?q=windows+sdk+7" target="_blank"><br /> </a>如果没有这个，编译会报错。另外这个是在线安装，时间很久，最好并行做其他的事情。</p> <p>3. VS2005的补丁<br /> KB918559<br /> KB926601<br /> KB935225<br /> KB943969<br /> KB947315</p> <p>已经安装了以上软件的童鞋，就可以开始进行下面的工作鸟</p> <p>1. 使用svn把代码<a href="http://code.google.com/p/google-breakpad/source/checkout" target="_blank">checkout</a>下来</p> <div><div id="highlighter_301422"  shell"=""><div><a href="http://bigasp.com/archives/450#" command_help=""  help"="">?</a></div><table border="0" cellpadding="0" cellspacing="0"><tbody><tr><td><div number1="" index0=""  alt2"="">1</div><div number2="" index1=""  alt1"="">2</div></td><td><div><div number1="" index0=""  alt2"=""><code comments"=""># Non-members may check out a read-only working copy anonymously over HTTP.</code></div><div number2="" index1=""  alt1"=""><code plain"="">svn checkout http:</code><code plain"="">//google-breakpad</code><code plain"="">.googlecode.com</code><code plain"="">/svn/trunk/</code> <code plain"="">google-breakpad-</code><code functions"="">read</code><code plain"="">-only</code></div></div></td></tr></tbody></table></div></div> <p>2. 设置Windows SDK 7<br /> 装过其他版本Windows SDK的童鞋，记得一定要进行这一步，SDK的安装程序，并不会帮你设置VS。<br /> 运行开始菜单-&gt;程序-&gt;Microsoft Windows SDK v7.0-&gt;Visual Studio  Registration-&gt;Windows SDK Configuration Tool，选择v7.0，点击Make Current。</p> <p>3. 为python设置环境变量<br /> 由于breakpad使用python来生成Windows下的工程文件，所以需要将python所在目录，设置到环境变量PATH中去。</p> <p>4. 生成Windows工程文件</p> <div><div id="highlighter_825083"  shell"=""><div><a href="http://bigasp.com/archives/450#" command_help=""  help"="">?</a></div><table border="0" cellpadding="0" cellspacing="0"><tbody><tr><td><div number1="" index0=""  alt2"="">1</div><div number2="" index1=""  alt1"="">2</div><div number3="" index2=""  alt2"="">3</div><div number4="" index3=""  alt1"="">4</div></td><td><div><div number1="" index0=""  alt2"=""><code functions"="">cd</code> <code string"="">"源码目录/src/tools/gyp"</code></div><div number2="" index1=""  alt1"="">&nbsp;</div><div number3="" index2=""  alt2"=""><code comments"=""># 注意，此处不能使用全路径，不然会出错</code></div><div number4="" index3=""  alt1"=""><code plain"="">gyp.bat </code><code string"="">"../../client/windows/breakpad_client.gyp"</code></div></div></td></tr></tbody></table></div></div> <p>此时，在src/client/windows下就可以看到生成好的breakpad_client.sln了。运行吧！</p> <p>5. Hello World!<br /> 编译build all，现在一般是不会报错了，如果报错，请检查是不是漏了什么步骤，特别是补丁。<br /> 编译完成之后，运行crash_generation_app吧，这是他的测试程序，dump的默认位置保存在C:Dumps下，请注意先建立好目录，不然会无法使用。<br /> 启动测试程序之后，此时还不能抓取dump，因为这个是breakpad中的服务器端，需要再启动一个测试程序，在第二个测试程序中，我们就可以试验Client菜单中的各种崩溃了。这些崩溃都会被抓住转存到C:Dumps目录下。</p> <h3><strong>如何使用breakpad</strong></h3> <p> </p> <p>在Windows下<a href="http://code.google.com/p/google-breakpad/wiki/WindowsClientIntegration" target="_blank">使用breakpad的方法</a>很简单，只需要创建一个ExceptionHandler的类即可，大家可以在crash_generation_app这个工程中找到示例代码，也可以直接<a href="http://code.google.com/p/google-breakpad/wiki/WindowsClientIntegration" target="_blank">移步</a>Wiki，上面说的也很详细。</p> <p>1.进程内抓取Dump文件</p> <p>进程内抓取Dump文件是最简单的breakpad的用法。使用方法很简单：</p> <div><div id="highlighter_394770"  cpp"=""><div><a href="http://bigasp.com/archives/450#" command_help=""  help"="">?</a></div><table border="0" cellpadding="0" cellspacing="0"><tbody><tr><td><div number1="" index0=""  alt2"="">1</div><div number2="" index1=""  alt1"="">2</div><div number3="" index2=""  alt2"="">3</div><div number4="" index3=""  alt1"="">4</div><div number5="" index4=""  alt2"="">5</div><div number6="" index5=""  alt1"="">6</div><div number7="" index6=""  alt2"="">7</div><div number8="" index7=""  alt1"="">8</div><div number9="" index8=""  alt2"="">9</div><div number10="" index9=""  alt1"="">10</div><div number11="" index10=""  alt2"="">11</div><div number12="" index11=""  alt1"="">12</div><div number13="" index12=""  alt2"="">13</div><div number14="" index13=""  alt1"="">14</div><div number15="" index14=""  alt2"="">15</div><div number16="" index15=""  alt1"="">16</div><div number17="" index16=""  alt2"="">17</div><div number18="" index17=""  alt1"="">18</div><div number19="" index18=""  alt2"="">19</div><div number20="" index19=""  alt1"="">20</div><div number21="" index20=""  alt2"="">21</div></td><td><div><div number1="" index0=""  alt2"=""><code keyword=""  bold"="">const</code> <code plain"="">std::wstring s_strCrashDir = L</code><code string"="">"c:\dumps"</code><code plain"="">;</code></div><div number2="" index1=""  alt1"="">&nbsp;</div><div number3="" index2=""  alt2"=""><code color1=""  bold"="">bool</code></div><div number4="" index3=""  alt1"=""><code plain"="">InitBreakpad()</code></div><div number5="" index4=""  alt2"=""><code plain"="">{</code></div><div number6="" index5=""  alt1"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;</code><code plain"="">google_breakpad::ExceptionHandler *pCrashHandler =</code></div><div number7="" index6=""  alt2"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><code keyword=""  bold"="">new</code> <code plain"="">google_breakpad::ExceptionHandler(s_strCrashDir,</code></div><div number8="" index7=""  alt1"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><code plain"="">onExceptionFilter,</code></div><div number9="" index8=""  alt2"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><code plain"="">onMinidumpDumped,</code></div><div number10="" index9=""  alt1"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><code plain"="">NULL,</code></div><div number11="" index10=""  alt2"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><code plain"="">google_breakpad::ExceptionHandler::HANDLER_ALL,</code></div><div number12="" index11=""  alt1"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><code plain"="">MiniDumpNormal,</code></div><div number13="" index12=""  alt2"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><code plain"="">NULL,</code></div><div number14="" index13=""  alt1"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><code plain"="">NULL);</code></div><div number15="" index14=""  alt2"="">&nbsp;</div><div number16="" index15=""  alt1"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;</code><code keyword=""  bold"="">if</code><code plain"="">(pCrashHandler == NULL) {</code></div><div number17="" index16=""  alt2"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><code keyword=""  bold"="">return</code> <code keyword=""  bold"="">false</code><code plain"="">;</code></div><div number18="" index17=""  alt1"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;</code><code plain"="">}</code></div><div number19="" index18=""  alt2"="">&nbsp;</div><div number20="" index19=""  alt1"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;</code><code keyword=""  bold"="">return</code> <code keyword=""  bold"="">true</code><code plain"="">;</code></div><div number21="" index20=""  alt2"=""><code plain"="">}</code></div></div></td></tr></tbody></table></div></div> <p>2.进程外抓取Dump文件</p> <p>使用进程外抓取Dump时，需要指定服务端和客户端，在服务端中需要创建CrashGenerationServer的实例，而在客户端中则只需要创建ExceptionHandler即可。此外，如果服务端自己需要抓进程内的Dump，请将pipe的参数置为NULL。</p> <div><div id="highlighter_709395"  cpp"=""><div><a href="http://bigasp.com/archives/450#" command_help=""  help"="">?</a></div><table border="0" cellpadding="0" cellspacing="0"><tbody><tr><td><div number1="" index0=""  alt2"="">1</div><div number2="" index1=""  alt1"="">2</div><div number3="" index2=""  alt2"="">3</div><div number4="" index3=""  alt1"="">4</div><div number5="" index4=""  alt2"="">5</div><div number6="" index5=""  alt1"="">6</div><div number7="" index6=""  alt2"="">7</div><div number8="" index7=""  alt1"="">8</div><div number9="" index8=""  alt2"="">9</div><div number10="" index9=""  alt1"="">10</div><div number11="" index10=""  alt2"="">11</div><div number12="" index11=""  alt1"="">12</div><div number13="" index12=""  alt2"="">13</div><div number14="" index13=""  alt1"="">14</div><div number15="" index14=""  alt2"="">15</div><div number16="" index15=""  alt1"="">16</div><div number17="" index16=""  alt2"="">17</div><div number18="" index17=""  alt1"="">18</div><div number19="" index18=""  alt2"="">19</div><div number20="" index19=""  alt1"="">20</div><div number21="" index20=""  alt2"="">21</div><div number22="" index21=""  alt1"="">22</div><div number23="" index22=""  alt2"="">23</div><div number24="" index23=""  alt1"="">24</div><div number25="" index24=""  alt2"="">25</div><div number26="" index25=""  alt1"="">26</div><div number27="" index26=""  alt2"="">27</div><div number28="" index27=""  alt1"="">28</div><div number29="" index28=""  alt2"="">29</div><div number30="" index29=""  alt1"="">30</div><div number31="" index30=""  alt2"="">31</div><div number32="" index31=""  alt1"="">32</div><div number33="" index32=""  alt2"="">33</div><div number34="" index33=""  alt1"="">34</div><div number35="" index34=""  alt2"="">35</div><div number36="" index35=""  alt1"="">36</div><div number37="" index36=""  alt2"="">37</div><div number38="" index37=""  alt1"="">38</div><div number39="" index38=""  alt2"="">39</div><div number40="" index39=""  alt1"="">40</div><div number41="" index40=""  alt2"="">41</div><div number42="" index41=""  alt1"="">42</div><div number43="" index42=""  alt2"="">43</div><div number44="" index43=""  alt1"="">44</div></td><td><div><div number1="" index0=""  alt2"=""><code keyword=""  bold"="">const</code> <code color1=""  bold"="">wchar_t</code> <code plain"="">s_pPipeName[] = L</code><code string"="">"\\.\pipe\breakpad\crash_handler_server"</code><code plain"="">;</code></div><div number2="" index1=""  alt1"=""><code keyword=""  bold"="">const</code> <code plain"="">std::wstring s_strCrashDir = L</code><code string"="">"c:\dumps"</code><code plain"="">;</code></div><div number3="" index2=""  alt2"="">&nbsp;</div><div number4="" index3=""  alt1"=""><code color1=""  bold"="">bool</code></div><div number5="" index4=""  alt2"=""><code plain"="">InitBreakpad()</code></div><div number6="" index5=""  alt1"=""><code plain"="">{</code></div><div number7="" index6=""  alt2"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;</code><code plain"="">google_breakpad::CrashGenerationServer *pCrashServer =</code></div><div number8="" index7=""  alt1"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><code keyword=""  bold"="">new</code> <code plain"="">google_breakpad::CrashGenerationServer(s_pPipeName,</code></div><div number9="" index8=""  alt2"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><code plain"="">NULL,</code></div><div number10="" index9=""  alt1"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><code plain"="">onClientConnected,</code></div><div number11="" index10=""  alt2"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><code plain"="">NULL,</code></div><div number12="" index11=""  alt1"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><code plain"="">onClientDumpRequest,</code></div><div number13="" index12=""  alt2"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><code plain"="">NULL,</code></div><div number14="" index13=""  alt1"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><code plain"="">onClientExited,</code></div><div number15="" index14=""  alt2"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><code plain"="">NULL,</code></div><div number16="" index15=""  alt1"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><code keyword=""  bold"="">true</code><code plain"="">,</code></div><div number17="" index16=""  alt2"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><code plain"="">&amp;s_strCrashDir);</code></div><div number18="" index17=""  alt1"="">&nbsp;</div><div number19="" index18=""  alt2"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;</code><code keyword=""  bold"="">if</code><code plain"="">(pCrashServer == NULL) {</code></div><div number20="" index19=""  alt1"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><code keyword=""  bold"="">return</code> <code keyword=""  bold"="">false</code><code plain"="">;</code></div><div number21="" index20=""  alt2"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;</code><code plain"="">}</code></div><div number22="" index21=""  alt1"="">&nbsp;</div><div number23="" index22=""  alt2"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;</code><code comments"="">// 如果已经服务端已经启动了，此处启动会失败</code></div><div number24="" index23=""  alt1"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;</code><code keyword=""  bold"="">if</code><code plain"="">(!pCrashServer-&gt;Start()) {</code></div><div number25="" index24=""  alt2"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><code keyword=""  bold"="">delete</code> <code plain"="">pCrashServer;</code></div><div number26="" index25=""  alt1"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><code plain"="">pCrashServer = NULL;</code></div><div number27="" index26=""  alt2"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;</code><code plain"="">}</code></div><div number28="" index27=""  alt1"="">&nbsp;</div><div number29="" index28=""  alt2"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;</code><code plain"="">google_breakpad::ExceptionHandler *pCrashHandler =</code></div><div number30="" index29=""  alt1"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><code keyword=""  bold"="">new</code> <code plain"="">google_breakpad::ExceptionHandler(s_strCrashDir,</code></div><div number31="" index30=""  alt2"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><code plain"="">onExceptionFilter,</code></div><div number32="" index31=""  alt1"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><code plain"="">onMinidumpDumped,</code></div><div number33="" index32=""  alt2"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><code plain"="">NULL,</code></div><div number34="" index33=""  alt1"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><code plain"="">google_breakpad::ExceptionHandler::HANDLER_ALL,</code></div><div number35="" index34=""  alt2"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><code plain"="">MiniDumpNormal,</code></div><div number36="" index35=""  alt1"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><code plain"="">(pCrashServer == NULL) ? s_pPipeName : NULL, </code><code comments"="">// 如果是服务端，则直接使用进程内dump</code></div><div number37="" index36=""  alt2"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><code plain"="">NULL);</code></div><div number38="" index37=""  alt1"="">&nbsp;</div><div number39="" index38=""  alt2"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;</code><code keyword=""  bold"="">if</code><code plain"="">(pCrashHandler == NULL) {</code></div><div number40="" index39=""  alt1"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><code keyword=""  bold"="">return</code> <code keyword=""  bold"="">false</code><code plain"="">;</code></div><div number41="" index40=""  alt2"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;</code><code plain"="">}</code></div><div number42="" index41=""  alt1"="">&nbsp;</div><div number43="" index42=""  alt2"=""><code spaces"="">&nbsp;&nbsp;&nbsp;&nbsp;</code><code keyword=""  bold"="">return</code> <code keyword=""  bold"="">true</code><code plain"="">;</code></div><div number44="" index43=""  alt1"=""><code plain"="">}</code></div></div></td></tr></tbody></table></div></div> <p>使用breakpad的时候，有两个地方需要注意：<br /> 1. 记得把breakpad的solution下的几个工程，包含到你开发的工程中，或者直接包含他们的lib。<br /> common：基础功能，包含一个对GUID的封装和http上传的类。<br /> exception_handler：用来捕获崩溃的类。<br /> crash_generation_server：breakpad的服务端，用来在产生崩溃时抓取dump。<br /> crash_generation_client：breakpad的客户端，用来捕获当前进程的崩溃。</p> <p>2. 在初始化breakpad之前，记得先创建好dump文件的目录，不然breakpad服务端将不能正常的写dump，这会导致breakpad客户端在崩溃时无限等待服务端dump写完的消息，最后失去响应。</p></div><img src ="http://www.cppblog.com/lauer3912/aggbug/159784.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lauer3912/" target="_blank">RTY</a> 2011-11-07 21:34 <a href="http://www.cppblog.com/lauer3912/archive/2011/11/07/159784.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>调试Release发布版程序的Crash错误 （转）</title><link>http://www.cppblog.com/lauer3912/archive/2011/11/07/159781.html</link><dc:creator>RTY</dc:creator><author>RTY</author><pubDate>Mon, 07 Nov 2011 13:15:00 GMT</pubDate><guid>http://www.cppblog.com/lauer3912/archive/2011/11/07/159781.html</guid><wfw:comment>http://www.cppblog.com/lauer3912/comments/159781.html</wfw:comment><comments>http://www.cppblog.com/lauer3912/archive/2011/11/07/159781.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lauer3912/comments/commentRss/159781.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lauer3912/services/trackbacks/159781.html</trackback:ping><description><![CDATA[<div><h1><a id="viewpost1_TitleUrl" href="../../Walker/articles/146153.html">调试Release发布版程序的Crash错误 （转）</a></h1> 	 		<div> 			<h2><a id="viewpost1_TitleUrl" href="../../woaidongmao/archive/2011/05/10/146092.html">调试Release发布版程序的Crash错误</a> </h2> <div> <p><a title="http://blog.sina.com.cn/s/blog_48f93b530100fsln.html" href="http://blog.sina.com.cn/s/blog_48f93b530100fsln.html">http://blog.sina.com.cn/s/blog_48f93b530100fsln.html</a></p> <p>&nbsp;</p> <p>在<span>Windows平台下用C++开发应用程序，最不想见到的情况恐怕就是程序崩溃，而要想解决引起问题的bug，最困难的应该就是调试release版本了。因为release版本来就少了很多调试信息，更何况一般都是发布出去由用户使用，crash的现场很难保留和重现。本文将给出几个解决方案，完成对release版应用程序crash错误的调试。（本文只讨论Windows平台MSVC环境下的调试，对于其他平台和开发环境没有关注，请大家自己借鉴和尝试。）</span></p> <p>&nbsp;<wbr></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp; <wbr><strong>方案一：崩溃地址</strong> <span><strong><span>+ MAP</span></strong></span><strong>文件</strong></p> <p><span>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> </span>这种方案只能对<span><span>VC7</span>以前的版本开发的程序使用。<span>&nbsp;<wbr></span></span></p>  <p><span>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> <strong>1</strong></span><strong>、崩溃地址</strong></p> <p><span>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>所谓崩溃地址就是引起程序崩溃的内存地址，在<span>WinXP</span>下应用程序<span>crash</span>的对话框如下图：</span></p> <p align="center"><a href="http://photo.blog.sina.com.cn/showpic.html#blogid=48f93b530100fsln&amp;url=http://s5.sinaimg.cn/orignal/48f93b53g79aad1fff694&amp;690" target="_blank"><span><span><img title="clip_image001" alt="clip_image001" src="../../images/cppblog_com/woaidongmao/WindowsLiveWriter/ReleaseCrash_A881/clip_image001_0ad42579-f1c9-44ae-b2f8-b50b8a737e0d.jpg" height="181" width="424" border="0" /></span></span></a></p> <p align="center"><a href="http://photo.blog.sina.com.cn/showpic.html#blogid=48f93b530100fsln&amp;url=http://s7.sinaimg.cn/orignal/48f93b53g79aad2c60546&amp;690" target="_blank"><span><span><img title="clip_image002" alt="clip_image002" src="../../images/cppblog_com/woaidongmao/WindowsLiveWriter/ReleaseCrash_A881/clip_image002_cb3f00fa-aa22-4301-a42c-92c12dff2ea4.jpg" height="120" width="494" border="0" /></span></span></a></p> <p align="center"><a href="http://photo.blog.sina.com.cn/showpic.html#blogid=48f93b530100fsln&amp;url=http://s2.sinaimg.cn/orignal/48f93b53g79aad34a5521&amp;690" target="_blank"><span><span><img title="clip_image003" alt="clip_image003" src="../../images/cppblog_com/woaidongmao/WindowsLiveWriter/ReleaseCrash_A881/clip_image003_fea81ac9-41ea-42be-a24f-d0fe99dafa80.jpg" height="380" width="494" border="0" /></span></span></a></p> <p><span>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> </span><span>上面第<span>2</span>张图中画红线的值为<span>crash</span>的代码偏移地址，第<span>3</span>张图为即<span>crash</span>绝对地址；一般引起<span>crash</span>的原因多为内存操作错误，我们用这两个地址和<span>MAP</span>文件就能定位出错的代码行。</span></p> <p><span>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> <strong>2</strong></span><strong>、<span><span>MAP</span>文件</span></strong></p> <p><span>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> MAP</span>文件是记录应用程序信息的文件（文本文件），里面大概包含了程序的全局符号、源码模块名、源码文件和行号等信息，而这些信息能够帮助我们定位出错的代码行。</p> <p><span>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> </span>怎样生成<span><span>MAP</span>文件呢？以<span>VC6</span>为例，在 <span>Project Settings -&gt; C/C++ <wbr>-&gt; Debug info</span>中，选择 <span>Line Numbers Only </span>；在 <span>Project Settings -&gt; Link </span>中，选择 <span>Generate mapfile</span>项，并在<span>Project Options </span>里面输入 </span><span>/MAPINFO:LINES</span> 和 <span><span>/MAPINFO:EXPORTS</span>，重新编译程序就会生成<span>.map</span>文件。</span></p> <p><span>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> </span>以上设置对应的编译链接选项分别分：</p> <p><span>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> </span><strong><span>/Zi</span></strong> &#8212; 表示生成<span><span>pdb</span>调试信息；</span></p> <p><span>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> </span><strong><span>/MAP[:filename]</span></strong> &#8212; 表示生成<span><span>map</span>文件名；</span></p> <p><span>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> </span><strong><span>/MAPINFO:EXPORTS <wbr></span></strong>&#8212; 表示生成的<span><span>map</span>文件中加入<span>exported functions</span>（生成<span>DLL</span>文件时）；</span></p> <p><span>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> </span><strong><span>/MAPINFO:LINES <wbr></span></strong>&#8212; 表示生成的<span><span>map</span>文件中加入代码行信息。</span></p> <p><span>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> </span>由于<span><span>/MAPINFO:LINES</span>选项在<span>VC8</span>以后的版本中不再支持，因此通过<span>MAP</span>文件中的信息和<span>crash</span>地址定位出错代码行就比较困难了，所以这种方案只能在<span>VC7</span>及以前的版本中使用。</span></p> <p><span>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>一个<span>MAP</span>文件片段示例如下：<span>&nbsp;<wbr></span></span></p>  <p><span>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> <a href="http://photo.blog.sina.com.cn/showpic.html#blogid=48f93b530100fsln&amp;url=http://s4.sinaimg.cn/orignal/48f93b53g79aae50a0e23&amp;690" target="_blank"><span><span><img title="clip_image004" alt="clip_image004" src="../../images/cppblog_com/woaidongmao/WindowsLiveWriter/ReleaseCrash_A881/clip_image004_fbf07de5-1b63-4473-917f-f6695904c9be.jpg" height="229" width="494" border="0" /></span>&nbsp;<wbr></span></a>&nbsp;<wbr></span></p>  <p><span>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> <a href="http://photo.blog.sina.com.cn/showpic.html#blogid=48f93b530100fsln&amp;url=http://s1.sinaimg.cn/orignal/48f93b53g79aae6db2060&amp;690" target="_blank"><span><span><img title="clip_image005" alt="clip_image005" src="../../images/cppblog_com/woaidongmao/WindowsLiveWriter/ReleaseCrash_A881/clip_image005_93f97fcb-0900-4f57-baf2-29a04f821fa2.jpg" height="227" width="494" border="0" /></span></span></a></span></p>  <p><span>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> </span><span>图中<span>Rva+Base</span>列的地址为该行函数对应的函数绝对地址，<span>Address</span>列中冒号后面的地址为函数相对偏移地址。<span>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr></span></span></p>  <p><span>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> <strong>3</strong></span><strong>、定位<span><span>crash</span>代码</span></strong></p> <p><span>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> </span>有了上面的介绍，定位<span><span>crash</span>代码就很简单了。用下面的公式来进行定位：</span></p> <p><span>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> </span><strong>崩溃行偏移 <span><span>= </span>崩溃地址 <span>- </span>崩溃函数绝对地址 <span>+ </span>函数相对偏移</span></strong></p> <p><span>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> </span>我们首先根据崩溃地址（绝对地址），按照找到第<span><span>2</span>张图中<span>Rva+Base</span>列的地址找到发生崩溃的函数（即崩溃地址大于该函数行的<span>Rva+Base</span>地址且小于下个函数的地址），然后找到该行对应的函数相对偏移地址，带入公式中，就得到了崩溃行偏移，该值表示崩溃行的代码相对于代码所在函数的偏移量。用该值去与第<span>3</span>张图中对应函数冒号后面的偏移量去比较，最接近的值前面的那个十进制数即为代码所在函数中的行号。</span></p> <p><span>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ok</span>，到此我们已经成功找到了崩溃的代码行，只不过这种方法还是比较费力，并且限制比较多，我们看看下面的方案。</p> <p>上篇给出的方案一还要补充几句。通过<span>&#8220;crash地址 + MAP文件&#8221;来定位出错代码位置虽然需要经过比较复杂的地址计算，但却是最简单实现的方式。如果仅仅想通过崩溃地址定位出错的函数，就更加方便了。我在网上找到一个解析MAP文件的小工具，可以非常清晰的列出每个函数的地址，并且可以将分析表格导出为Excel文件。工具下载地址：<a href="http://e.ys168.com/?tinyfun"><span>http://e.ys168.com/?tinyfun</span></a></span>，工具目录下VCMapper.exe。</p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 另外上篇主要参考两篇文章：</p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> <a href="http://www.vckbase.com/document/viewdoc/?id=908"><span>http://www.vckbase.com/document/viewdoc/?id=908</span></a></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> <a href="http://www.vckbase.com/document/viewdoc/?id=1473"><span>http://www.vckbase.com/document/viewdoc/?id=1473</span></a></p> <p>&nbsp;<wbr></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> <strong>方案二：崩溃地址<span> + MAP文件 + COD文件</span></strong></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 由于<span>VC8以后的版本都不再支持MAP文件中产生代码行信息，因此我们寻找另一种定位方式：COD文件。</span></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> <strong><span>1</span></strong><strong>、<span>COD文件</span></strong></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> COD文件是一个包含了汇编码、二进制机器码和源代码对应信息的文件，每一个<span>cpp都对应一个COD文件。通过这个文件，我们可以非常方便地进行定位。</span></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 在<span>VC6中生成COD文件的设置方式为：Project Settings -&gt; C/C++，在 Category 中选 Listing Files，在 Listing file type 组合框中选 Assembly，Machine code，and source。在VC8中生成COD文件的设置方式为：Project Properties -&gt; C/C++ -&gt; Output Files -&gt; Assembler Output 项，选择 Assembly，Machine code，and Source(/Facs)。</span></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> <strong><span>2</span></strong><strong>、定位崩溃行</strong></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 下面通过举例进行说明。现在我有一个基于对话框的<span>MFC应用程序CrashTest，在CCrashTestDlg::OnInitDialog函数中写入导致crash的代码语句（第99行），源文件如下：</span></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> <a href="http://photo.blog.sina.com.cn/showpic.html#blogid=48f93b530100fv7y&amp;url=http://s16.sinaimg.cn/orignal/48f93b53g79bbc36cd95f&amp;690" target="_blank"><span><span><img title="clip_image006" alt="clip_image006" src="../../images/cppblog_com/woaidongmao/WindowsLiveWriter/ReleaseCrash_A881/clip_image006_887560ee-8b72-42ea-a623-220dcb47ac1f.jpg" height="169" width="360" border="0" /></span></span></a></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 根据崩溃地址（<span>0x004012A3）以及MAP文件（定位片段图片如下），定位crash函数为OnInitDialog；并且我们可以很容易地计算出崩溃地址相对于崩溃函数的偏移量为 0x004012A3 - 0x004011E0 = 0xC3。</span></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> <a href="http://photo.blog.sina.com.cn/showpic.html#blogid=48f93b530100fv7y&amp;url=http://s11.sinaimg.cn/orignal/48f93b53g79bbc5f052da&amp;690" target="_blank"><span><span><img title="clip_image007" alt="clip_image007" src="../../images/cppblog_com/woaidongmao/WindowsLiveWriter/ReleaseCrash_A881/clip_image007_dc9962ce-f969-4c7f-aa0b-e973cac321ce.jpg" height="36" width="494" border="0" /></span></span></a></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 再来看看<span>CrashTestDlg.cod文件，我们根据文件中源码信息找到OnInitDialog函数信息片段：</span></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> <a href="http://photo.blog.sina.com.cn/showpic.html#blogid=48f93b530100fv7y&amp;url=http://s4.sinaimg.cn/orignal/48f93b53g79bc3c02d3f3&amp;690" target="_blank"><span><span><img title="clip_image008" alt="clip_image008" src="../../images/cppblog_com/woaidongmao/WindowsLiveWriter/ReleaseCrash_A881/clip_image008_00ac91bc-99ca-4d4f-86cd-b0d1d23bb10a.jpg" height="231" width="494" border="0" /></span></span></a></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 可以看到图片中第一行为<span>OnInitDialog函数汇编代码的起始行；找到&#8220;int * p = NULL;&#8221;这一句源码，其前面的98表示这行代码在源文件中的行号，下面的000c1表示相对于函数开始位置的偏移量，后面的&#8220;33 c0&#8221;为机器码，&#8220;xor eax，eax&#8221;为汇编码。那么我们根据前面算出来的偏移量0xC3，找到对应出错的语句为99行：&#8220;*p = 5;&#8221;。</span></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 总结一下定位步骤：</p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 1) 根据公式 <strong><span>崩溃语句在函数中偏移地址<span> = 崩溃地址 - 崩溃函数地址</span></span></strong> 计算出偏移量X；</p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 2) 根据公式 <strong><span>崩溃语句在<span>COD文件中地址 = 崩溃函数在COD文件中地址 + <wbr>X</span></span></strong> 计算出地址Y。其中崩溃函数在COD文件中地址为COD文件中函数起始括号&#8220;{&#8221;后面表明的地址，一般情况下为0x0000；</p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 3) 根据<span>Y在COD文件中找到对应代码行。</span></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ok，方案二介绍完了。这种方法最大的好处是没有<span>VC开发环境版本限制，而且COD文件里面包含的信息更加丰富，不但可以帮助我们定位crash，还能帮我们分析很多东西。当然，这也导致编译生成了很多信息文件。</span></p> <p>根据前面两篇博文，我们要定位崩溃行代码，必须要自己根据相关信息文件进行计算。如果需要处理的量比较大，恐怕会很费力气。有没有更简单快速的办法呢？</p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 最直接的想法就是写一个小工具，根据规则和信息进行自动定位，不过开发起来也是要费一番功夫的。令人开心的是，我们可以找到类似的工具，而且是开源免费的！程序员的世界也许很多时候都是这么单纯而乐于分享！</p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> <strong>方案三：崩溃地址<span> + PDB文件 + CrashFinder</span></strong></p> <p>&nbsp;<wbr><wbr>&nbsp;<wbr><wbr>&nbsp;<wbr><wbr> CrashFinder是一个开源工具，作者是<span>John Robbin，大家可以去他的blog上去找关于CrashFinder的信息。我们这里以CrashFinder2.5版本为例介绍，相关文章链接为：<a href="http://www.wintellect.com/CS/blogs/jrobbins/archive/2006/04/19/crashfinder-returns.aspx"><span>http://www.wintellect.com/CS/blogs/jrobbins/archive/2006/04/19/crashfinder-returns.aspx</span></a></span></p> <p>&nbsp;<wbr><wbr>&nbsp;<wbr><wbr>&nbsp;<wbr> <strong><span>1</span></strong><strong>、<span>PDB文件</span></strong></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> PDB（<span>Program Database）文件中包含了exe程序所有的调试相关信息，具体可以查阅MSDN。当编译选项设置为/Zi，链接选项设置为/DEBUG，/OPT:REF时，就会生成工程的.pdb文件。具体到VC2005中，就是 Project Propertise -&gt; C/C++ -&gt; General -&gt; Debug Information Format 项设置为 Program Database（/Zi），Linker -&gt; Debugging -&gt; Generate Debug Info 项设置为 Yes（/Debug），Linker -&gt; Optimization -&gt; References <wbr>项设置为 Eliminate <wbr>Unreferenced Data（/OPT:REF）。</span></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 只要设置以上选项，<span>release版本也能生成PDB文件。当然，对应的应用程序也会稍大。</span></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> <strong><span>2</span></strong><strong>、<span>CrashFinder</span></strong></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> CrashFinder能够运行需要两个条件：一是系统必须要有<span>dbghelp.dll文件；二是PDB文件必须与exe文件在一个路径下。对于dbghelp.dll，一般在系统system32路径下都有，如果没有下载一个放到这个目录下就可以了。</span></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 先看一下<span>CrashFinder的界面。</span></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr></p> <p align="center"><a href="http://photo.blog.sina.com.cn/showpic.html#blogid=48f93b530100fxvm&amp;url=http://s3.sinaimg.cn/orignal/48f93b53g7a37be0ea632&amp;690" target="_blank"><span><span><img title="clip_image009" alt="clip_image009" src="../../images/cppblog_com/woaidongmao/WindowsLiveWriter/ReleaseCrash_A881/clip_image009_60e666ee-6f31-4132-929c-3ef20c5a9fde.jpg" height="327" width="494" border="0" /></span></span></a></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 用起来也非常简单。首先选择<span>File-&gt;New或点击工具栏新建按钮，选择要调试的exe文件打开，会发现exe及所依赖的dll文件信息都已经加载进来。在下半部分的编辑框中输入崩溃地址（16进制），点右边的&#8220;Find&#8221;按钮，就会在下面显示崩溃的源文件路径、名称以及崩溃所在行号了，如下图所示。</span></p> <p align="center"><a href="http://photo.blog.sina.com.cn/showpic.html#blogid=48f93b530100fxvm&amp;url=http://s10.sinaimg.cn/orignal/48f93b53g7a37da3b3c69&amp;690" target="_blank"><span><span><img title="clip_image010" alt="clip_image010" src="../../images/cppblog_com/woaidongmao/WindowsLiveWriter/ReleaseCrash_A881/clip_image010_4b25ce3c-1bea-4818-be58-230d6cba8a6a.jpg" height="370" width="494" border="0" /></span></span></a></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 用<span>CrashFinder进行crash定位真的非常方便。但是我在使用过程中发现了一个bug，每次启动程序后，直接新建的话加载进来的exe模块都显示叉，提示找不到debug symbols。但是用打开按钮随便打开一个文件失败后，再新建就能成功。猜测可能是直接新建，定位PDB文件时的路径不对引起的。有源码，但是懒的看了呵呵，大家感兴趣可以试一下。</span></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 好了，方案三就介绍到这里，后面还有更加强大的方案<span> : )</span></p> <p>前面几个方案都是直接定位<span>crash的代码位置，但是在比较大型的程序中，只知道这个信息还是远远不够的，我们希望知道更多关于调用函数顺序及变量值等信息，也就是crash时调用堆栈信息。</span></p> <p>&nbsp;<wbr></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> <strong>方案四：<span>SetUnhandledExceptionFil<wbr>ter + StackWalker</span></strong></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 这个方案需要自己动手往工程里添加代码了。要实现上面的想法，需要做两件事情：<span>1、需要在crash时有机会对程序堆栈进行处理；2、对堆栈信息进行收集。</span></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> <strong><span>1</span></strong><strong>、<span>SetUnhandleExceptionFilt<wbr>er函数</span></strong></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> Windows平台下的<span>C++程序异常通常可分为两种：结构化异常（Structured Exception，可以理解为与操作系统相关的异常）和C++异常。对于结构化异常处理（SEH），可以找到很多资料，在此不细说。对于crash错误，一般由未被正常捕获的异常引起，Windows操作系统提供了一个API函数可以在程序crash之前有机会处理这些异常，就是SetUnhandleExceptionFilt<wbr>er函数。（C++也有一个类似函数set_terminate可以处理未被捕获的C++异常。）</span></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> SetUnhandleExceptionFilt<wbr>er函数声明如下：</p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFil<wbr>ter(<br />&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> __in <wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> LPTOP_LEVEL_EXCEPTION_FILTER <em>lpTopLevelExceptionFilte<wbr>r</em><br />&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> );</p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 其中<span> LPTOP_LEVEL_EXCEPTION_FILTER 定义如下：</span></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> typedef LONG (WINAPI *PTOP_LEVEL_EXCEPTION_FILTER)(<br />&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> __in struct _EXCEPTION_POINTERS *ExceptionInfo<br />&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> );<br />&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> typedef PTOP_LEVEL_EXCEPTION_FILTER LPTOP_LEVEL_EXCEPTION_FILTER;</p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 简单来说，<span>SetUnhandleExceptionFilt<wbr>er允许我们设置一个自己的函数作为全局SEH过滤函数，当程序crash前会调用我们的函数进行处理。我们可以利用的是 _EXCEPTION_POINTERS 结构类型的变量ExceptionInfo，它包含了对异常的描述以及发生异常的线程状态，过滤函数可以通过返回不同的值来让系统继续运行或退出应用程序。</span></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 关于<span> SetUnhandleExceptionFilt<wbr>er 函数的具体用法和示例请参考MSDN。</span></p> <p>&nbsp;<wbr></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> <strong><span>2</span></strong><strong>、<span>StackWalker</span></strong><br />&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 现在我们已经有机会可以在<span>crash之前对程序状态信息进行处理了，只需要生成并保存堆栈信息就大功告成了。Windows的dbghelp.dll库提供了一个函数可以得到当前堆栈信息：StackWalk64（在Win2K以前版本中为StackWalk）。该函数声明如下：</span></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> BOOL WINAPI StackWalk64(<br />&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> __in <wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> DWORD <em>MachineType</em>,<br />&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> __in <wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> HANDLE <em>hProcess</em>,<br />&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> __in <wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> HANDLE <em>hThread</em>,<br />&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> __in_out <wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> LPSTACKFRAME64 <em>StackFrame</em>,<br />&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> __in_out <wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> PVOID <em>ContextRecord</em>,<br />&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> __in <wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> PREAD_PROCESS_MEMORY_ROUTINE64 <em>ReadMemoryRoutine</em>,<br />&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> __in <wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> PFUNCTION_TABLE_ACCESS_ROUTINE64 <em>FunctionTableAccessRouti<wbr>ne</em>,<br />&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> __in <wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> PGET_MODULE_BASE_ROUTINE64 <em>GetModuleBaseRoutine</em>,<br />&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> __in <wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> PTRANSLATE_ADDRESS_ROUTINE64 <em>TranslateAddress</em><br />&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> );<br />&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 该函数的具体用法可以参考<span>MSDN。在这里推荐一个牛人写好的StackWalker，可以直接拿来用，开源的。StackWalker提供了一个基类，给出了几个简单的接口，可以方便地生成堆栈信息，并且支持一系列VC版本，非常好用。我们可以自己写一个子类，并重载虚函数OnOutput，就可以将堆栈信息输出为特定格式了。StackWalker的地址为：<a href="http://www.codeproject.com/KB/threads/StackWalker.aspx"><span>http://www.codeproject.com/KB/threads/StackWalker.aspx</span></a></span>。</p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 不过对于<span>Release版本来说，StackWalk64函数获得的堆栈信息有可能不完整。如果异常是由MFC的模块抛出，那么获得的堆栈可能缺少前面调用模块信息。另外，StackWalk64需要最新的dbghelp.dll文件支持才能工作；要正确输出crash的函数名和行号，需要要pdb文件支持。以上不足有可能影响输出信息的完整性和效果，而对于发布在外的程序，要带上pdb文件几乎不可能，因此这个方案还是有缺憾的，比较适用于本地的release版本调试。</span></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 下一篇我们将介绍一个更加完善的解决方案</p> <p>当我们把自己的<span>release版本程序发布出去以后，一般都是在用户的机器上运行。这种情况下，对于第四种方案，因为需要pdb文件才能够正确生成堆栈调用的函数行号及代码行号，因此方案四只适用于本地release版的调试，否则只能生成不完整的堆栈信息。对于前三种方案，其实只需要用户告知崩溃地址，然后在本地查找crash地址就可以了，但是定位crash的过程非常不方便，如果crash的情况比较多，前三种方案都不合适。而且，前三种方案均不能生成堆栈调用信息，对于debug的作用有限。</span></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 下面我们就来看一个更加完善的解决方案。</p> <p>&nbsp;<wbr></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> <strong>方案五：<span>SetUnhandledExceptionFil<wbr><wbr>ter + Minidump</span></strong></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> SetUnhandleExceptionFilt<wbr>er函数我们已经介绍过了，本方案的思路还是要利用我们自己的异常处理函数，来生成<span>minidump文件。</span></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> <strong><span>1</span></strong><strong>、<span>Minidump概念</span></strong></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> minidump（小存储器转储）可以理解为一个<span>dump文件，里面记录了能够帮助调试crash的最小有用信息。实际上，如果你在 系统属性 -&gt; 高级 -&gt; 启动和故障恢复 -&gt; 设置 -&gt; 写入调试信息 中选择&#8220;小内存转储(64 KB)&#8221;的话，当系统意外停止时都会在C:\Windows\Minidump\路径下生成一个.dmp后缀的文件，这个文件就是minidump文件，只不过这个是内核态的minidump。</span></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>我们要生成的是用户态的<span>minidump，文件中包含了程序运行的模块信息、线程信息、堆栈调用信息等。而且为了符合其mini的特性，dump文件是压缩过的。</span></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> <strong><span>2</span></strong><strong>、生成<span>minidump文件</span></strong></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 生成<span>minidump文件的API函数是MiniDumpWriteDump，该函数需要dbghelp.lib支持，其原型如下:</span></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> BOOL WINAPI MiniDumpWriteDump(<br />&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> __in <wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> HANDLE <em>hProcess</em>,<br />&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> __in <wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> DWORD <em>ProcessId</em>,<br />&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> __in <wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> HANDLE <em>hFile</em>,<br />&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> __in <wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> MINIDUMP_TYPE <em>DumpType</em>,<br />&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> __in <wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> PMINIDUMP_EXCEPTION_INFORMATION <em>ExceptionParam</em>,<br />&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> __in <wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> PMINIDUMP_USER_STREAM_INFORMATION <em>UserStreamParam</em>,<br />&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> __in <wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> PMINIDUMP_CALLBACK_INFORMATION <em>CallbackParam</em><br />&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> );</p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 在我们的异常处理函数中加入以下代码：</p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> HANDLE hFile = ::CreateFile( _T("E:\\dumpfile.dmp"), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);<br />&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> if( hFile != INVALID_HANDLE_VALUE)<br />&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> {<br />&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> MINIDUMP_EXCEPTION_INFORMATION einfo;<br />&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> einfo.ThreadId = ::GetCurrentThreadId();<br />&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> einfo.ExceptionPointers = pExInfo;<br />&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> einfo.ClientPointers = FALSE;</p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ::MiniDumpWriteDump(::GetCurrentProcess(), ::GetCurrentProcessId(), hFile, MiniDumpNormal, &amp;einfo, NULL, NULL);<br />&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ::CloseHandle(hFile);<br />&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> }</p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 其中，<span>pExInfo变量为异常处理函数PEXCEPTION_POINTERS类型的参数。具体请参考MSDN。</span></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> <strong><span>3</span></strong><strong>、调试<span>minidump</span></strong></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>调试<span>dump文件首先需要pdb文件，因此我们build程序时需要设置 Debug Infomation Format 为 &#8220;Program Database（/Zi）&#8221;。其次，我们还要确保所用的dump文件与源代码、exe、pdb文件版本是一致的，这要求我们必须维护好程序版本信息。</span></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 调试<span>minidump最方便的环境就是VS了，我们只要将.dmp、.exe、.pdb文件放在一个路径下，保证源代码文件的路径与编译时的路径一致就可以了，剩下的就是VS帮我们完成。双击.dmp文件或者在文件打开工程中选择&#8220;dump files&#8221;，加载dump文件，然后按F5运行就能直接恢复crash时的现场了，你可以定位crash的代码，可以查看调用堆栈，可以查看线程和模块信息...一切都跟你设置断点调试一样，太强大了！看个截图吧。</span></p> <p><a href="http://photo.blog.sina.com.cn/showpic.html#blogid=48f93b530100g282&amp;url=http://s16.sinaimg.cn/orignal/48f93b53g7b0e4eff189f&amp;690" target="_blank"><span><span><img title="clip_image012" alt="clip_image012" src="../../images/cppblog_com/woaidongmao/WindowsLiveWriter/ReleaseCrash_A881/clip_image012_3df8e4ef-d0e8-49d3-a9af-47f58cc713c6.jpg" height="469" width="616" border="0" /></span></span></a></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 需要注意的是，对于<span>release版的程序来说，很多代码是经过编译器优化过的，因此定位的时候可能会有所偏差，大家可以考虑设置选项去掉代码优化。</span></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 其他可以调试<span>minidump的工具还有WinDbg等，大家可以查阅相关资料。</span></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 本文主要参考了这篇文章：<span><a href="http://vicchina.51.net/research/other/seh/minidumps/intro.htm"><span>http://vicchina.51.net/research/other/seh/minidumps/intro.htm</span></a></span>。</p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 下一篇，我们将给出一个调试<span>release发布程序的完美解决方案，适合用户量较大的应用发布程序的调试。</span></p> <p>上一篇我们已经给出了方案，能够非常方便的通过<span>dump文件对crash错误进行调试和定位；从整个流程上看还差最后一步，即怎样拿到crash时产生的dump文件。如果可以让用户把文件发送过来自然不错，但对于类似免费共享软件等在互联网上发布的程序呢？我们的用户是不确定的，而且用户量有可能非常大，即使我们能想办法联系到用户，总不能挨个去收集crash信息吧。</span></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 我们需要一种方案，能够提供<span>crash信息汇报功能。</span></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 我们可以架设一台服务器专门进行信息收集，只要客户端在<span>crash时正确汇报即可，但是相应的维护成本和开发难度也不可忽视。有没有更简单的方法呢？还记得我的博文&#8220;<a href="http://blog.sina.com.cn/s/blog_48f93b530100esmg.html" target="_blank"><span><span>为程序添加自动发送Email</span><span>功能</span></span></a>&#8221;</span>吗？这就是简单有效的方法！</p> <p>&nbsp;<wbr></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> <strong>方案六：<span>minidump + email</span></strong></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 我们只需要在异常处理时，先生成<span>minidump信息文件，再用email方式将文件发送到指定邮箱就行了。剩下的就是我们每天查看邮箱，提取dump文件进行调试了。</span></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> <strong><span>1</span></strong><strong>、<span>Email功能</span></strong></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 首先我们来看一下<span>email发送都需要哪些相关信息。</span></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> a、发送端邮箱帐户；</p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> b、接收端邮箱帐户；</p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> c、<span>email标题，一般应有软件名称及版本信息；</span></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> d、<span>email正文，一般应有简单的crash信息提示，以区别不同原因造成的crash；</span></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>e、<span>email附件，当然就是我们的dump文件了，还可以加上软件生成的log文件等。</span></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 当然，对于标题应该尽量多加一些信息区别引起<span>crash的原因，比如将crash的地址信息加到标题中；因为当每天有成百上千的crash汇报上来，重复的crash占大多数，把时间都花在区分它们身上有点太浪费。由此看来，前面方案中提到的StackWalker还是有些用处的，我们可以用它来生成一些crash的文字描述信息，写到标题或正文中去。</span></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> dump文件的大小是否适合作为邮件的附件呢？实际上<span>minidump产生的文件一般在几K到几十K之间，作为email的附件没有任何问题。</span></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 关于发送<span>email相关技术细节，已经在&#8220;<a href="http://blog.sina.com.cn/s/blog_48f93b530100esmg.html" target="_blank"><span><span>为程序添加自动发送Email</span><span>功能</span></span></a>&#8221;</span>文中介绍了，大家可以参考。其实，对接受邮箱中邮件的处理还是很费时费力的，大家可以考虑写一些脚本将处理流程自动化，提高效率。</p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> <strong><span>2</span></strong><strong>、<span>google breakpad</span></strong></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> google breakpad是一个开源的跨平台<span>crash report系统，光从开源和跨平台这两个特点上来看，它就足以称的上是一个完善而有效的工具了。其实，breakpad在整个crash report层次上给出了一个系统级的解决方案，也就是说它几乎能适应各种软件、各种平台的应用要求。</span></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> breakpad的整体思路跟上面介绍的方案是相似的，只不过最后提交<span>dump文件的方式更加完善。大家有兴趣可以去它的官方网址查阅相关资料：<a href="http://code.google.com/p/google-breakpad/"><span>http://code.google.com/p/google-breakpad/</span></a></span>。</p> <p>&nbsp;<wbr></p> <p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ok，关于调试<span>release发布程序的crash错误系列文章就写完了。这几篇文章给出的方案由简单到复杂，由简陋到完善，对crash调试有了一个比较全面的总结。当然，其中涉及到的概念和技术还很多，需要我们去不断学习和领悟，也希望大家能够互相交流。</span></p> </div>  		</div></div><img src ="http://www.cppblog.com/lauer3912/aggbug/159781.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lauer3912/" target="_blank">RTY</a> 2011-11-07 21:15 <a href="http://www.cppblog.com/lauer3912/archive/2011/11/07/159781.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>程序员的共鸣 - 读《卓有成效的程序员》</title><link>http://www.cppblog.com/lauer3912/archive/2011/09/15/155812.html</link><dc:creator>RTY</dc:creator><author>RTY</author><pubDate>Wed, 14 Sep 2011 23:36:00 GMT</pubDate><guid>http://www.cppblog.com/lauer3912/archive/2011/09/15/155812.html</guid><wfw:comment>http://www.cppblog.com/lauer3912/comments/155812.html</wfw:comment><comments>http://www.cppblog.com/lauer3912/archive/2011/09/15/155812.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lauer3912/comments/commentRss/155812.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lauer3912/services/trackbacks/155812.html</trackback:ping><description><![CDATA[<span class="Apple-style-span" style="color: #333333; font-family: 微软雅黑, verdana, Arial, sans-serif; background-color: #ffffff; "><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">最近读了《卓有成效的程序员》，感觉收获颇大。这是一本写给程序员的难得的好书。书中大都是一些浅显的道理，但作者将这些东西加以收集、归纳、总结，并最终成书。作者为了收集各种提高效率的工具和方法，东奔西走，可谓费了一番苦心。</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">我觉得此书第一部分总结的一些法则非常好，我提取了一下：<br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><h3 style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-weight: bold; color: #000000; ">法则：&nbsp;<br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></h3><h4 style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-weight: bold; color: #000000; ">1.加速法则</h4><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp; 关注本质，而非形式</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp; 一个应用程序列表的有用程度与它的长度成反比&nbsp;<br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp;&nbsp;<span style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; line-height: 1.5; color: red; ">程序员的很多时间都浪费在找东西上</span>&nbsp;<br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp; 华而不实的东西中看不中用</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp; 键盘输入总比导航快</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp;&nbsp;<span style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; line-height: 1.5; color: red; ">首选键盘而非鼠标</span></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp; 地址栏是Windows资源管理器界面中最高效的部分</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp;&nbsp;<span style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; line-height: 1.5; color: red; ">花点时间来学习你手边的所有隐藏的快捷键</span></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp; 环境切换会消耗时间</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp;&nbsp;<span style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; line-height: 1.5; color: red; ">成批复制粘贴要比反复多次复制粘贴快</span></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp; 忘记历史就意味着你得再输入一遍</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp; 嵌入图形化工具的命令提示符让你鱼与熊掌兼得</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp; 在上下文中学习IDE快捷键，而不要去背长长的列表</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp; 当你第二次输入一个复杂结构时，将它做成模板</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp; 如果要对多行文本做同样的操作，就应该找出其中的模式，并把它记录为一个宏</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp;&nbsp;<span style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; line-height: 1.5; color: red; ">不要总是重复输入相同的命令</span></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp; 每天花一点点时间来使每一天都更高效&nbsp;<br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><h4 style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-weight: bold; color: #000000; ">2.专注法则</h4><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp;&nbsp;<span style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; line-height: 1.5; color: red; ">精力越集中，思维越缜密</span></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp; 排除干扰：隔离策略，关掉不需要的提示，创造安静时间&nbsp;&nbsp;<br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp;&nbsp;<span style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; line-height: 1.5; color: red; ">草堆越大，从中找到一根针就越难</span></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp; 不要问文件树，要搜索</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp; 使用多显示器</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp; 虚拟桌面可以让原本杂乱无章的一大堆窗口变得整洁&nbsp;<br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><h4 style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-weight: bold; color: #000000; ">3.自动化法则</h4><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp; 不要重新发明轮子</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp; 用Selenium浏览网页</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp; 不要浪费时间动手去做可以被自动化的事情</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp; 用Windows Power Shell替代批处理文件</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp; 驯服Subversion命令行</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp; 以创造性的方式解决问题，有助于在将来解决类似的问题</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp;&nbsp;<span style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; line-height: 1.5; color: red; ">是否应该自动化的关键在于投资回报率和缓解风险</span></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp; 研究性的工作应该放在时间盒里做</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp; 别给牦牛剪毛&nbsp;<br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><h4 style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-weight: bold; color: #000000; ">4.规范性法则<br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></h4><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp;&nbsp;&nbsp; 对于任何你不自己去构建的东西，只在版本控制中保存一份副本<br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp; 使用标准的构建服务器</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp;&nbsp;<span style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; line-height: 1.5; color: red; ">通过复制粘贴来复用是邪恶的，不论你复制粘贴的是什么</span></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp; 利用虚拟平台使项目依赖标准化</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp; 不要让对象 - 关系映射工具（O/R映射器）违反规范原则&nbsp;<br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp; 通过扩展。开放类（open class），或者部分类（partial class） 来为生成的代码增加行为<br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp; 始终保持代码和数据结构的同步</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp;&nbsp;<span style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; line-height: 1.5; color: red; ">过时的文档比没有文档更糟，因为它会主动误导你</span></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp; 任何需要费劲创造的东西，都让它的创造者欲罢不能</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp;&nbsp;<span style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; line-height: 1.5; color: red; ">白板 + 数码相机强过任何CASE工具</span></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp; 尽量生成所有技术文档</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp;&nbsp;<span style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; line-height: 1.5; color: red; ">重复是软件开发中最大的阻力&nbsp;</span><br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><h3 style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-weight: bold; color: #000000; ">工具：<br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></h3><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">书中，还提到了大量的提高效率的工具，都是非常不错的。相信很多人都有自己的一个列表，下面是我电脑中必不可少的几款软件：</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp; 1. FireFox 及其各类插件<br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp; 2. Launchy启动加速器</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp; 3. Total Commander</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp; 4. ClipX多重剪切板</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp; 5. EmEditor文本编辑器&nbsp;<br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp; 6. Vistual Studio的VA插件</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp; 7. Search And Replace</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp; 8. Everything</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp; 9. Miranda IM<br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&nbsp; &nbsp; 10. ....&nbsp;<br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><h3 style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-weight: bold; color: #000000; ">感触：&nbsp;<br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></h3><h4 style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-weight: bold; color: #000000; ">1. 愤怒的猴子&nbsp;<br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></h4><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">在书中的第二部分，提到了很多实践相关的内容。让我感触最深的是&#8220;愤怒的猴子&#8221;的故事：</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&#8220;<em style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">早在20世纪60年代（那时候科学家们可以做任何疯狂的事情），行为科学家们进行了一项实验。他们把五只猴子和一架活梯放在一间屋子里，并在天花板上挂了一串香蕉。这些猴子很快就想到它们可以爬上梯子去吃香蕉，但每当它们靠近活梯的时候，科学家们就用冰水浸满整个屋子。我想你能猜到会发生什么：一群愤怒的猴子。很快，再没有一只猴子会去靠近那个梯子了。<br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></em></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; "><em style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">之后，科学家们将其中一只猴子替换成另一只没有忍受过冰水折磨的新猴子。这只新猴子所做的第一件事就是直奔那架梯子，但当它这么做时其他所有猴子都痛打它。它不明白为什么，但很快就学乖了：不要去靠近那架梯子。科学家们逐渐将最初的那些猴子都替换成新猴子，直到这群猴子中谁都没有被水浸泡过，然而它们还是会去攻击任何靠近梯子的猴子。</em></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; "><em style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">这说明了什么？软件项目中许多惯例之所以存在，就因为&#8221;我们一直是那样做的&#8220;。换句话说，是因为愤怒的猴子。</em>&#8221;</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">我们小组在制定C++相关的代码规范时就遇到过无数类似的问题。比如，在制定变量的命名规范时，我们针对是否采用匈牙利命名法争论了很久。有的人认为， 几乎以前看到的所有C++代码都采用了匈牙利命名法，甚至，微软定义的所有API都使用了此类命名法。刚开始，我也是有同样的疑惑。<br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">后来，我们经过仔细分析C++匈牙利命名法由来，渐渐感觉我们就是那些愤怒的猴子，盲目跟从前人的方式，缺乏打破传统的勇气。C++有着其特殊的历史原因，很多标准一直沉淀下来并很少改变。我们再看看后来新生的那些编程语言，C#, Python&#8230;&#8230; 都抛弃了匈牙利命名法，同时再看看现在C++前沿的C++ 0x以及现在出版的一些书中，也渐渐放弃了对匈牙利命名法的使用。因为类型的意义在对象模型中越来越弱化。因此，最后我们放弃了匈牙利命名法这个老古董。&nbsp;<br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><h4 style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-weight: bold; color: #000000; ">2. 敏捷开发<br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></h4><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">这本书带有强烈的ThoughtWorks色彩，敏捷的思想贯穿全书，包括测试驱动设计，白板，结对编程。这也让我对敏捷产生了更加强烈的兴趣。 其中有一段测试驱动开发TDD的一段故事：<br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">&#8220;<em style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">记得第一次和一些已经习惯于单元测试的开发人员一起动手开始修改代码时，我也是非常紧张，因为大量的修改往往会破坏很多东西，但他们看起来丝毫没有犹豫。逐渐地，我也放下心来，因为我慢慢地认识到：有了测试的保证，完全可以放心大胆地去修改代码。</em>&#8221;&nbsp;<br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><h4 style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-weight: bold; color: #000000; ">3. 有趣的故事&nbsp;<br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></h4><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">书中还有一些有趣的故事，比如作者的一个朋友在和别人结对编程时，为了养成同伴使用快捷键的习惯，每当同伴未使用快捷键时，他都会要求将操作撤销，然后要求使用快捷键再重复操作3次。然后，在其凶狠的眼神中，同伴很快掌握了快捷键。&nbsp;<br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><h3 style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-weight: bold; color: #000000; ">总结：</h3><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">这本书很薄，蕴藏的道理却不少，相信每个读过它的人都会从中收获。读过之后，我们不应该局限于书中提到的某些小技巧， 或是书中某一个细节，毕竟，提供效率的方法有很多很多，法则也有很多很多，一本书很难将其穷举完。我们应该从书中吸取其思想，并在实际工作和学习中不断总结，做一个真正的&#8220;卓有成效的程序员&#8221;！</p></span><img src ="http://www.cppblog.com/lauer3912/aggbug/155812.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lauer3912/" target="_blank">RTY</a> 2011-09-15 07:36 <a href="http://www.cppblog.com/lauer3912/archive/2011/09/15/155812.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Parallel Programming Essentials via the Intel TBB</title><link>http://www.cppblog.com/lauer3912/archive/2011/09/15/155810.html</link><dc:creator>RTY</dc:creator><author>RTY</author><pubDate>Wed, 14 Sep 2011 23:05:00 GMT</pubDate><guid>http://www.cppblog.com/lauer3912/archive/2011/09/15/155810.html</guid><wfw:comment>http://www.cppblog.com/lauer3912/comments/155810.html</wfw:comment><comments>http://www.cppblog.com/lauer3912/archive/2011/09/15/155810.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lauer3912/comments/commentRss/155810.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lauer3912/services/trackbacks/155810.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: http://www.codeproject.com/KB/Parallel_Programming/Threading_Blocks.aspxIntroductionThe Intel&nbsp;TBB&nbsp;was released by Intel during their movement to enhance system performance by cores, rather t...&nbsp;&nbsp;<a href='http://www.cppblog.com/lauer3912/archive/2011/09/15/155810.html'>阅读全文</a><img src ="http://www.cppblog.com/lauer3912/aggbug/155810.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lauer3912/" target="_blank">RTY</a> 2011-09-15 07:05 <a href="http://www.cppblog.com/lauer3912/archive/2011/09/15/155810.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Google Test 架构</title><link>http://www.cppblog.com/lauer3912/archive/2011/09/15/155807.html</link><dc:creator>RTY</dc:creator><author>RTY</author><pubDate>Wed, 14 Sep 2011 22:40:00 GMT</pubDate><guid>http://www.cppblog.com/lauer3912/archive/2011/09/15/155807.html</guid><wfw:comment>http://www.cppblog.com/lauer3912/comments/155807.html</wfw:comment><comments>http://www.cppblog.com/lauer3912/archive/2011/09/15/155807.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lauer3912/comments/commentRss/155807.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lauer3912/services/trackbacks/155807.html</trackback:ping><description><![CDATA[网址：<a href="http://code.google.com/p/googletest/">http://code.google.com/p/googletest/<br /><br />引文</a>：<a href="http://www.cnblogs.com/coderzh/archive/2009/04/06/1426755.html">http://www.cnblogs.com/coderzh/archive/2009/04/06/1426755.html<br /><span class="Apple-style-span" style="color: #333333; font-family: 微软雅黑, verdana, Arial, sans-serif; font-size: 13px; line-height: 24px; -webkit-text-decorations-in-effect: none; background-color: #ffffff; "><h1 class="postTitle" style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-weight: bold; color: #000000; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: #dddddd; font-size: 14px; "><a id="ctl03_TitleUrl" class="postTitle2" href="http://www.cnblogs.com/coderzh/archive/2009/04/06/1426755.html" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; text-decoration: none; color: #399ab2; ">玩转Google开源C++单元测试框架Google Test系列(gtest)(总)</a></h1><div class="clear" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; clear: both; "></div><div class="postBody" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-size: 14px; line-height: 1.5; "><div id="cnblogs_post_body" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; "><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">前段时间学习和了解了下Google的开源C++单元测试框架Google Test，简称gtest，非常的不错。 我们原来使用的是自己实现的一套单元测试框架，在使用过程中，发现越来越多使用不便之处，而这样不便之处，gtest恰恰很好的解决了。<br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">其实gtest本身的实现并不复杂，我们完全可以模仿gtest，不断的完善我们的测试框架， 但最后我们还是决定使用gtest取代掉原来的自己的测试框架，原因是：<br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">1.不断完善我们的测试框架之后就会发觉相当于把gtest重新做了一遍，虽然轮子造的很爽，但是不是必要的。</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">2.使用gtest可以免去维护测试框架的麻烦，让我们有更多精力投入到案例设计上。<br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">3.gtest提高了非常完善的功能，并且简单易用，极大的提高了编写测试案例的效率。</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">gtest的官方网站是：</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; "><a target="_blank" href="http://code.google.com/p/googletest/" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; text-decoration: none; color: #0066aa; border-bottom-width: 0px; border-bottom-style: initial; border-bottom-color: initial; ">http://code.google.com/p/googletest/</a><br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">从官方的使用文档里，你几乎可以获得你想要的所有东西&nbsp;<br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; "><a target="_blank" href="http://code.google.com/p/googletest/wiki/GoogleTestPrimer" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; text-decoration: none; color: #0066aa; border-bottom-width: 0px; border-bottom-style: initial; border-bottom-color: initial; ">http://code.google.com/p/googletest/wiki/GoogleTestPrimer</a><br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /><a target="_blank" href="http://code.google.com/p/googletest/wiki/GoogleTestPrimer" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; text-decoration: none; color: #0066aa; border-bottom-width: 0px; border-bottom-style: initial; border-bottom-color: initial; "></a></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; "><a target="_blank" href="http://code.google.com/p/googletest/wiki/GoogleTestAdvancedGuide" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; text-decoration: none; color: #0066aa; border-bottom-width: 0px; border-bottom-style: initial; border-bottom-color: initial; ">http://code.google.com/p/googletest/wiki/GoogleTestAdvancedGuide</a>&nbsp;</p><a target="_blank" href="http://code.google.com/p/googletest/wiki/GoogleTestAdvancedGuide" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; text-decoration: none; color: #0066aa; border-bottom-width: 0px; border-bottom-style: initial; border-bottom-color: initial; "></a><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; "></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">如果还想对gtest内部探个究竟，就把它的代码下载下来研究吧，这就是开源的好处，哈！&nbsp;<br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">官方已经有如此完备的文档了，为什么我还要写呢？一方面是自己记记笔记，好记性不如烂笔头，以后自己想查查一些用法也可以直接在这里查到，一方面是对于不想去看一大堆英文文档的朋友，在我这里可以快速的找到gtest相关的内容。</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">下面是该系列的目录：<br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; "><a target="_blank" href="http://www.cnblogs.com/coderzh/archive/2009/03/31/1426758.html" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; text-decoration: none; color: #0066aa; border-bottom-width: 0px; border-bottom-style: initial; border-bottom-color: initial; ">1.玩转Google开源C++单元测试框架Google Test系列(gtest)之一 - 初识gtest</a><br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; "><a target="_blank" href="http://www.cnblogs.com/coderzh/archive/2009/04/06/1430364.html" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; text-decoration: none; color: #0066aa; border-bottom-width: 0px; border-bottom-style: initial; border-bottom-color: initial; ">2.玩转Google开源C++单元测试框架Google Test系列(gtest)之二 - 断言</a><br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; "><a target="_blank" href="http://www.cnblogs.com/coderzh/archive/2009/04/06/1430396.html" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; text-decoration: none; color: #0066aa; border-bottom-width: 0px; border-bottom-style: initial; border-bottom-color: initial; ">3.玩转Google开源C++单元测试框架Google Test系列(gtest)之三 - 事件机制</a></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; "><a target="_blank" href="http://www.cnblogs.com/coderzh/archive/2009/04/08/1431297.html" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; text-decoration: none; color: #0066aa; border-bottom-width: 0px; border-bottom-style: initial; border-bottom-color: initial; ">4.玩转Google开源C++单元测试框架Google Test系列(gtest)之四 - 参数化</a></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; "><a target="_blank" href="http://www.cnblogs.com/coderzh/archive/2009/04/08/1432043.html" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; text-decoration: none; color: #0066aa; border-bottom-width: 0px; border-bottom-style: initial; border-bottom-color: initial; ">5.玩转Google开源C++单元测试框架Google Test系列(gtest)之五 - 死亡测试</a>&nbsp;<br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; "><a target="_blank" href="http://www.cnblogs.com/coderzh/archive/2009/04/10/1432789.html" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; text-decoration: none; color: #0066aa; border-bottom-width: 0px; border-bottom-style: initial; border-bottom-color: initial; ">6.玩转Google开源C++单元测试框架Google Test系列(gtest)之六 - 运行参数</a></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; "><a target="_blank" href="http://www.cnblogs.com/coderzh/archive/2009/04/11/1433744.html" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; text-decoration: none; color: #0066aa; border-bottom-width: 0px; border-bottom-style: initial; border-bottom-color: initial; ">7.玩转Google开源C++单元测试框架Google Test系列(gtest)之七 - 深入解析gtest</a><br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; "><a target="_blank" href="http://www.cnblogs.com/coderzh/archive/2009/04/12/1434155.html" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; text-decoration: none; color: #0066aa; border-bottom-width: 0px; border-bottom-style: initial; border-bottom-color: initial; ">8.玩转Google开源C++单元测试框架Google Test系列(gtest)之八 - 打造自己的单元测试框架</a></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; "><br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">额外篇：</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; "><a target="_blank" href="http://www.cnblogs.com/coderzh/archive/2009/08/02/1536901.html" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; text-decoration: none; color: #0066aa; border-bottom-width: 0px; border-bottom-style: initial; border-bottom-color: initial; ">1.gtest中如何跳出当前测试案例</a></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; "><a target="_blank" href="http://www.cnblogs.com/coderzh/archive/2010/01/09/beautiful-testcase.html" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; text-decoration: none; color: #0066aa; border-bottom-width: 0px; border-bottom-style: initial; border-bottom-color: initial; ">2.编写优美的GTest测试案例</a></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; "><a target="_blank" id="homepage1_HomePageDays_DaysList_ctl00_DayItem_DayList_ctl00_TitleUrl" href="http://www.cnblogs.com/coderzh/archive/2010/03/19/gtest_demo.html" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; text-decoration: none; color: #0066aa; border-bottom-width: 0px; border-bottom-style: initial; border-bottom-color: initial; ">3.gtest 参数化测试代码示例</a>&nbsp;(内含完整工程示例)</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; "></p></div><div id="MySignature" style="margin-top: 10px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; "><div id="MySignature" style="margin-top: 10px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; "><p id="LyjSignature" style="padding-top: 10px; padding-right: 10px; padding-bottom: 10px; padding-left: 60px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-style: dashed; border-right-style: dashed; border-bottom-style: dashed; border-left-style: dashed; border-top-color: #e0e0e0; border-right-color: #e0e0e0; border-bottom-color: #e0e0e0; border-left-color: #e0e0e0; background-color: #fffeee; background-image: url(http://www.cnblogs.com/images/cnblogs_com/lyj/o_info.png); background-attachment: scroll; font-family: 微软雅黑; background-position: 1% 50%; background-repeat: no-repeat no-repeat; ">作者：<a target="_blank" href="http://coderzh.cnblogs.com/" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; text-decoration: none; color: #0066aa; border-bottom-width: 0px; border-bottom-style: initial; border-bottom-color: initial; ">CoderZh</a>（<a target="_blank" href="http://coderzh.cnblogs.com/" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; text-decoration: none; color: #0066aa; border-bottom-width: 0px; border-bottom-style: initial; border-bottom-color: initial; ">CoderZh的技术博客 - 博客园</a>）<br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " />微博：<a target="_blank" href="http://t.sina.com.cn/coderzh" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; text-decoration: none; color: #0066aa; border-bottom-width: 0px; border-bottom-style: initial; border-bottom-color: initial; ">http://t.sina.com.cn/coderzh</a>&nbsp;<br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " />出处：<a target="_blank" href="http://coderzh.cnblogs.com/" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; text-decoration: none; color: #0066aa; border-bottom-width: 0px; border-bottom-style: initial; border-bottom-color: initial; ">http://coderzh.cnblogs.com</a><br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " />文章版权归本人所有，欢迎转载，但未经作者同意必须保留此段声明，且在文章页面明显位置给出原文连接，否则保留追究法律责任的权利。</p></div></div></div></span><br /></a><img src ="http://www.cppblog.com/lauer3912/aggbug/155807.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lauer3912/" target="_blank">RTY</a> 2011-09-15 06:40 <a href="http://www.cppblog.com/lauer3912/archive/2011/09/15/155807.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Const，Const函数，Const变量，函数后面的Const</title><link>http://www.cppblog.com/lauer3912/archive/2011/07/26/151840.html</link><dc:creator>RTY</dc:creator><author>RTY</author><pubDate>Mon, 25 Jul 2011 23:14:00 GMT</pubDate><guid>http://www.cppblog.com/lauer3912/archive/2011/07/26/151840.html</guid><wfw:comment>http://www.cppblog.com/lauer3912/comments/151840.html</wfw:comment><comments>http://www.cppblog.com/lauer3912/archive/2011/07/26/151840.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lauer3912/comments/commentRss/151840.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lauer3912/services/trackbacks/151840.html</trackback:ping><description><![CDATA[<span class="Apple-style-span" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff; "><p style="margin-top: 0mm; margin-right: 0mm; margin-bottom: 0pt; margin-left: 0mm; text-indent: 21pt; ">看到const 关键字，C++程序员首先想到的可能是const 常量。这可不是良好的条件反射。如果只知道用const 定义常量，那么相当于把火药仅用于制作鞭炮。const 更大的魅力是它可以修饰函数的参数、返回值，甚至函数的定义体。</p><p style="margin-top: 0mm; margin-right: 0mm; margin-bottom: 0pt; margin-left: 0mm; text-indent: 21pt; ">const 是constant 的缩写，&#8220;恒定不变&#8221;的意思。被const 修饰的东西都受到强制保护，可以预防意外的变动，能提高程序的健壮性。所以很多C++程序设计书籍建议：&#8220;Use const whenever you need&#8221;。</p><p style="margin-top: 0mm; margin-right: 0mm; margin-bottom: 0pt; margin-left: 0mm; text-indent: 21pt; ">1.用const 修饰函数的参数</p><p style="margin-top: 0mm; margin-right: 0mm; margin-bottom: 0pt; margin-left: 0mm; text-indent: 21pt; ">如果参数作输出用，不论它是什么数据类型，也不论它采用&#8220;指针传递&#8221;还是&#8220;引用传递&#8221;，都不能加const 修饰，否则该参数将失去输出功能。const 只能修饰输入参数：</p><p style="margin-top: 0mm; margin-right: 0mm; margin-bottom: 0pt; margin-left: 0mm; text-indent: 21pt; "><span style="text-decoration: underline; "><strong>如果输入参数采用&#8220;指针传递&#8221;，那么加const 修饰可以防止意外地改动该指针，起到保护作用。</strong></span></p><p style="margin-top: 0mm; margin-right: 0mm; margin-bottom: 0pt; margin-left: 0mm; text-indent: 21pt; ">例如StringCopy 函数：</p><p style="margin-top: 0mm; margin-right: 0mm; margin-bottom: 0pt; margin-left: 0mm; text-indent: 21pt; ">void StringCopy(char *strDestination, const char *strSource);</p><p style="margin-top: 0mm; margin-right: 0mm; margin-bottom: 0pt; margin-left: 0mm; text-indent: 21pt; ">其中strSource 是输入参数，strDestination 是输出参数。给strSource 加上const修饰后，<strong><span style="text-decoration: underline; ">如果函数体内的语句试图改动</span><span style="text-decoration: underline; ">strSource 的内容，编译器将指出错误。</span></strong></p><p style="margin-top: 0mm; margin-right: 0mm; margin-bottom: 0pt; margin-left: 0mm; text-indent: 21pt; ">如果输入参数采用&#8220;值传递&#8221;，由于函数将自动产生临时变量用于复制该参数，该输入参数本来就无需保护，所以不要加const 修饰。</p><p style="margin-top: 0mm; margin-right: 0mm; margin-bottom: 0pt; margin-left: 0mm; text-indent: 21pt; ">例如不要将函数void Func1(int x) 写成void Func1(const int x)。同理不要将函数void Func2(A a) 写成void Func2(const A a)。其中A 为用户自定义的数据类型。</p><p style="margin-top: 0mm; margin-right: 0mm; margin-bottom: 0pt; margin-left: 0mm; text-indent: 21pt; ">对于非内部数据类型的参数而言，象void Func(A a) 这样声明的函数注定效率比较底。因为函数体内将产生A 类型的临时对象用于复制参数a，而临时对象的构造、复制、析构过程都将消耗时间。</p><p style="margin-top: 0mm; margin-right: 0mm; margin-bottom: 0pt; margin-left: 0mm; text-indent: 21pt; "><span style="text-decoration: underline; "><strong>为了提高效率，可以将函数声明改为void Func(A &amp;a)，因为&#8220;引用传递&#8221;仅借用一下参数的别名而已，不需要产生临时对象。但是函数void Func(A &amp;a) 存在一个缺点：</strong></span></p><p style="margin-top: 0mm; margin-right: 0mm; margin-bottom: 0pt; margin-left: 0mm; text-indent: 21pt; ">&#8220;引用传递&#8221;有可能改变参数a，这是我们不期望的。解决这个问题很容易，加const修饰即可，因此函数最终成为void Func(const A &amp;a)。</p><p style="margin-top: 0mm; margin-right: 0mm; margin-bottom: 0pt; margin-left: 0mm; text-indent: 21pt; ">以此类推，是否应将void Func(int x) 改写为void Func(const int &amp;x)，以便提高效率？完全没有必要，因为内部数据类型的参数不存在构造、析构的过程，而复制也非常快，&#8220;值传递&#8221;和&#8220;引用传递&#8221;的效率几乎相当。</p><p style="margin-top: 0mm; margin-right: 0mm; margin-bottom: 0pt; margin-left: 0mm; text-indent: 21pt; ">问题是如此的缠绵，我只好将&#8220;const &amp;&#8221;修饰输入参数的用法总结一下。</p><p style="margin-top: 0mm; margin-right: 0mm; margin-bottom: 0pt; margin-left: 0mm; ">&nbsp;</p><p style="margin-top: 0mm; margin-right: 0mm; margin-bottom: 0pt; margin-left: 0mm; text-indent: 21pt; ">对于非内部数据类型的输入参数，应该将&#8220;值传递&#8221;的方式改为&#8220;const 引用传递&#8221;，目的是提高效率。例如将void Func(A a) 改为void Func(const A &amp;a)。</p><p style="margin-top: 0mm; margin-right: 0mm; margin-bottom: 0pt; margin-left: 0mm; ">&nbsp;</p><p style="margin-top: 0mm; margin-right: 0mm; margin-bottom: 0pt; margin-left: 0mm; text-indent: 21pt; ">对于内部数据类型的输入参数，不要将&#8220;值传递&#8221;的方式改为&#8220;const 引用传递&#8221;。否则既达不到提高效率的目的，又降低了函数的可理解性。例如void Func(int x) 不应该改为void Func(const int &amp;x)。</p><p style="margin-top: 0mm; margin-right: 0mm; margin-bottom: 0pt; margin-left: 0mm; text-indent: 21pt; ">2 用const 修饰函数的返回值<br />如果给以&#8220;指针传递&#8221;方式的函数返回值加const 修饰，那么函数返回值（即指针）的内容不能被修改，<strong><span style="text-decoration: underline; ">该返回值只能被赋给加const 修饰的同类型指针。例如函数<br /></span></strong>const char * GetString(void);<br />如下语句将出现编译错误：<br />char *str = GetString();<br />正确的用法是<br />const char *str = GetString();<br />如果函数返回值采用&#8220;值传递方式&#8221;，由于函数会把返回值复制到外部临时的存储单元中，加const 修饰没有任何价值。<br />例如不要把函数int GetInt(void) 写成const int GetInt(void)。<br />同理不要把函数A GetA(void) 写成const A GetA(void)，其中A 为用户自定义的数据类型。<br />如果返回值不是内部数据类型，将函数A GetA(void) 改写为const A &amp; GetA(void)的确能提高效率。但此时千万千万要小心，一定要搞清楚函数究竟是想返回一个对象的&#8220;拷贝&#8221;还是仅返回&#8220;别名&#8221;就可以了，否则程序会出错。<br />函数返回值采用&#8220;引用传递&#8221;的场合并不多，这种方式一般只出现在类的赋值函数中，目的是为了实现链式表达。</p><p style="margin-top: 0mm; margin-right: 0mm; margin-bottom: 0pt; margin-left: 0mm; text-indent: 21pt; ">例如：<br />class A<br />{<br />A &amp; operate = (const A &amp;other); // 赋值函数<br />};<br />A a, b, c; // a, b, c 为A 的对象<br /><br />a = b = c; // 正常的链式赋值<br />(a = b) = c; // 不正常的链式赋值，但合法<br />如果将赋值函数的返回值加const 修饰，那么该返回值的内容不允许被改动。上例中，语句 a = b = c 仍然正确，但是语句 (a = b) = c 则是非法的。<br />3 const 成员函数<br />任何不会修改数据成员的函数都应该声明为const 类型。如果在编写const 成员函数时，不慎修改了数据成员，或者调用了其它非const 成员函数，编译器将指出错误，这无疑会提高程序的健壮性。以下程序中，类stack 的成员函数GetCount 仅用于计数，从逻辑上讲GetCount 应当为const 函数。编译器将指出GetCount 函数中的错误。<br />class Stack<br />{<br />public:<br />void Push(int elem);<br />int Pop(void);<br />int GetCount(void) const; // const 成员函数<br />private:<br />int m_num;<br />int m_data[100];<br />};<br />int Stack::GetCount(void) const<br />{<br />++ m_num; // 编译错误，企图修改数据成员m_num<br />Pop(); // 编译错误，企图调用非const 函数<br />return m_num;<br />}<br />const 成员函数的声明看起来怪怪的：const 关键字只能放在函数声明的尾部，大概是因为其它地方都已经被占用了。<br />关于Const函数的几点规则：<br /><br />a. const对象只能访问const成员函数,而非const对象可以访问任意的成员函数,包括const成员函数.<br />b. const对象的成员是不可修改的,然而const对象通过指针维护的对象却是可以修改的.<br />c. const成员函数不可以修改对象的数据,不管对象是否具有const性质.它在编译时,以是否修改成员数据为依据,进行检查.<br />e. 然而加上mutable修饰符的数据成员,对于任何情况下通过任何手段都可修改,自然此时的const成员函数是可以修改它的</p></span><img src ="http://www.cppblog.com/lauer3912/aggbug/151840.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lauer3912/" target="_blank">RTY</a> 2011-07-26 07:14 <a href="http://www.cppblog.com/lauer3912/archive/2011/07/26/151840.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>const 基本用法说明，请注意！</title><link>http://www.cppblog.com/lauer3912/archive/2011/07/26/151839.html</link><dc:creator>RTY</dc:creator><author>RTY</author><pubDate>Mon, 25 Jul 2011 23:13:00 GMT</pubDate><guid>http://www.cppblog.com/lauer3912/archive/2011/07/26/151839.html</guid><wfw:comment>http://www.cppblog.com/lauer3912/comments/151839.html</wfw:comment><comments>http://www.cppblog.com/lauer3912/archive/2011/07/26/151839.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lauer3912/comments/commentRss/151839.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lauer3912/services/trackbacks/151839.html</trackback:ping><description><![CDATA[<span class="Apple-style-span" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff; ">const类型定义：指明变量或对象的值是不能被更新,引入目的是为了取代预编译指令&nbsp;<br /><br />**************常量必须被初始化*************************<br /><br />cons的作用<br />&nbsp;&nbsp;&nbsp;（1）可以定义const常量&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;例如：<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;int&nbsp;Max=100;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;Array[Max];&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;（2）便于进行类型检查&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;例如：<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;void&nbsp;f(const&nbsp;int&nbsp;i)&nbsp;{&nbsp;.........}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;编译器就会知道i是一个常量，不允许修改；<br />&nbsp;&nbsp;&nbsp;（3）可以保护被修饰的东西，防止意外的修改，增强程序的健壮性。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;还是上面的例子，如果在函数体内修改了i，编译器就会报错；<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;例如：&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;void&nbsp;f(const&nbsp;int&nbsp;i)&nbsp;{&nbsp;i=10;//error!&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;(5)&nbsp;为函数重载提供了一个参考。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class&nbsp;A<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;......<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;void&nbsp;f(int&nbsp;i)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{......}&nbsp;file://一个函数<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;void&nbsp;f(int&nbsp;i)&nbsp;const&nbsp;{......}&nbsp;file://上一个函数的重载<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;......<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;};<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(6)&nbsp;可以节省空间，避免不必要的内存分配。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;例如：<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#define&nbsp;PI&nbsp;3.14159&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;file://常量宏<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;doulbe&nbsp;&nbsp;Pi=3.14159;&nbsp;&nbsp;file://此时并未将Pi放入ROM中<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;......<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;double&nbsp;i=Pi;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;file://此时为Pi分配内存，以后不再分配！<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;double&nbsp;I=PI;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;file://编译期间进行宏替换，分配内存<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;double&nbsp;j=Pi;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;file://没有内存分配<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;double&nbsp;J=PI;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;file://再进行宏替换，又一次分配内存！<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const定义常量从汇编的角度来看，只是给出了对应的内存地址，而不是象#define一样给出的是立即数，所以，const定义的常量在程序运行过程中只有一份拷贝，而#define定义的常量在内存中有若干个拷贝。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;（7）&nbsp;提高了效率。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;编译器通常不为普通const常量分配存储空间，而是将它们保存在符号表中，这使得它成为一个编译期间的常量，没有了存储与读内存的操作，使得它的效率也很高。<br /><br />使用const<br />&nbsp;&nbsp;&nbsp;（1）修饰一般常量,常数组，常对象<br />　　&nbsp;修饰符const可以用在类型说明符前，也可以用在类型说明符后。&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;例如：&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;const&nbsp;x=2;　　或　　const&nbsp;int&nbsp;x=2;<br /><br />　　&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;const&nbsp;a[5]={1,&nbsp;2,&nbsp;3,&nbsp;4,&nbsp;5};&nbsp;&nbsp;&nbsp;&nbsp;或&nbsp;&nbsp;const&nbsp;int&nbsp;a[5]={1,&nbsp;2,&nbsp;3,&nbsp;4,&nbsp;5};<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class&nbsp;A;　&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;A&nbsp;a;&nbsp;&nbsp;或&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;A&nbsp;const&nbsp;a;<br />　　&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;（2）修饰指针<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;int&nbsp;*A;&nbsp;&nbsp;&nbsp;或&nbsp;&nbsp;int&nbsp;const&nbsp;*A;&nbsp;//const修饰指向的对象，A可变，A指向的对象不可变<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;*const&nbsp;A;&nbsp;　&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//const修饰指针A，&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;A不可变，A指向的对象可变&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;int&nbsp;*const&nbsp;A;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//指针A和A指向的对象都不可变<br />&nbsp;&nbsp;&nbsp;（3）修饰引用<br />&nbsp;　　&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;double&nbsp;&amp;&nbsp;v;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;该引用所引用的对象不能被更新<br />　（4）修饰函数的返回值：<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const修饰符也可以修饰函数的返回值，是返回值不可被改变，格式如下：<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;int&nbsp;Fun1();&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;MyClass&nbsp;Fun2();<br />&nbsp;&nbsp;&nbsp;（5）修饰类的成员函数：<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const修饰符也可以修饰类的成员函数，格式如下：<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class&nbsp;ClassName&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;　&nbsp;&nbsp;&nbsp;&nbsp;　int&nbsp;Fun()&nbsp;const;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;　&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.....<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}；<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;这样，在调用函数Fun时就不能修改类里面的数据&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;（6）在另一连接文件中引用const常量<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;extern&nbsp;const&nbsp;int&nbsp;i;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//正确的引用<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;extern&nbsp;const&nbsp;int&nbsp;j=10;&nbsp;&nbsp;//错误！常量不可以被再次赋值<br />&nbsp;&nbsp;&nbsp;<br /><br /><br />*******************放在类内部的常量有什么限制？<br />&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class&nbsp;A<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;private:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;int&nbsp;c3&nbsp;=&nbsp;7;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;err<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;static&nbsp;int&nbsp;c4&nbsp;=&nbsp;7;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;err<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;static&nbsp;const&nbsp;float&nbsp;c5&nbsp;=&nbsp;7;&nbsp;&nbsp;//&nbsp;err<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;......<br />&nbsp;&nbsp;};<br />&nbsp;初始化类内部的常量<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1&nbsp;初始化列表：<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class&nbsp;A<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;A(int&nbsp;i=0):test(i)&nbsp;{}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;private:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;int&nbsp;i;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}；<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;外部初始化，例如：<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class&nbsp;A<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;A()&nbsp;{}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;private:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;static&nbsp;const&nbsp;int&nbsp;i;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}；<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;int&nbsp;A::i=3;&nbsp;</span><img src ="http://www.cppblog.com/lauer3912/aggbug/151839.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lauer3912/" target="_blank">RTY</a> 2011-07-26 07:13 <a href="http://www.cppblog.com/lauer3912/archive/2011/07/26/151839.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>