﻿<?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/15567.html</link><description>没有理由不学习</description><language>zh-cn</language><lastBuildDate>Sat, 19 Jul 2014 10:37:31 GMT</lastBuildDate><pubDate>Sat, 19 Jul 2014 10:37:31 GMT</pubDate><ttl>60</ttl><item><title>Java Gossip: 國際化訊息</title><link>http://www.cppblog.com/lauer3912/archive/2013/03/08/198288.html</link><dc:creator>RTY</dc:creator><author>RTY</author><pubDate>Fri, 08 Mar 2013 03:03:00 GMT</pubDate><guid>http://www.cppblog.com/lauer3912/archive/2013/03/08/198288.html</guid><wfw:comment>http://www.cppblog.com/lauer3912/comments/198288.html</wfw:comment><comments>http://www.cppblog.com/lauer3912/archive/2013/03/08/198288.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lauer3912/comments/commentRss/198288.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lauer3912/services/trackbacks/198288.html</trackback:ping><description><![CDATA[<h1><a href="http://caterpillar.onlyfun.net/Gossip/JavaGossip-V2/JavaGossip2.htm" style="color: #444444; text-decoration: none;"><font color="#444444">Java Gossip:&nbsp;國際化訊息</font><br /><br /></a><a href="http://caterpillar.onlyfun.net/Gossip/JavaGossip-V2/JavaGossip2.htm"><small style="color: #000000; font-family: serif; font-weight: normal; line-height: normal; background-color: #ffffff;">國際化的英文是<span style="font-weight: bold;">Internationalization</span>，因為單字中總共有18個字母，簡稱<span style="font-weight: bold;">I18N</span>，目的是讓應用程式可以應地區不同而顯示不同的訊息，最基本的就是讓不同語系的使用者可以看到屬於自己語系的訊息，像是英文語系的看到英文內容，而中文語系的可以看到中文的內容。<br /><br />為了在應用程式中表示一個區域，Java提供有<span style="font-weight: bold;">java.util.Locale</span>類，一個Locale實例包括了語系資訊與區域資訊，例如說"en"表示英文語系的國家，這個字母組合是在&nbsp;<a href="http://ftp.ics.uci.edu/pub/ietf/http/related/iso639.txt" style="color: #444444; font-weight: bold; text-decoration: none;">ISO 639</a>&nbsp;中定義的，而區域資訊則是像"US"表示美國，這個字母組合則是在&nbsp;<a href="http://www.chemie.fu-berlin.de/diverse/doc/ISO_3166.html" style="color: #444444; font-weight: bold; text-decoration: none;">ISO 3166</a>&nbsp;中定義的。<br /><br />您可以這麼新增一個Locale的實例：<br /></small><div style="color: #000000; font-family: serif; font-size: medium; font-weight: normal; line-height: normal; background-color: #ffffff; margin-left: 40px;"><small><span style="font-weight: bold; font-family: 'Courier New', Courier, monospace;">Locale locale = new Locale("zh", "TW");</span><br /></small></div><small style="color: #000000; font-family: serif; font-weight: normal; line-height: normal; background-color: #ffffff;">&nbsp;<br />如何將Locale用於訊息綁定呢？當您使用ResourceBundle.getBundle()方法時，預設就會自動取得電腦上的語系與區域訊息，而事實上訊息檔案的名稱由basename加上語系與地區來組成，例如：<br /></small><ul style="margin-top: 3px; margin-bottom: 3px; color: #000000; font-family: serif; font-size: medium; font-weight: normal; line-height: normal; background-color: #ffffff;"><li><small>basename.properties</small></li><li><small>basename_en.properties</small></li><li><small>basename_zh_TW.properties</small></li></ul><small style="color: #000000; font-family: serif; font-weight: normal; line-height: normal; background-color: #ffffff;"><br />沒有指定語言與地區的basename是預設的資源檔名稱，當沒有提供專用的語系、區域訊息檔案時，就會找尋預設的資源檔案。<br /><br />如果您想要提供繁體中文的訊息，由於訊息資源檔必須是ISO-8859-1編碼，所以對於非西方語系的處理，必須先將之轉換為Java Unicode Escape格式，例如您可以先在訊息資源檔中寫下以下的內容：<br /></small><ul style="margin-top: 3px; margin-bottom: 3px; color: #000000; font-family: serif; font-size: medium; font-weight: normal; line-height: normal; background-color: #ffffff;"><li>messages_zh_TW.txt</li></ul><pre style="color: #333333; border: 1px solid #777777; background-color: #eeeeee; margin: 3px 25px; padding: 0px 10px; font-size: small; font-weight: normal; line-height: 17px; width: auto; font-family: 'Courier New', Courier, monospace;">onlyfun.caterpillar.welcome=哈囉<br />onlyfun.caterpillar.name=世界 <br /></pre><br style="color: #000000; font-family: serif; font-size: medium; font-weight: normal; line-height: normal; background-color: #ffffff;" /><small style="color: #000000; font-family: serif; font-weight: normal; line-height: normal; background-color: #ffffff;">然後使用JDK的工具程式<span style="font-weight: bold;">native2ascii</span>來轉換，例如：<br /></small><div style="color: #000000; font-family: serif; font-size: medium; font-weight: normal; line-height: normal; background-color: #ffffff; margin-left: 40px;"><small><em><strong>native2ascii -encoding Big5 messages_zh_TW.txt messages_zh_TW.properties</strong></em><br /></small></div><small style="color: #000000; font-family: serif; font-weight: normal; line-height: normal; background-color: #ffffff;"><br />轉換後的內容會如下：</small><br style="color: #000000; font-family: serif; font-size: medium; font-weight: normal; line-height: normal; background-color: #ffffff;" /><ul style="margin-top: 3px; margin-bottom: 3px; color: #000000; font-family: serif; font-size: medium; font-weight: normal; line-height: normal; background-color: #ffffff;"><li>messages_zh_TW.properties</li></ul><pre style="color: #333333; border: 1px solid #777777; background-color: #eeeeee; margin: 3px 25px; padding: 0px 10px; font-size: small; font-weight: normal; line-height: 17px; width: auto; font-family: 'Courier New', Courier, monospace;">onlyfun.caterpillar.welcome=\u54c8\u56c9<br />onlyfun.caterpillar.name=\u4e16\u754c <br /></pre><p style="color: #000000; font-family: serif; font-size: medium; font-weight: normal; line-height: normal; background-color: #ffffff;"></p><small style="color: #000000; font-family: serif; font-weight: normal; line-height: normal; background-color: #ffffff;">將這個檔案放於classpath可以存取的到的位置，您也可以提供預設的訊息檔案：</small><br style="color: #000000; font-family: serif; font-size: medium; font-weight: normal; line-height: normal; background-color: #ffffff;" /><ul style="margin-top: 3px; margin-bottom: 3px; color: #000000; font-family: serif; font-size: medium; font-weight: normal; line-height: normal; background-color: #ffffff;"><li>messages.properties</li></ul><pre style="color: #333333; border: 1px solid #777777; background-color: #eeeeee; margin: 3px 25px; padding: 0px 10px; font-size: small; font-weight: normal; line-height: 17px; width: auto; font-family: 'Courier New', Courier, monospace;">onlyfun.caterpillar.welcome=Hello<br />onlyfun.caterpillar.name=World <br /></pre><br style="color: #000000; font-family: serif; font-size: medium; font-weight: normal; line-height: normal; background-color: #ffffff;" /><small style="color: #000000; font-family: serif; font-weight: normal; line-height: normal; background-color: #ffffff;">來測試一下訊息檔案，我所使用的作業系統是語系設定是中文，區域設定是台灣，當我使用下面的程式時：</small><br style="color: #000000; font-family: serif; font-size: medium; font-weight: normal; line-height: normal; background-color: #ffffff;" /><ul style="margin-top: 3px; margin-bottom: 3px; color: #000000; font-family: serif; font-size: medium; font-weight: normal; line-height: normal; background-color: #ffffff;"><li>ResourceBundleDemo.java</li></ul><pre style="color: #333333; border: 1px solid #777777; background-color: #eeeeee; margin: 3px 25px; padding: 0px 10px; font-size: small; font-weight: normal; line-height: 17px; width: auto; font-family: 'Courier New', Courier, monospace;">package onlyfun.caterpillar;<br />&nbsp;<br />import java.util.ResourceBundle;<br /><br />public class ResourceBundleDemo {<br />    public static void main(String[] args) {<br />        ResourceBundle resource = <br />                  ResourceBundle.getBundle("messages");<br />        <br />        System.out.print(resource.getString(<br />                      "onlyfun.caterpillar.welcome") + "!");<br />        System.out.println(resource.getString(<br />                      "onlyfun.caterpillar.name") + "!");<br />    }<br />} <br /></pre><br style="color: #000000; font-family: serif; font-size: medium; font-weight: normal; line-height: normal; background-color: #ffffff;" /><small style="color: #000000; font-family: serif; font-weight: normal; line-height: normal; background-color: #ffffff;">會使用預設的語系<span style="font-weight: bold;">"zh"</span>與區域設定<span style="font-weight: bold;">"TW"</span>，所以就會找尋messages_<span style="font-weight: bold;">zh_TW</span>.properties的內容，所以會顯示以下的訊息：</small><table border="0" cellpadding="2" cellspacing="2" style="font-family: serif; background-color: #ffffff; width: 690px;"><tbody><tr><td style="background-color: #000000;"><small><span style="color: #ffffff;">哈囉!世界!</span></small><span style="color: #ffffff;"><br /></span></td></tr></tbody></table><br style="color: #000000; font-family: serif; font-size: medium; font-weight: normal; line-height: normal; background-color: #ffffff;" /><small style="color: #000000; font-family: serif; font-weight: normal; line-height: normal; background-color: #ffffff;">在使用ResourceBundle.getBundle()時可以給定Locale實例作為參數，例如若您想提供 messages_en_US.properties，並想要ResourceBundle.getBundle()取得這個檔案的內容，則可以如下撰寫：<br /></small><div style="color: #000000; font-family: serif; font-size: medium; font-weight: normal; line-height: normal; background-color: #ffffff; margin-left: 40px;"><small><span style="font-weight: bold; font-family: 'Courier New', Courier, monospace;">Locale locale = new Locale("en", "US");</span><br style="font-weight: bold; font-family: 'Courier New', Courier, monospace;" /><span style="font-weight: bold; font-family: 'Courier New', Courier, monospace;">ResourceBundle resource =&nbsp;</span><br style="font-weight: bold; font-family: 'Courier New', Courier, monospace;" /><span style="font-weight: bold; font-family: 'Courier New', Courier, monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ResourceBundle.getBundle("messages", locale);</span><br /></small></div><small style="color: #000000; font-family: serif; font-weight: normal; line-height: normal; background-color: #ffffff;">&nbsp;<br />則取得的訊息會是messages_en_US.properties的內容。</small></a></h1><img src ="http://www.cppblog.com/lauer3912/aggbug/198288.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> 2013-03-08 11:03 <a href="http://www.cppblog.com/lauer3912/archive/2013/03/08/198288.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Object-C学习笔记 基础部分(一)NSString常用方法</title><link>http://www.cppblog.com/lauer3912/archive/2012/05/29/176653.html</link><dc:creator>RTY</dc:creator><author>RTY</author><pubDate>Tue, 29 May 2012 13:59:00 GMT</pubDate><guid>http://www.cppblog.com/lauer3912/archive/2012/05/29/176653.html</guid><wfw:comment>http://www.cppblog.com/lauer3912/comments/176653.html</wfw:comment><comments>http://www.cppblog.com/lauer3912/archive/2012/05/29/176653.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lauer3912/comments/commentRss/176653.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lauer3912/services/trackbacks/176653.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: http://keys.iteye.com/blog/1117190个人学习笔记&nbsp;NSString&nbsp;--实例化方法--------------&nbsp;NSString *str = [[NSString alloc] init];&nbsp;NSString *str = [[[NSString alloc] init] autorelease];&nbsp;&nbsp;&...&nbsp;&nbsp;<a href='http://www.cppblog.com/lauer3912/archive/2012/05/29/176653.html'>阅读全文</a><img src ="http://www.cppblog.com/lauer3912/aggbug/176653.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-05-29 21:59 <a href="http://www.cppblog.com/lauer3912/archive/2012/05/29/176653.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Cocoa读取和写入plist文件</title><link>http://www.cppblog.com/lauer3912/archive/2012/05/27/176331.html</link><dc:creator>RTY</dc:creator><author>RTY</author><pubDate>Sun, 27 May 2012 01:09:00 GMT</pubDate><guid>http://www.cppblog.com/lauer3912/archive/2012/05/27/176331.html</guid><wfw:comment>http://www.cppblog.com/lauer3912/comments/176331.html</wfw:comment><comments>http://www.cppblog.com/lauer3912/archive/2012/05/27/176331.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lauer3912/comments/commentRss/176331.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lauer3912/services/trackbacks/176331.html</trackback:ping><description><![CDATA[<div>http://www.cocoachina.com/b/?p=237<br /><p style="font-size: 12px; line-height: 17px; color: #35383d; font-family: Myriad, 'Lucida Grande', Geneva, Verdana, Arial, Helvetica, sans-serif; background-color: #ffffff; ">plist文件是标准的xml文件，在cocoa中可以很简单地使用。这里介绍一下使用方法：</p><p style="font-size: 12px; line-height: 17px; color: #35383d; font-family: Myriad, 'Lucida Grande', Geneva, Verdana, Arial, Helvetica, sans-serif; background-color: #ffffff; ">以下代码在Mac和iPhone中均适用。</p><p style="font-size: 12px; line-height: 17px; color: #35383d; font-family: Myriad, 'Lucida Grande', Geneva, Verdana, Arial, Helvetica, sans-serif; background-color: #ffffff; "></p><p style="font-size: 12px; line-height: 17px; color: #35383d; font-family: Myriad, 'Lucida Grande', Geneva, Verdana, Arial, Helvetica, sans-serif; background-color: #ffffff; ">写入plist文件：</p><div no=""  objc"="" style="background-color: #ffffff; overflow: auto; white-space: nowrap; margin-bottom: 1em; position: relative; font-family: Helvetica, monospace; font-size: 12px; color: #35383d; line-height: 17px; "><ol style="margin: 0px; padding: 0px 0px 0.5em 1em; border-left-style: none; list-style-type: none; "><li style="padding: 2px 0px 2px 15px; list-style-type: none; background-image: url(http://www.cocoachina.com/b/wp-content/themes/illacrimo/images/PostContentLiIco.png); line-height: 15px; color: #4c4c4c; background-position: 0% 0%; background-repeat: no-repeat no-repeat; "><div style="background-image: none; color: #000000; padding-left: 5px; "><span style="color: #0000ff; ">NSMutableDictionary</span>*&nbsp;dict&nbsp;=&nbsp;<span style="color: #005000; ">[</span>&nbsp;<span style="color: #005000; ">[</span>&nbsp;<span style="color: #0000ff; ">NSMutableDictionary</span>&nbsp;<span style="color: #0000ff; ">alloc</span>&nbsp;<span style="color: #005000; ">]</span>&nbsp;<span style="color: #0000ff; ">initWithContentsOfFile</span>:@<span style="color: #7f2d00; ">"/Sample.plist"</span>&nbsp;<span style="color: #005000; ">]</span>;</div></li><li style="padding: 2px 0px 2px 15px; list-style-type: none; background-image: url(http://www.cocoachina.com/b/wp-content/themes/illacrimo/images/PostContentLiIco.png); line-height: 15px; color: #4c4c4c; background-position: 0% 0%; background-repeat: no-repeat no-repeat; "><div style="background-image: none; color: #000000; padding-left: 5px; "><span style="color: #005000; ">[</span>&nbsp;dict&nbsp;<span style="color: #0000ff; ">setObject</span>:@<span style="color: #7f2d00; ">"Yes"</span>&nbsp;<span style="color: #0000ff; ">forKey</span>:@<span style="color: #7f2d00; ">"RestartSpringBoard"</span>&nbsp;<span style="color: #005000; ">]</span>;</div></li><li style="padding: 2px 0px 2px 15px; list-style-type: none; background-image: url(http://www.cocoachina.com/b/wp-content/themes/illacrimo/images/PostContentLiIco.png); line-height: 15px; color: #4c4c4c; background-position: 0% 0%; background-repeat: no-repeat no-repeat; "><div style="background-image: none; color: #000000; padding-left: 5px; "><span style="color: #005000; ">[</span>&nbsp;dict&nbsp;<span style="color: #0000ff; ">writeToFile</span>:@<span style="color: #7f2d00; ">"/Sample.plist"</span>&nbsp;<span style="color: #0000ff; ">atomically</span>:<span style="color: #0000ff; ">YES</span>&nbsp;<span style="color: #005000; ">]</span>;</div></li></ol></div><p style="font-size: 12px; line-height: 17px; color: #35383d; font-family: Myriad, 'Lucida Grande', Geneva, Verdana, Arial, Helvetica, sans-serif; background-color: #ffffff; ">&nbsp;</p><p style="font-size: 12px; line-height: 17px; color: #35383d; font-family: Myriad, 'Lucida Grande', Geneva, Verdana, Arial, Helvetica, sans-serif; background-color: #ffffff; ">读取plist文件：</p><div no=""  objc"="" style="background-color: #ffffff; overflow: auto; white-space: nowrap; margin-bottom: 1em; position: relative; font-family: Helvetica, monospace; font-size: 12px; color: #35383d; line-height: 17px; "><ol style="margin: 0px; padding: 0px 0px 0.5em 1em; border-left-style: none; list-style-type: none; "><li style="padding: 2px 0px 2px 15px; list-style-type: none; background-image: url(http://www.cocoachina.com/b/wp-content/themes/illacrimo/images/PostContentLiIco.png); line-height: 15px; color: #4c4c4c; background-position: 0% 0%; background-repeat: no-repeat no-repeat; "><div style="background-image: none; color: #000000; padding-left: 5px; "><span style="color: #0000ff; ">NSMutableDictionary</span>*&nbsp;dict&nbsp;=&nbsp;&nbsp;<span style="color: #005000; ">[</span>&nbsp;<span style="color: #005000; ">[</span>&nbsp;<span style="color: #0000ff; ">NSMutableDictionary</span>&nbsp;<span style="color: #0000ff; ">alloc</span>&nbsp;<span style="color: #005000; ">]</span>&nbsp;<span style="color: #0000ff; ">initWithContentsOfFile</span>:@<span style="color: #7f2d00; ">"/Sample.plist"</span>&nbsp;<span style="color: #005000; ">]</span>;</div></li><li style="padding: 2px 0px 2px 15px; list-style-type: none; background-image: url(http://www.cocoachina.com/b/wp-content/themes/illacrimo/images/PostContentLiIco.png); line-height: 15px; color: #4c4c4c; background-position: 0% 0%; background-repeat: no-repeat no-repeat; "><div style="background-image: none; color: #000000; padding-left: 5px; "><span style="color: #0000ff; ">NSString</span>*&nbsp;<span style="color: #0000ff; ">object</span>&nbsp;=&nbsp;<span style="color: #005000; ">[</span>&nbsp;dict&nbsp;<span style="color: #0000ff; ">objectForKey</span>:@<span style="color: #7f2d00; ">"RestartSpringBoard"</span>&nbsp;<span style="color: #005000; ">]</span>;</div></li></ol></div><p style="font-size: 12px; line-height: 17px; color: #35383d; font-family: Myriad, 'Lucida Grande', Geneva, Verdana, Arial, Helvetica, sans-serif; background-color: #ffffff; ">&nbsp;</p></div><img src ="http://www.cppblog.com/lauer3912/aggbug/176331.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-05-27 09:09 <a href="http://www.cppblog.com/lauer3912/archive/2012/05/27/176331.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>OSGi开发起步</title><link>http://www.cppblog.com/lauer3912/archive/2012/05/15/174990.html</link><dc:creator>RTY</dc:creator><author>RTY</author><pubDate>Tue, 15 May 2012 09:07:00 GMT</pubDate><guid>http://www.cppblog.com/lauer3912/archive/2012/05/15/174990.html</guid><wfw:comment>http://www.cppblog.com/lauer3912/comments/174990.html</wfw:comment><comments>http://www.cppblog.com/lauer3912/archive/2012/05/15/174990.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lauer3912/comments/commentRss/174990.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lauer3912/services/trackbacks/174990.html</trackback:ping><description><![CDATA[<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 1em; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: arial, FreeSans, Helvetica, sans-serif; line-height: 20px; background-color: #ffffff; ">OSGi开发起步(Getting Started with OSGi)-1第一个OSGi模块<br />原文出自：&nbsp;<a href="http://neilbartlett.name/blog/osgi-articles/" style="color: #3c78b5; cursor: pointer; text-decoration: none; ">http://neilbartlett.name/blog/osgi-articles/</a>&nbsp;- Getting Started with OSGi<br /><div>http://hi.baidu.com/tinawhisper/home</div></p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 1em; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: arial, FreeSans, Helvetica, sans-serif; line-height: 20px; background-color: #ffffff; ">本文系翻译文章，但并未严格按照原文翻译。</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 1em; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: arial, FreeSans, Helvetica, sans-serif; line-height: 20px; background-color: #ffffff; ">1. 第一个OSGi模块<br />本文期望展示OSGi开发环境的简洁性，因此这里没有使用eclipse开发环境。本文只使用文本编辑器和基本的命令行工具进行OSGi程序开发。<br />本文的第一个例子相对其他示例有些长，这是因为我们需要建立一个基本的工作环境。在开始之前，我们需要一个可以运行的OSGi框架。现在我们可以在三种开源框架中进行选择：Apache Felix，Knopflerfish和Equinox。本文所有编写的代码对以上三个框架来说都是可用的，只是在使得这些代码运行的指令上有所区别。本文选择eclipse使用的Equinox。首先需要找到eclipse安装目录下的org.eclipse.osgi_3.2.1.R32x_v20060919.jar并复制此文件到一个空目录（文件中的版本号会根据安装eclipse的不同而不同）。为了使命令行参数短一些，把刚才复制过来的jar文件更名为equinox.jar。现在就可以在命令行中定位到刚才那个空目录并执行这个命令：<br />&gt; java -jar equinox.jar -console<br />几秒钟后，命令窗口中会出现osgi&gt;提示。如果是这样，就说明OSGi已经运行起来了（意外总会发生，比如你没有安装java！）。<br />osgi&gt;命令行提示允许我们运行Equinox中的命令来进行整个框架的控制。可以在提示符后输入help获得所有命令的列表。经常使用的一个命令是ss。ss表示简要状态（short status）的意思。ss会列出当前已经安装到框架中所有模块（bundle）和它们状态的列表。在OSGi中bundle表示一个模块，这与eclipse中的插件（plug-ins）具有等同的概念。<br />输入ss后，Equinox输出为：<br />Framework is launched.<br />id State Bundle<br />0 ACTIVE system.bundle_3.2.1.R32x_v20060919<br />以上输出告诉我们当前只安装了一个已经启动的模块：系统模块（System bundle）。此模块是OSGi的一个总是安装并处于活动状态的特殊模块，它代表了框架本身。<br />现在我们开始编写一个模块。在相同的目录下，创建一个名称为HelloActivator.java的文件并复制以下代码到文件中：<br />import org.osgi.framework.*;<br />public class HelloActivator implements BundleActivator {<br />public void start(BundleContext context) { System.out.println("Hello Eclipse!"); }<br />public void stop(BundleContext context) { System.out.println("Goodbye Eclipse!"); }<br />}<br />一个模块还需要一个元文件（manifest file）对它的各种元信息进行声明：如模块的名称，版本等信息。所以现在还需要创建一个名称为HelloWorld.mf的文件并复制下文的文本到此文件中。特别需要注意的是要在此文本文件的最后保留一个空白行，否则打包命令jar在打包时会截短文件（最后一行丢失）。这样就会使得生产的jar包中的元文件缺失一行，导致jar文件读取和运行失败。<br />Manifest-Version: 1.0<br />Bundle-Name: HelloWorld<br />Bundle-Activator: HelloActivator<br />Bundle-SymbolicName: HelloWorld<br />Bundle-Version: 1.0.0<br />Import-Package: org.osgi.framework<br />现在另外打开一个命令行窗口使用如下命令构建一个jar文件：<br />&gt; javac -classpath equinox.jar HelloActivator.java<br />&gt; jar -cfm HelloWorld.jar HelloWorld.mf HelloActivator.class<br />回到原先的OSGi控制台，输入install&nbsp;<a href="file://helloworld.jar/" style="color: #3c78b5; cursor: pointer; text-decoration: none; ">file:HelloWorld.jar</a>。控制台会输出"Bundle id is 1" ，接着输入ss命令就可以得到如下输出信息：<br />Framework is launched.<br />id State Bundle<br />0 ACTIVE system.bundle_3.2.1.R32x_v20060919<br />1 INSTALLED HelloWorld_1.0.0<br />刚才开发的HelloWorld模块安装到了OSGi框架中，但此模块还没有运行起来。现在输入命令start 1。命令中的1是安装HelloWorld模块完成后，OSGi赋值给此模块的ID号。当运行启动命令后，控制台显示消息"Hello Eclipse!"。接着输入stop 1就会得到消息"Goodbye Eclipse!"。<br />这些命令都做了些什么事情？上面的代码实现了一个BundleActivator接口，这个接口用于允许框架通知我们一些重要的生命期事件。当模块启动时，框架调用start方法，而如果模块停止时，框架则调用stop方法。还有一个要说明的是元文件中有一行声明"Bundle-Activator: HelloActivator"就是用于通知框架在一个模块中那个类是启动类（activator）。通常我们使用一个完整的类名称，但在此示例中为了简单而只使用的缺省包名。<br />术语表：<br />Bundle： 模块<br />Activator：启动器<br />Component：组件<br />Service：服务<br />Sevice Register：服务注册表<br />Declarative Service: 服务声明，简称DS<br />OSGi Framework： OSGi框架或者框架<br />manifest file： 元文件</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 1em; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: arial, FreeSans, Helvetica, sans-serif; line-height: 20px; background-color: #ffffff; ">=========================================================================================<br />OSGi开发起步(Getting Started with OSGi)-2与OSGi框架进行交互<br />原文出自：&nbsp;<a href="http://neilbartlett.name/blog/osgi-articles/" style="color: #3c78b5; cursor: pointer; text-decoration: none; ">http://neilbartlett.name/blog/osgi-articles/</a>&nbsp;- Getting Started with OSGi</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 1em; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: arial, FreeSans, Helvetica, sans-serif; line-height: 20px; background-color: #ffffff; ">本文系翻译文章，但并未严格按照原文翻译。</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 1em; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: arial, FreeSans, Helvetica, sans-serif; line-height: 20px; background-color: #ffffff; ">2. 与OSGi框架进行交互</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 1em; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: arial, FreeSans, Helvetica, sans-serif; line-height: 20px; background-color: #ffffff; ">上一节举了一个简单的示例模块HelloWorld：启动和停止时输出消息的模块。该模块通过实现BundleActivator接口定义的start和stop函数方法完成了这些消息的输出。如果回过头来仔细的研究start和stop方法的定义，我们可以发现函数定义了一个BundleContext类型的参数。在本节描述参数BundleContext以及它的用法。<br />BundleContext是OSGi框架提供给一个模块的'通行票据'。当模块需要与框架进行交互和通信时，就可以使用BundleContext。实际上，这也是一个模块可以唯一与OSGi框架进行交互的渠道。OSGi框架在每个模块启动时会通过此模块的BundleActivator派送一个票据：BundleContext。<br />如果上一节的OSGi控制台还在运行状态，下面的工作就不需要重启控制台。如果控制台关闭了就需要使用下面的命令启动它。<br />&gt; java -jar equinox.jar -console<br />输入ss后就会发现上次安装过的HelloWorld模块。即便是OSGi框架关闭重启了之后，HelloWorld模块也会保持上节的状态。这主要是因为OSGi框架进行了模块的持久化存储处理。<br />在这一节我们完成一个查找并卸载HelloWorld模块的模块。在OSGi控制台可以使用uninstall命令简单的完成这项任务。但是这一节希望通过OSGiAPI编码的方式实现此任务。<br />创建一个名称为HelloWorldKiller.java的文件，输入下面的代码：<br />import org.osgi.framework.*;<br />public class HelloWorldKiller implements BundleActivator {<br />public void start(BundleContext context) {<br />System.out.println("HelloWorldKiller searching...");<br />Bundle[] bundles = context.getBundles();<br />for(int i=0; i&lt;bundles.length; i++) {<br />if("HelloWorld".equals(bundles[i]<br />.getSymbolicName())) {<br />try { System.out.println("Hello World found, " + "destroying!"); bundles[i].uninstall(); return; } catch (BundleException e) { System.err.println("Failed: " + e.getMessage()); }<br />}<br />}<br />System.out.println("Hello World bundle not found");<br />}<br />public void stop(BundleContext context) { System.out.println("HelloWorldKiller shutting down"); }<br />}<br />接着创建此模块的元文件，注意文件最后要留一个空白行。在元文件中输入以下内容：<br />Manifest-Version: 1.0<br />Bundle-Name: HelloWorldKiller<br />Bundle-Activator: HelloWorldKiller<br />Bundle-SymbolicName: HelloWorldKiller<br />Bundle-Version: 1.0.0<br />Import-Package: org.osgi.framework<br />编译和构建模块对应的jar文件：<br />&gt; javac -classpath equinox.jar HelloWorldKiller.java<br />&gt;jar -cfm HelloWorldKiller.jar HelloWorldKiller.mf HelloWorldKiller.class<br />在控制台安装构建完成的新模块：install&nbsp;<a href="file://helloworldkiller.jar/" style="color: #3c78b5; cursor: pointer; text-decoration: none; ">file:HelloWorldKiller.jar</a>。然后输入ss。模块的状态类似以下列表：<br />id State Bundle<br />0 ACTIVE system.bundle_3.2.1.R32x_v20060919<br />1 ACTIVE HelloWorld_1.0.0<br />2 INSTALLED HelloWorldKiller_1.0.0<br />接着启动我们的新模块HelloWorldKiller：start 2。其输出信息为：<br />HelloWorldKiller searching...<br />Hello World found, destroying!<br />Goodbye Eclipse!<br />注意最后一行是先前那个HelloWorld模块的输出信息。当这个模块处于运行状态并且启动HelloWorldKiller时，HelloWorld模块被停止并进行了卸载。这种操作触发了HelloWorld模块启动器的stop方法。<br />输入命令ss就会发现HelloWorld模块消失了。<br />id State Bundle<br />0 ACTIVE system.bundle_3.2.1.R32x_v20060919<br />2 ACTIVE HelloWorldKiller_1.0.0<br />这里的问题是：安全性如何保证？好像任何模块都可以卸载其他模块。实际上OSGi定义了一个复杂的安全层次机制。这个安全机制提供了对模块与框架交互的精确控制。这样就可以把卸载模块的权限限制在某些特定的管理模块。重要的是OSGi进行安全控制基本上是一个配置过程，所以在这个讲解系列里我们关注点主要放在编码上。<br />在开始下节讲解之前，我们可以了解一下BundleContext接口，看看此接口提供了哪些方法可供使用。比如，我们可以使用installBundle方法安装一个模块到OSGi中。或者也可以获取当前所有安装的模块列表并打印这些模块最近一次修改的日期和时间。具体的方法可以参考OSGi R4的API javadoc .<br />术语表：<br />Bundle： 模块<br />Activator：启动器<br />Component：组件<br />Service：服务<br />Sevice Register：服务注册表<br />Declarative Service: 服务声明，简称DS<br />OSGi Framework： OSGi框架或者框架<br />manifest file： 元文件</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 1em; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: arial, FreeSans, Helvetica, sans-serif; line-height: 20px; background-color: #ffffff; ">=========================================================================<br />OSGi开发起步(Getting Started with OSGi)-3 模块间依赖<br />3. 模块间依赖<br />上两节展示了模块的启动和停止以及模块与系统框架的交互方式。但是问题是模块真正的用途是什么呢？<br />模块是功能集合。模块使我们能够把庞大纷杂的项目划分成可管理的单元。这些单元可以方便的载入到OSGi运行环境。问题是，不管我们喜欢还是不喜欢，模块几乎总是依赖于其他模块。对jar文件来说，从来没有一个可靠的方式来指定不同jar之间的依赖关系（注意，jar元文件中的类路径也不是一个可靠的方法）。因此你永远不能真正的确定： Jar中的代码会正常工作？还是会在运行时失控抛出一个ClassNotFoundException异常？<br />OSGi以简洁的方式解决了这个问题。仅仅这样说还不如通过功能展示来的更有说服力些。所以让我们马上开始编码来做到这点。<br />不幸的是直至现在，我们一直使用默认包的方式进行类的开发。这种方式不适应更复杂问题的编码，所以现在需要使用java包的形式进行编码开发和功能划分。首先以一个非常简单的JavaBean类开始，复制以下代码到文件osgitut /movies/ Movie.java ：<br />package osgitut.movies;<br />public class Movie {<br />private final String title;<br />private final String director;<br />public Movie(String title, String director) { this.title = title; this.director = director; }<br />public String getTitle() { return title; }<br />public String getDirector() { return director; }<br />}<br />现在接着在此包内创建一个接口。复制以下代码到文件osgitut /movies/ MovieFinder.java ：<br />package osgitut.movies;<br />public interface MovieFinder { Movie[] findAll(); }<br />然后把这两个类封装为一个模块。当然，这个模块十分简单而且用途也不大。但是对于本节探讨的内容已经足够了。同前面一样，模块还必要有一个对应的元文件MoviesInterface.mf：<br />Manifest-Version: 1.0<br />Bundle-ManifestVersion: 2<br />Bundle-Name: Movies Interface<br />Bundle-SymbolicName: MoviesInterface<br />Bundle-Version: 1.0.0<br />Export-Package: osgitut.movies;version="1.0.0"<br />文件中多了一行：Export-Package。这个属性表明包osgitut.movies从这个模块中导出。而问题是java的jar文件中所有的东西都是导出的。但是我们难道不想只导出包中的部分代码而保持其他部分只在包内部可见吗?我们当然可以使用private或者protected限制类的访问范围，而这样一来同一个jar中其他包也不能访问这些类了。所以OSGi有效地引进了一种新的代码保护级别：如果模块中的包名没有在Export-Package头中列出，那么此包就只能在这个模块中才能访问到。<br />此外，我们还在导出包名称后挂接了软件版本号。在下一个阶段的讲解中我们会发现这十分重要。当然，这也不是绝对有必要提供一个版本。但如果你不指定版本号时，OSGi将自动为导出的包指定版本" 0.0.0 "。始终为导出的包指定明确的版本是一个优秀的实践经验。<br />现在构建此模块：<br />&gt; javac osgitut/movies/Movie.java osgitut/movies/MovieFinder.java<br />&gt; jar -cfm MoviesInterface.jar MoviesInterface.mf osgitut/movies/*.class<br />完成此项工作后我们不要急于安装此模块。在这之前我们还需要构建此模块的一个依赖模块。即一个实现了MovieFinder接口的具体实现类：osgitut/movies/impl/BasicMovieFinderImpl.java :<br />package osgitut.movies.impl;<br />import osgitut.movies.*;<br />public class BasicMovieFinderImpl implements MovieFinder {<br />private static final Movie[] MOVIES = new Movie[] { new Movie("The Godfather", "Francis Ford Coppola"), new Movie("Spirited Away", "Hayao Miyazaki") };<br />public Movie[] findAll() { return MOVIES; }<br />}<br />还需要一个元文件：BasicMovieFinder.mf。<br />Manifest-Version: 1.0<br />Bundle-ManifestVersion: 2<br />Bundle-Name: Basic Movie Finder<br />Bundle-SymbolicName: BasicMovieFinder<br />Bundle-Version: 1.0.0<br />Import-Package: osgitut.movies;version="[1.0.0,2.0.0)"<br />注意，在这里我们导入了一个由其他模块导出的包osgitut.movies。而且还同时为此导入包设置了版本范围。框架在运行时刻使用此版本范围匹配一个适合的导出包。OSGi表达版本范围使用的是通用的数学式语法：方括号表示包含，圆括号表示排除。在上面这个示例元文件中我们使用这个语法有效的指定了一个1.x的版本。<br />对本节示例来说，在导入部分加入版本限制并不是必需的。但这是一个通用的实践准则。<br />现在按照以下命令构建第二个模块：<br />&gt; javac -classpath MoviesInterface.jar osgitut/movies/impl/BasicMovieFinderImpl.java<br />&gt; jar -cfm BasicMovieFinder.jar BasicMovieFinder.mf osgitut/movies/impl/*.class<br />最后我们可以准备在OSGi环境中试验这两个模块。第一步就是安装BasicMovieFinder模块并运行ss命令。这时此模块的状态显示为INSTALLED。<br />id State Bundle<br />0 ACTIVE org.eclipse.osgi_3.3.0.v20070208<br />4 INSTALLED BasicMovieFinder_1.0.0<br />（说明，你的模块列表可能与上面这个示例列表存在一定的差异。实际上，模块id号会根据你上次安装和卸载HelloWorld模块的次数而确定。因此下面示例中的id号要根据你运行环境中相应的id进行替换。）<br />INSTALLED表示框架载入了这个模块，但还没有完成此模块依赖关系的解析。一个让框架执行模块依赖解析的命令是refresh。这里输入refresh 4然后是ss命令就可以看到：<br />id State Bundle<br />0 ACTIVE org.eclipse.osgi_3.3.0.v20070208<br />4 INSTALLED BasicMovieFinder_1.0.0<br />这个模块没有解析成功并仍然处于INSTALLED状态。这个问题出在我们并没有安装其依赖的那个包含Movie类和MovieFinder接口的模块。如果想确认是不是这个问题，在框架控制台输入diag 4以获取此模块的调试信息：<br /><a href="file://basicmoviefinder.jar/" style="color: #3c78b5; cursor: pointer; text-decoration: none; ">file:BasicMovieFinder.jar</a>&nbsp;[4]<br />Missing imported package osgitut.movies_[1.0.0,2.0.0).<br />看来确实是因为框架运行环境没有任何可供导出的osgitut.movies包。那么现在就安装MoviesInterface.jar模块并运行ss。大致的输出结果为：<br />id State Bundle<br />0 ACTIVE org.eclipse.osgi_3.3.0.v20070208<br />4 INSTALLED BasicMovieFinder_1.0.0<br />5 INSTALLED MoviesInterface_1.0.0<br />最后一步就是通知框架重新执行BasicMovieFinder模块的依赖解析。依旧使用refresh 4命令。输出的结果为：<br />id State Bundle<br />0 ACTIVE org.eclipse.osgi_3.3.0.v20070208<br />4 RESOLVED BasicMovieFinder_1.0.0<br />5 RESOLVED MoviesInterface_1.0.0<br />BasicMovieFinder模块现在处于已解析状态。这是一个基本步骤。因为只有处于已解析的模块才能启动。<br />需要注意的是通常情况下并不需要使用本节展示的手工方式进行模块依赖解析。一般来说模块会在它们需要的时候自动进行解析。例如，我们可以注意到MoviesInterface模块也处理已解析状态，而我们并没有针对此模块显示的执行refresh命令。<br />下一节我们进行OSGi服务开发。</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 1em; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: arial, FreeSans, Helvetica, sans-serif; line-height: 20px; background-color: #ffffff; ">=======================================================================================<br />OSGi开发起步(Getting Started with OSGi)-4注册一个服务<br />4 注册一个服务<br />我们终于可以开始OSGi服务的讲解了。在我看来，服务层是OSGi中最具特点的部分。在接下来的几节你就会充分体会到这一点。<br />上节讲解的是MovieFinder接口的例子。这个接口被MovieLister用来查找电影。这个例子是一个Ioc（控制反转）的例子。<br />MovieLister并不太关心原始电影数据的来源，所以我们使用了MovieFinder接口隐藏了这些细节。关键在于我们可以使用另外一种实现替代现有的MovieFinder：比如从数据库或者亚马逊web服务进行电影查询。而MovieLister只依赖与接口而不是具体的实现。<br />虽然不错，但是有些时候我们实际上还是要把一个MovieFinder的具体实现明确指定给MovieLister。我们是用一个外部容器把这个具体实现"推"给了MovieLister，而不是让MovieLister自己调用一个查找方法发现这个具体实现对象。<br />这就是IoC术语的由来。现在存在大量的这类外部容器，如PicoContainer,HiveMind,Spring以及EJB3.0。但是直至现在这些容器都有一个基本限制：它们都是静态的。一旦一个MovieFinder指定给了MovieLister，两者在整个JVM虚拟机生命周期内都一直关联。<br />OSGi支持具有动态特性的IoC模式。OSGi支持动态的提供给MovieLister一个MovieFinder的实现然后移除它们。这样我们就能在一个支持文本电影数据查询的应用程序和一个依赖亚马逊web服务电影数据查询的应用程序之间进行热切换。<br />OSGi服务层用于支持以上特性。我们要做的只是在服务注册表中注册MovieFinder为其中的一个服务就能支持热切换特性。这可是相当简单啊。同样的MovieLister的功能也可以由一个MovieLister服务提供。一个服务与一个普通的java对象（POJO）没有什么不同，它只是使用java接口名进行了服务注册而已。<br />在本节我们就看看如何在注册表中注册服务。接下来就试试怎样编码从注册表获取服务并提供给一个MovieLister。<br />在上节代码基础上增加一个BasicMovieFinder。不需要更改任何现有的类，只是在增加一个启动器类。下面就是这个启动类osgitut/movies/impl/BasicMovieFinderActivator.java:<br />package osgitut.movies.impl;<br />import org.osgi.framework.*;<br />import osgitut.movies.*;<br />import java.util.Properties;<br />import java.util.Dictionary;<br />public class BasicMovieFinderActivator implements BundleActivator {<br />private ServiceRegistration registration;<br />public void start(BundleContext context) { MovieFinder finder = new BasicMovieFinderImpl(); Dictionary props = new Properties(); props.put("category", "misc"); registration = context.registerService( MovieFinder.class.getName(), finder, props); }<br />public void stop(BundleContext context) { registration.unregister(); }<br />}<br />接下来就是修改BasicMovieFinder.mf文件：<br />Manifest-Version: 1.0<br />Bundle-ManifestVersion: 2<br />Bundle-Name: Basic Movie Finder<br />Bundle-SymbolicName: BasicMovieFinder<br />Bundle-Version: 1.0.0<br />Bundle-Activator: osgitut.movies.impl.BasicMovieFinderActivator<br />Import-Package: org.osgi.framework,<br />osgitut.movies;version="[1.0.0,2.0.0)"<br />在这个元文件中相对上节变化了两行。第一个增加了Bundle-Activator行。此行描述当前模块使用的启动模块（以前我们并没有使用）。此外还在导入包行中增加了org.osgi.framework。本节以前版本的模块并没有与框架进行交互，因此也就不需要导入OSGiAPI相关的包。<br />现在重新构建BasicMovieFinder.jar。<br />&gt; javac -classpath equinox.jar:MoviesInterface.jar osgitut/movies/impl/*.java<br />&gt; jar cfm BasicMovieFinder.jar BasicMovieFinder.mf osgitut/movies/impl/*.class<br />回到OSGi控制台，上节安装的BasicMovieFinder.jar还在框架中运行。因此要更新这个模块，运行update N命令，N是这个模块的ID（使用ss可以得到此ID）。接着使用start N命令启动此模块。接着我们看到什么-基本上也没看到些什么。<br />实际上，模块已经在OSGi服务注册表中注册了，但是并没有其他人在另外一端要使用此服务。因此这个服务的注册过程并没有产生任何可视效果。如果我们想向自己保证我们的代码确实运行了，也大可不必掘地三尺。执行下面这个命令就可以了：<br />services (objectClass=*MovieFinder)<br />我们会看到下面的输出：<br />{osgitut.movies.MovieFinder}={category=misc, service.id=22}<br />Registered by bundle:&nbsp;<a href="file://basicmoviefinder.jar/" style="color: #3c78b5; cursor: pointer; text-decoration: none; ">file:BasicMovieFinder.jar</a>&nbsp;[4]<br />No bundles using service.<br />看到了，服务的确注册了。在下节我们就讲解如何查找这个服务并在另外模块中使用此服务。</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 1em; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: arial, FreeSans, Helvetica, sans-serif; line-height: 20px; background-color: #ffffff; ">=============================================================================<br />OSGi开发起步(Getting Started with OSGi)-5使用一个服务<br />5. 使用一个服务：Consuming a Service<br />上节讲解了如何注册一个服务。现在就是在一个模块中查找和使用这些服务的时候了。<br />与上节一样，我们还是使用电影搜索的例子。我们已经构建了一个MovieFinder服务并在服务注册表中进行了服务注册。现状我们需要构建一个MovieLister来使用这个MovieFinder服务搜索某个导演执导的影片。这里假设MovieLister也是一个服务，也可以被其他诸如GUI程序使用。问题是，OSGi服务是动态的：它们可能有效也可能处理不可使用状态。这也就是说有时存在需要使用MovieFinder服务而它却正好不能使用的情况。<br />那么当MovieFinder不可用的时候MovieLister怎么办？显然，调用MovieFinder是MovieLister能有效运作的核心部分。实际上我们也只有以下几种选择：</p><ul type="square" style="margin-top: 0px; margin-right: 0px; margin-bottom: 1em; margin-left: 2em; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: arial, FreeSans, Helvetica, sans-serif; line-height: 20px; background-color: #ffffff; "><li 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; ">产生错误。如返回null或者抛出一个异常。</li><li 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; ">等待。</li><li 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; ">不让这种情况出现。<br />在本文我们选择比较简单的前两种选择。The third option may not even make any sense to you yet, but hopefully it will after we look at some of the implications of the first two.<br />首先我们需要定义MovieLister服务的接口。复制以下内容到文件osgitut/movies/MovieLister.java：<br />package osgitut.movies;<br />import java.util.List;<br />public interface MovieLister { List listByDirector(String name); }<br />然后是文件osgitut/movies/impl/MovieListerImpl.java :<br />package osgitut.movies.impl;<br />import java.util.*;<br />import osgitut.movies.*;<br />import org.osgi.framework.*;<br />import org.osgi.util.tracker.ServiceTracker;<br />public class MovieListerImpl implements MovieLister {<br />private final ServiceTracker finderTrack;<br />public MovieListerImpl(ServiceTracker finderTrack) { this.finderTrack = finderTrack; }<br />public List listByDirector(String name)<div style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; ">Unknown macro: { MovieFinder finder = (MovieFinder) finderTrack.getService(); if(finder == null) { return null; } else { return doSearch(name, finder); } }</div><br />private List doSearch(String name, MovieFinder finder) {<br />Movie[] movies = finder.findAll();<br />List result = new LinkedList();<br />for (int i = 0; i &lt; movies.length; i++)<div style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; ">Unknown macro: { if(movies[i].getDirector().indexOf(name) &gt; -1) { result.add(movies[i]); } }</div><br />return result;<br />}<br />}<br />上面的代码可能是至今我们最长的一段示例代码了。这里面有些什么呢？第一，实际的影片搜索逻辑分离到了doSearch(string, MovieFinder)方法中。此方法帮助我们隔离了搜索代码与OSGi代码。虽然，这里制定的搜索办法并不是很高效，但对于一个以示例为目的的程序已经足够了：实际上只是实现了一个只有两部影片的数据库。<br />第二部分是listByDirector(String name)方法。这个方法使用了一个ServiceTracker对象从服务注册表获取一个MovieFinder。ServiceTracker是一个十分有用的类。它封装和抽象了一些难于使用的OSGi底层API。无论如何我们还是要检查需要的服务是否实际可用。此服务假定在MovieLister构造函数中把ServiceTracker传递过啦。<br />注意，你可能在其他部分看到一些没有使用ServiceTracker从服务注册表查询服务的代码。例如，使用BundleContext的getServiceReference和GetSevice方法。但这样一来就需要编写一些更复杂的代码并需要仔细进行一些清理工作。在我看来，使用底层API并不能得到太多的好处，反倒是会引起许多问题。最好的选择还是使用ServiceTracker。<br />在一个模块的启动器中是创建ServiceTracker的最佳地点。复制以下代码到文件osgitut/movies/impl/MovieListerActivator.java：<br />package osgitut.movies.impl;<br />import java.util.*;<br />import org.osgi.framework.*;<br />import org.osgi.util.tracker.ServiceTracker;<br />import osgitut.movies.*;<br />public class MovieListerActivator implements BundleActivator {<br />private ServiceTracker finderTracker;<br />private ServiceRegistration listerReg;<br />public void start(BundleContext context) throws Exception { // Create and open the MovieFinder ServiceTracker finderTracker = new ServiceTracker(context, MovieFinder.class.getName(), null); finderTracker.open(); // Create the MovieLister and register as a service MovieLister lister = new MovieListerImpl(finderTracker); listerReg = context.registerService(MovieLister.class.getName(), lister, null); // Execute the sample search doSampleSearch(lister); }<br />public void stop(BundleContext context) throws Exception { // Unregister the MovieLister service listerReg.unregister(); // Close the MovieFinder ServiceTracker finderTracker.close(); }<br />private void doSampleSearch(MovieLister lister) {<br />List movies = lister.listByDirector("Miyazaki");<br />if(movies == null) { System.err.println("Could not retrieve movie list"); } else<div style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; ">Unknown macro: { for (Iterator it = movies.iterator(); it.hasNext();) { Movie movie = (Movie) it.next(); System.out.println("Title: " + movie.getTitle()); } }</div><br />}<br />}<br />现在这个启动器可以启动服务查找感兴趣的影片了。首先，在start方法中创建了一个ServiceTracker对象。这个对象由之前写的MovieLister使用。接着就打开ServiceTracker并设置此对象跟踪注册表中的MovieFinder服务实例。然后创建MovieListerImpl对象并使用MovieLister为名称在注册表中注册一个服务。最后，为了证明服务确实运行，在启动模块时启动器使用MovieLister执行了一个简单的搜索并输出了搜索结果。<br />到此，我们可以使用前几节的方法构建和安装我们设计的这些模块。要记得创建元文件，并在元文件中正确的设置Bundle-Activator为osgitut.movies.impl.MovieListerActivator。还有就是在导入包中包含以下三个包：org.osgi.framework , org.osgi.util.tracker and osgitut.movies。<br />一旦把MovieLister.jar安装到了OSGi框架运行环境，就可以启动这个模块。根据BasicMovieFinder模块是否处于运行状态，此时OSGi控制台会输出不同的消息。<br />如果没有运行，会输出：<br />osgi&gt; start 2<br />Could not retrieve movie list<br />如果处于运行状态则会输出：<br />osgi&gt; start 2<br />Title: Spirited Away<br />在停止和启动模块的时候，以上消息都会在控制台出现。还记得我们可以在服务处于不可用状态是选择等待吗？使用上面的代码，只需要做简单的修改：在MovieListerImpl的第16行中使用ServiceTracker的waitForService(5000)代替getService()，同时增加一个处理InterruptedException的try/catch块。<br />这会导致listByDirector()方法阻塞5000毫秒等待MovieFinder服务可用。如果这时刚好MovieFinder服务安装并且可以使用，这个方法就可以立即获取到这个服务。<br />通常并不建议以这样的方式挂起一个线程。在实践中，这样做是相对危险的。因为listByDirector方法实际是由模块启动器中的start方法调用的。而模块启动器start方法又是由一个框架线程调用。启动器必须快速返回，因为当前一个模块启动是还需要执行许多其他的工作。实际上在最糟糕的情况下这种等待的做法可能导致一个死锁。因为我们进入了一个框架对象的同步区，而这个同步区可能已经被其他现场锁定。通常的建议是不要在模块启动器的start方法或者其他任何框架直接调用的代码中执行任何长时间或者阻断性的操作。</li></ul><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 1em; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: arial, FreeSans, Helvetica, sans-serif; line-height: 20px; background-color: #ffffff; ">=================================================================<br />OSGi开发起步(Getting Started with OSGi)-6(1)动态服务跟踪<br />6 动态服务跟踪：Dynamic Service Tracking<br />上节讲解了如何使用一个服务：MovieLister使用MovieFinder查找由某个导演指导的影片。同时还涉及了处理OSGi服务动态特性的各种策略，特别介绍了MovieLister找不到一个有效的MovieFinder服务时的处理办法。<br />还有一种上节没有涉及的情况是：如何处理同时有多个MovieFinder服务可用的情况？因为任何模块都可以使用MovieFinder接口注册一个服务，并且对于服务注册机制来说任何模块都是同等对待的。<br />我们可以简单的忽略这个问题，这也是上节代码的实际处理逻辑。通过调用ServiceTracker上的getService()方法，我们选择由服务注册表任意提供一个MovieFinder服务。有一些参数可以调整注册表的选择策略（比如在服务注册时设置的SERVICE_RANKKING属性），但是作为一个服务的使用者我们并没有参与这个服务选择过程。并且实际上尽量少的控制并不是一件坏事，因为我们应该可以使用任何一个MovieFinder服务。这也是使用接口的原因。<br />从另外一方面来说，在一些情况下注册和使用多个服务也大有用途，例如，如果有多个有效的MovieFinder服务，这就意味这存在多个影片数据来源可供MovieLister使用。如果可以通过使用所有的这些服务进行影片查找，我们就可以获得更大的搜索网络空间并提供给用户更好的搜索结果。<br />另一个问题是上节遗留下来的：在没有任何一个MovieFinder服务可用时，MovieLister应该如何正确处理？上节的处理逻辑是在服务无效时简单的在listByDirector调用时返回null。But what if we made it impossible for methods on MovieLister to be called when the MovieFinder isn't present?<br />MovieLister与MovieFinder一样是一个服务。如果MovieFinder服务消失了，MovieLister服务是不是也应该消失？也就是说，我们希望MovieLister与MovieFinder之间有一个一对多的依赖关系。本文的最后一节，我们介绍零对一依赖关系。<br />首先使用下面的代码修改MovieListerImpl类：<br />package osgitut.movies.impl;<br />import java.util.*;<br />import osgitut.movies.*;<br />public class MovieListerImpl implements MovieLister {</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 1em; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: arial, FreeSans, Helvetica, sans-serif; line-height: 20px; background-color: #ffffff; ">private Collection finders =<br />Collections.synchronizedCollection(new ArrayList());</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 1em; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: arial, FreeSans, Helvetica, sans-serif; line-height: 20px; background-color: #ffffff; ">protected void bindFinder(MovieFinder finder) { finders.add(finder); System.out.println("MovieLister: added a finder"); }</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 1em; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: arial, FreeSans, Helvetica, sans-serif; line-height: 20px; background-color: #ffffff; ">protected void unbindFinder(MovieFinder finder) { finders.remove(finder); System.out.println("MovieLister: removed a finder"); }</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 1em; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: arial, FreeSans, Helvetica, sans-serif; line-height: 20px; background-color: #ffffff; ">public List listByDirector(String director) {<br />MovieFinder[] finderArray = (MovieFinder[])<br />finders.toArray(new MovieFinder[finders.size()]);<br />List result = new LinkedList();<br />for(int j=0; j&lt;finderArray.length; j++) {<br />Movie[] all = finderArray[j].findAll();&nbsp;<br />for(int i=0; i&lt;all.length; i++) {<br />if(director.equals(all[i].getDirector())) { result.add(all[i]); }<br />}<br />}<br />return result;<br />}</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 1em; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: arial, FreeSans, Helvetica, sans-serif; line-height: 20px; background-color: #ffffff; ">}<br />上面的代码移除了MovieListerImpl上所有的OSGi依赖。MovieListerImpl现在是一个纯POJO了。当然，它还需要其他对象协助跟踪MovieFinder服务并通过bindFinder方法提供此服务。因此我们还需要创建一个新的java文件：osgitut/movies/impl/MovieFinderTracker.java。<br />package osgitut.movies.impl;<br />import org.osgi.framework.*;<br />import org.osgi.util.tracker.*;<br />import osgitut.movies.*;<br />public class MovieFinderTracker extends ServiceTracker {</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 1em; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: arial, FreeSans, Helvetica, sans-serif; line-height: 20px; background-color: #ffffff; ">private final MovieListerImpl lister = new MovieListerImpl();<br />private int finderCount = 0;<br />private ServiceRegistration registration = null;</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 1em; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: arial, FreeSans, Helvetica, sans-serif; line-height: 20px; background-color: #ffffff; ">public MovieFinderTracker(BundleContext context) { super(context, MovieFinder.class.getName(), null); }</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 1em; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: arial, FreeSans, Helvetica, sans-serif; line-height: 20px; background-color: #ffffff; ">private boolean registering = false;<br />public Object addingService(ServiceReference reference) {<br />MovieFinder finder = (MovieFinder) context.getService(reference);<br />lister.bindFinder(finder);<br />synchronized(this) { finderCount ++; if (registering) return finder; registering = (finderCount == 1); if (!registering) return finder; }<br />ServiceRegistration reg = context.registerService(<br />MovieLister.class.getName(), lister, null);<br />synchronized(this) { registering = false; registration = reg; }<br />return finder;<br />}<br />public void removedService(ServiceReference reference, Object service) {<br />MovieFinder finder = (MovieFinder) service;<br />lister.unbindFinder(finder);<br />context.ungetService(reference);<br />ServiceRegistration needsUnregistration = null;<br />synchronized(this) {<br />finderCount --;<br />if (finderCount == 0) { needsUnregistration = registration; registration = null; }<br />}<br />if(needsUnregistration != null) { needsUnregistration.unregister(); }<br />}<br />}</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 1em; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: arial, FreeSans, Helvetica, sans-serif; line-height: 20px; background-color: #ffffff; ">==========================================================================================<br />OSGi开发起步(Getting Started with OSGi)-6(2)动态服务跟踪<br />上面这个类覆盖了上节讨论的ServiceTracker类，并且定制了服务有效和失效时ServiceTracker的行为。具体来说就是，当一个增加MovieFinder服务时调用addingSevice方法，当删除一个MovieFinder服务时调用removedService方法。此外还有一个modifiedService方法，只不过在本节处理逻辑中不需要而没有覆盖。<br />有必要仔细看看这两个方法中的处理逻辑。首先，在addingSevice方法中传入的参数是ServiceReference而不是一个实际服务实现对象。ServiceReference是一个轻量级的句柄对象。它可以作为一个参数任意的进行传输，并可以用来获取实际服务的属性。比如，服务在进行服务注册时传入的属性集。实际上，使用一个ServiceReference对象并不会导致OSGi框架增加实际服务的引用计数。你可以认为ServiceReference承担了类似java反射API中WeakReference类的角色。<br />在addingSevice方法中首先是通过ServiceReference获取一个实际的MovieFinder服务对象。这又要使用BundleContext：任何与OSGi框架的交互都是通过BundleContext接口进行的。幸运的是，ServiceTracker定义了一个我们可以直接使用的类型为BundleContext的保护成员context。<br />接下来我们使用bindFinder方法把获取到的MovieFinder服务绑定到MovieListerImpl。然后就把MovieListerImpl也注册为一个使用MovieLister接口的服务。注意，我们只在MovieListerImpl没有注册为服务的情况下才进行服务注册。这是因为，我们现在希望一个MovieLister使用多个MovieFinder服务。<br />最后，addingSevice方法返回一个Object。问题是，我们到底要返回一个什么对象呢？实际上，ServiceTracker并不关心addingSevice返回什么对象。addingSevice可以返回任何我们需要的对象。重点是，addingSevice返回的对象会在调用modifiedService或者removedService的时候重新传回。这也就是在removedService方法第一行见到的直接对object进行的MovieFinder类型转换。接着就使用此对象进行它与MovieLister的解绑，同时我们根据可以使用的MovieFinder服务是否为零进行MovieLister服务的反注册。<br />一般说来，任何在addingSevice中的行为都应该在removedService中进行状态清理。因此，我们应该在addingSevice中返回任何有助于我们需要在removedService中进行清理工作的对象。返回的可以是一个HashMap中的键值，也可以是一个ServiceRegistration对象，或者是一个实际的服务对象。<br />在removedService的最后一步，我们解绑了在addingSevice中绑定的服务。这一点十分重要，这个操作使得服务注册表减少服务使用计数值，从而可以使计数值为零进行服务释放。<br />现状我们还需要一个模块启动器osgitut/movies/impl/TrackingMovieListerActivator.java：<br />package osgitut.movies.impl;<br />import org.osgi.framework.*;<br />public class TrackingMovieListerActivator implements BundleActivator {<br />private MovieFinderTracker tracker;<br />public void start(BundleContext context) { tracker = new MovieFinderTracker(context); tracker.open(); }<br />public void stop(BundleContext context) { tracker.close(); }<br />}<br />不要忘记TrackingMovieLister.mf：<br />Manifest-Version: 1.0<br />Bundle-ManifestVersion: 2<br />Bundle-Name: Tracking Movie Lister<br />Bundle-SymbolicName: TrackingMovieLister<br />Bundle-Version: 1.0.0<br />Bundle-Activator: osgitut.movies.impl.TrackingMovieListerActivator<br />Import-Package: org.osgi.framework,<br />org.osgi.util.tracker,<br />osgitut.movies;version="[1.0.0,2.0.0)"<br />按照前几节的步骤进行模块的构建和部署。完成后，执行以下的步骤验证运行逻辑是否正确：</p><ul type="square" style="margin-top: 0px; margin-right: 0px; margin-bottom: 1em; margin-left: 2em; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: arial, FreeSans, Helvetica, sans-serif; line-height: 20px; background-color: #ffffff; "><li 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; ">启动 BasicMovieFinder和TrackingMovieLister。检查是否出现"MovieLister：added a finder"消息。</li><li 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; ">停止 BasicMovieFinder。检查是否出现"MovieLister: removed a finder"消息。</li><li 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; ">执行service命令，检查是否MovieLister服务已经反注册。<br />本节内容展示的是一个十分强大的技术。我们把一个服务的生命周期与另一个服务（实际上是多个）的生命周期绑定。进一步，我们可以绑定MovieLister和第三个服务并可以依此类推。这样我们就构建了一个服务依赖图，这种依赖图与其他一些静态IOC容器构建的beans图不同。OSGi的服务依赖图更健壮，self-healing并具有自动调整能力。<br />另一方面，这一节的代码还存在一些问题。MovieFinderTracker和TrackingMovieListerActivator类会形成整个系统负载的瓶颈。当我们打算进行这个系统的扩展时，我们还不得不重复进行这些基本雷同的编码工作。因此在下一节我们讲解如何使用几行XML语句完成这些重复和繁琐的工作。<br />在下一节不再在一个目录下使用命令行构建我们的系统。本文的目标是展示OSGi是一个简单而强大的框架，但并不妨碍我们使用更高效的方式比如eclipse的IDE完成这些工作。</li></ul><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 1em; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: arial, FreeSans, Helvetica, sans-serif; line-height: 20px; background-color: #ffffff; ">==================================================================================================<br />OSGi开发起步(Getting Started with OSGi)-7服务声明介绍<br />7 服务声明介绍：Introducing Declarative Services<br />服务声明（简称DS）规范是OSGi最新的内容之一。增加此服务规范是为了在多个模块之间有效协调服务。这种服务协调工作本身并不复杂（如上节我们展示的那样），但是这项工作总是面临那些恼人的代码。而且具体实现时还必须注意处理各种线程问题。总的来说，模块服务协作这件工作是一件极易出错的工作。<br />最早试图解决服务协作问题的是一个称为Service Binder的工具。这个工具由Humberto Cervantes 和 Richard Hall开发。Service Binder设计了自动服务依赖管理特性。这个特性允许开发者专注于服务。服务之间的协作和依赖通过声明实现，而所有的声明都使用XML完成。<br />服务声明规范是由Service Binder发展而来，并成为了OSGi4.0标准的一部分。<br />前面几节的示例都是使用的命令行完成所有的编码、配置、构建和运行工作。从这节开始则使用EclipseSDK。需要明确的是，本文中介绍的任何技术和方法并不依赖于Eclipse。尽管Eclipse可以很好的帮助我们完成很多工作，但同样的我们也可以使用NetBeans，Intellij或者vi来完成这些工作。<br />首先我们需要下载Equinox的服务声明实现包。可以在Equinox的下载页面下载最新的org.eclipse.equniox.ds_x.x.x_xxxx.jar。下载完成后，把下载的jar复制到Eclipse的plugins目录，并重新启动Eclipse。<br />现在重新创建一个模块。在Eclipse中使用插件工程向导：<br />l 在主菜单中选择File-&gt;New-&gt;Project。<br />l 选择Plug-in Project并点击Next。<br />l 输入工程的名称：SampleExporter。<br />l 在最下面的"This plug-in is targeted to run with"文字下选择"OSGi framework"以及下拉框中的"standard"。这个是个基本步骤：这一步可以防止我们使用OSGi框架实现之外的特性。<br />l 选择"Next"。在向导显示的下一个对话框中选择"Generate an activator..."。如果选择，点击"Finish"完成创建工作。<br />我们现在生成了一个空的模块工程。接下来的工作就是在这个工程中增加代码。为了使得示例尽量简单，我们使用在每个java运行环境中都存在的java.lang.Runnable接口提供服务。现在我们可以使用Eclipse的复制代码特性创建包和源文件。复制下面的代码，在Eclipse中选择SampleExporter工程的src目录，并执行Edit-&gt;Paste。<br />package org.example.ds;<br />public class SampleRunnable implements Runnable {<br />public void run() { System.out.println("Hello from SampleRunnable"); }<br />}<br />接下来我们需要创建一个XML文件。使用这个文件声明SampleRunnable是一个服务。在工程的顶级目录下创建一个名称为OSGI-INF的目录，新建一个名称为samplerrunnable.xml的文件然后复制下面的文本到文件中。<br />&lt;?xml version="1.0"?&gt;<br />&lt;component name="samplerunnable"&gt;<br />&lt;implementation class="org.example.ds.SampleRunnable"/&gt;<br />&lt;service&gt;<br />&lt;provide interface="java.lang.Runnable"/&gt;<br />&lt;/service&gt;<br />&lt;/component&gt;<br />这是一个最简单的DS声明。这个声明表示有一个名称为"samplerunnable"的组件，这个组件使用接口java.lang.Runnable向服务注册器提供一个服务。同时还说明了这个模块是由org.example.da.SampleRunnable类实现的。<br />最后就是要告知服务声明runtime这个XML文件的位置。通知是通过在组件的MANIFEST.MF文件中增加一项说明实现的。在Eclipse中打开元文件页面，定位到"MANIFEST.MF"文件窗格。在这个文件内容的最后增加一行：<br />Sevice-Component: OSGI-INF/samplerunnable.xml<br />然后保存文件。在进行下一步之前，我们可以运行Equinox检查一下我们的模块是否可以正常运行。在Run菜单中选择"Run..."，当运行对话框打开后选择左边树中的"Equniox OSGi Framework"并点击New按钮。如果你是一个熟练的Eclipse用户但又没有做过OSGi开发，这个对话框相对来说有些复杂（或者奇怪）。第一个标识为Bundles的窗格允许我们选择哪些Bundles是运行环境需要包含的，是否需要启动等。为了使用一个简化的运行环境，取消当前所有选中的Bundles，并重新选择以下的Bundles：<br />SampleExporter (underneath Workspace)<br />org.eclipse.equinox.ds (underneath Target Platform)<br />除此之外我们还有一些依赖的Bundles，这可以使用Add Required Bundles功能把这些依赖的Bundles添加进来。最好把"Validate bundles automatically prior to launching"也选中。最后，点击Run运行这个工程。一段时间后osgi&gt;提示符就出现在Eclipse控制台。OSGi框架运行起来了，我们可以使用前几节的命令与框架进行交互。<br />我们开发的服务注册了没有？使用services命令检查一下。在命令的输出结果中，我们会发现类似的信息：<br />{java.lang.Runnable}={component.name=samplerunnable, component.id=1, service.id=22}<br />Registered by bundle: initial@reference:<a href="file://..../SampleExporter/" style="color: #3c78b5; cursor: pointer; text-decoration: none; ">file:..../SampleExporter/</a>&nbsp;[5]<br />No bundles using service.<br />看来服务的确注册了。需要指出的是：服务是由我们的模块注册的，而不是由声明服务模块注册的，实际上，In fact DS has registered it on behalf of our bundle。还需要注意的是，服务的使用者并不需要为了使用这个服务而做什么特别的处理，它甚至不必知道我们使用了服务声明。这些服务使用者也可以使用DS或者直接使用OSGi代码来访问服务。<br />还有一点就是在上面的模块代码中并没有出现与OSGi标准相关的java代码。也就是说，本节我们编写的是一个POJO类。这也是DS的一个主要特点。</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 1em; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: arial, FreeSans, Helvetica, sans-serif; line-height: 20px; background-color: #ffffff; ">=================================================================================================================<br />OSGi开发起步(Getting Started with OSGi)-8(1)服务声明和依赖</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 1em; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: arial, FreeSans, Helvetica, sans-serif; line-height: 20px; background-color: #ffffff; ">8服务声明和依赖：Declarative Services and Dependencies<br />上节对DS做了初步的介绍。本节介绍如何使用DS声明的服务。上节我们使用java.lang.Runnable接口使用DS注册了一个服务，本节介绍如何创建一个依赖于这个服务的组件。<br />正如介绍的那样，DS规范使得开发者只需关注应用自身的逻辑，避免如前几节需要编写的那些OSGi服务"粘合"代码。使用DS，我们只要简单的编写代码就可以了。但在之间编码之前，我们还得创建一个Eclipse工程。使用上节的工程创建步骤创建一个名称为SampleImporter的工程。<br />复制下面的代码并粘贴到新建工程的src目录：<br />package org.example.ds;<br />import org.eclipse.osgi.framework.console.CommandInterpreter;<br />import org.eclipse.osgi.framework.console.CommandProvider;<br />public class SampleCommandProvider1 implements CommandProvider {</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 1em; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: arial, FreeSans, Helvetica, sans-serif; line-height: 20px; background-color: #ffffff; ">private Runnable runnable;</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 1em; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: arial, FreeSans, Helvetica, sans-serif; line-height: 20px; background-color: #ffffff; ">public synchronized void setRunnable(Runnable r) { runnable = r; }<br />public synchronized void unsetRunnable(Runnable r) { runnable = null; }<br />public synchronized void _run(CommandInterpreter ci) {<br />if(runnable != null) { runnable.run(); } else { ci.println("Error, no Runnable available"); }<br />}<br />public String getHelp() { return "\trun - execute a Runnable service"; }<br />}<br />这个类实现了CommandProvider接口。启动Equinox后在osgi&gt;提示符可以执行一系列的命令。CommandProvider接口用于扩充这些命令。编写CommandProvider的原因是为了提供一种方便的交互式测试代码方式。在IBM developerWorks上Chirs Aniszczyk的一篇文章详细讨论了CommandProvider。<br />在上面的代码中我们注意到没有任何OSGi方法调用，实际上我们并不需要从org.osgi.*导入任何类。我们依赖的那个服务（在本示例中是一个java.lang.Runnable实例）是通过setRunnable方法提供的，并且通过unsetRunnable移除。我们可以认为这是某种形式的依赖注射。<br />另外两个方法getHelp和_run是CommandProvider接口定义的实现方法。_run方法名的下划线有些滑稽，但这也只是Equinox控制台API的一项odd特性，与OSGi的DS无关。在Equinox控制台中规定具有下划线的方法名称是一个控制台命令。因此定义一个_run方法就相当于在Equinox控制台中增加了一个run命令。另外一个需要注意的情况是，在上面这个类的实现代码中仔细的处理了runnable属性的线程安全特性。由于OSGi本质上是多线程的，因此在OSGi中线程安全是十分重要的。Frankly我们应该总是编写线程安全的代码。<br />和以前一样，我们还是需要一个包含DS声明的XML文件。复制下面的文本到这个插件工程的OSGI-INF/commandprovider1.xml文件中：<br />&lt;?xml version="1.0"?&gt;<br />&lt;component name="commandprovider1"&gt;<br />&lt;implementation class="org.example.ds.SampleCommandProvider1"/&gt;<br />&lt;service&gt;<br />&lt;provide interface="org.eclipse.osgi.framework.console.CommandProvider"/&gt;<br />&lt;/service&gt;<br />&lt;reference name="RUNNABLE"<br />interface="java.lang.Runnable"<br />bind="setRunnable"<br />unbind="unsetRunnable"<br />cardinality="0..1"<br />policy="dynamic"/&gt;<br />&lt;/component&gt;<br />编写完成了XML文件后还要把下面这行加入到bundle元文件的最后：<br />Service-Component: OSGI-INF/commandprovider1.xml</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 1em; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: arial, FreeSans, Helvetica, sans-serif; line-height: 20px; background-color: #ffffff; ">===========================================================================================================<br />OSGi开发起步(Getting Started with OSGi)-8(2)服务声明和依赖</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 1em; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: arial, FreeSans, Helvetica, sans-serif; line-height: 20px; background-color: #ffffff; ">在XML文件中有两个我们已经介绍过的元素项：implementation和service。Implementation提供了组件的实现类，而service则告知DS把这个组件注册为一个服务。这个示例中的服务使用CommandProvider接口注册。这个接口使得Equinox控制台知晓这个CommandProvider的存在。<br />文件中还有一个以前没有介绍的reference元素项。reference用于向DS声明组件与一个服务之间的依赖关系。其中的name属性为任意字符串。名称属性用于对依赖进行命名（我们并不需要关心它的用途-实际上只是一个可读性的标识）。本示例中使用了组件依赖的接口名称来命名。bind属性指定一个实现类的方法名。当一个服务可用时，也就是一个Runnable的服务在服务注册表中注册时，DS就会调用此方法。DS使用这个方法传入这个新服务对象的引用提供给我们的组件使用。同样的，unbind属性是当一个服务不可用时，DS回调的方法。<br />Cardinality是一个能显示DS真正能力的属性。这个属性控制服务的依赖是可选的还是强制的、单依赖还是多依赖。这个属性可能的值为：<br />0..1: optional and singular, "zero or one"<br />1..1: mandatory and singular, "exactly one"<br />0..n: optional and multiple, "zero to many"<br />1..n: mandatory and multiple, "one to many" or "at least one"<br />在这个示例中我们使用的是0.1。也就是能cope那些不可用的服务。回头看以下_run方法中的代码，我们就会发现为了处理这种情况而进行的null检查代码。<br />现在还是来看看这个模块运行的情况。如果上节安装的SampleExporter还有效，在osgi控制台输入run命令会得到如下输出：<br />Hello from SampleRunnable<br />这证实了我们已经成功将上节编写的Runnable服务成功导入。接着运行stop命令停止SampleExporter模块。再运行run命令得到的输出是：<br />Error,no Runnable available<br />这个输出表明DS发现了Runnable服务已经停止并调用了unsetRunnable方法。<br />再看一下cardinality属性，如果把它的值改为1.1（改可选为强制）会怎样？更改这个属性后重新运行Equinox。在SampleExporter启动后运行run命令会得到与前面相同的结果。但是当停止SampleExporter后运行run，OSGi输出的是一个与前面不同的错误消息。实际上，Equinox控制台输出的是一个帮助消息。这个消息是对一个不可识别命令的标准错误输出消息。这也就是说我们这个命令执行服务已经被DS反注册了。当一个组件的强制依赖不能满足是，DS强制停止这个组件并反注册组件提供的任何服务。这就是Equinox不能识别run命令的原因。<br />如此方便的服务装配是选择DS的最佳原因。还记得使用ServiceTracker时，我们处理这种情况时不得不编写的那些代码吗？<br />还有一个值得关注的属性是ploicy。这个属性的值定义为static和dynamic之一。这两个状态值用于表明组件是否支持服务的动态切换。如果不支持，DS就没有必要每次在目标服务变化后停止原组件并创建一个新组件实例。停止后新建这个过程可是一个重负载性的过程，所以我们的建议最好还是尽可能的编码一个支持动态切换的组件。不过DS的这个属性缺省是静态的，因此在组件开发时还得手动的调整此属性为动态。<br />上面我们试了0.1和1.1，但是没有讲解多对多（0.n）的依赖。在服务注册表中不会仅仅只有一个Runnable服务注册。如果我们只是需要绑定其中的一个服务，那么我们所绑定的将是其中的任意一个服务。看来我们还需要一个runall命令用来运行服务注册表中已经注册的所有Runnable服务。<br />但就对上面那个组件来说，我们把cardinality属性更改为"0.n"时会出现什么情况？其实，更改后这个服务还是可以运行：只不过setRunnable不会只调用一次，DS会在每个Runnable服务注册是调用此方法一次。问题是多次调用会导致上面组件处理逻辑的混乱。因此为应对更通常的0.n的情况，就不能只是使用单个的Runnable成员，而是需要一个Runnable集合成员。下面是根据以上分析修改的类，把这个类的代码复制到工程的src目录下：<br />package org.example.ds;<br />import java.util.*;<br />import org.eclipse.osgi.framework.console.CommandInterpreter;<br />import org.eclipse.osgi.framework.console.CommandProvider;<br />public class SampleCommandProvider2 implements CommandProvider {<br />private List&lt;Runnable&gt; runnables =<br />Collections.synchronizedList(new ArrayList&lt;Runnable&gt;());</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 1em; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: arial, FreeSans, Helvetica, sans-serif; line-height: 20px; background-color: #ffffff; ">public void addRunnable(Runnable r) { runnables.add(r); }<br />public void removeRunnable(Runnable r) { runnables.remove(r); }<br />public void _runall(CommandInterpreter ci) {<br />synchronized(runnables) {<br />for(Runnable r : runnables) { r.run(); }<br />}<br />}<br />public String getHelp() { return "\trunall - Run all registered Runnables"; }<br />}<br />再创建OSGI-INF/commandprovider2.xml：<br />&lt;?xml version="1.0"?&gt;<br />&lt;component name="commandprovider2"&gt;<br />&lt;implementation class="org.example.ds.SampleCommandProvider2"/&gt;<br />&lt;service&gt;<br />&lt;provide interface="org.eclipse.osgi.framework.console.CommandProvider"/&gt;<br />&lt;/service&gt;<br />&lt;reference name="RUNNABLE"<br />interface="java.lang.Runnable"<br />bind="addRunnable"<br />unbind="removeRunnable"<br />cardinality="0..n"<br />policy="dynamic"/&gt;<br />&lt;/component&gt;<br />最后是把这个文件按照如下方式加到元文件中：<br />Service-Component: OSGI-INF/commandprovider1.xml,<br />OSGI-INF/commandprovider2.xml<br />上面的XML文件中的DS声明与以前的基本一样，只是修改了bind和unbind方法的名称，以及cardinality的值（0.n）。现在就可以试着运行这个新的runall命令看看结果如何啦。欣赏完输出结果后，还可以试着更改cardinality为1.n看看有什么不一样的事情发生！<br />本节是这个讲解的最后一节。通过这几节的讲解我们发现OSGi并不神秘并且相当易用。接下来的事情就是不断的开发和使用它。不过，我还是强烈的建议在开始和进行过程中一定认真的学习一下OSGi R4 core和service规范。</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 1em; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: arial, FreeSans, Helvetica, sans-serif; line-height: 20px; background-color: #ffffff; ">=======================================================================================</p><img src ="http://www.cppblog.com/lauer3912/aggbug/174990.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-05-15 17:07 <a href="http://www.cppblog.com/lauer3912/archive/2012/05/15/174990.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>什么叫IOC（编程术语)</title><link>http://www.cppblog.com/lauer3912/archive/2012/05/15/174960.html</link><dc:creator>RTY</dc:creator><author>RTY</author><pubDate>Tue, 15 May 2012 05:11:00 GMT</pubDate><guid>http://www.cppblog.com/lauer3912/archive/2012/05/15/174960.html</guid><wfw:comment>http://www.cppblog.com/lauer3912/comments/174960.html</wfw:comment><comments>http://www.cppblog.com/lauer3912/archive/2012/05/15/174960.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lauer3912/comments/commentRss/174960.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lauer3912/services/trackbacks/174960.html</trackback:ping><description><![CDATA[<h1><div style="text-align: left;"><a id="cb_post_title_url" href="http://www.cnblogs.com/jazzka702/archive/2009/06/17/1504756.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; color: #6466b3; text-decoration: none; ">什么叫IOC（编程术语</a>)<br /><div><span style="font-size: 10pt; ">IoC就是Inversion of Control，控制反转。在Java开发中，IoC意味着将你设计好的类交给系统去控制，而不是在你的类内部控制。这称为控制反转。</span></div><div></div><div></div><div><span style="font-size: 10pt; ">下面我们以几个例子来说明什么是IoC</span></div><div></div><div></div><div><span style="font-size: 10pt; ">假设我们要设计一个Girl和一个Boy类，其中Girl有kiss方法，即Girl想要Kiss一个Boy。那么，我们的问题是，Girl如何能够认识这个Boy？</span></div><div></div><div></div><div><span style="font-size: 10pt; ">在我们中国，常见的MM与GG的认识方式有以下几种&nbsp;</span></div><div><span style="font-size: 10pt; ">1 青梅竹马； 2 亲友介绍； 3 父母包办&nbsp;</span></div><div><span style="font-size: 10pt; ">那么哪一种才是最好呢？</span></div><div></div><div></div><div><span style="font-size: 10pt; ">青梅竹马：Girl从小就知道自己的Boy。</span></div><div></div><div></div><div><span style="font-size: 10pt; ">public class Girl {&nbsp;</span></div><div><span style="font-size: 10pt; ">&nbsp; &nbsp; void kiss(){&nbsp;</span></div><div><span style="font-size: 10pt; ">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Boy boy = new Boy();&nbsp;</span></div><div><span style="font-size: 10pt; ">&nbsp; &nbsp; }&nbsp;</span></div><div><span style="font-size: 10pt; ">}</span></div><div></div><div></div><div><span style="font-size: 10pt; ">然而从开始就创建的Boy缺点就是无法在更换。并且要负责Boy的整个生命周期。如果我们的Girl想要换一个怎么办？（严重不支持Girl经常更换Boy,#_#）</span></div><div></div><div></div><div><span style="font-size: 10pt; ">亲友介绍：由中间人负责提供Boy来见面</span></div><div></div><div></div><div><span style="font-size: 10pt; ">public class Girl {&nbsp;</span></div><div><span style="font-size: 10pt; ">&nbsp; &nbsp; &nbsp;void kiss(){&nbsp;</span></div><div><span style="font-size: 10pt; ">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Boy boy = BoyFactory.createBoy();&nbsp;</span></div><div><span style="font-size: 10pt; ">&nbsp; &nbsp; &nbsp;}&nbsp;</span></div><div><span style="font-size: 10pt; ">}</span></div><div></div><div></div><div><span style="font-size: 10pt; ">亲友介绍，固然是好。如果不满意，尽管另外换一个好了。但是，亲友BoyFactory经常是以Singleton的形式出现，不然就是，存在于Globals，无处不在，无处不能。实在是太繁琐了一点，不够灵活。我为什么一定要这个亲友掺和进来呢？为什么一定要付给她介绍费呢？万一最好的朋友爱上了我的男朋友呢？</span></div><div></div><div></div><div><span style="font-size: 10pt; ">父母包办：一切交给父母，自己不用费吹灰之力，只需要等着Kiss就好了。</span></div><div></div><div></div><div><span style="font-size: 10pt; ">public class Girl {&nbsp;</span></div><div><span style="font-size: 10pt; ">&nbsp; &nbsp; &nbsp;void kiss(Boy boy){&nbsp;</span></div><div><span style="font-size: 10pt; ">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// kiss boy&nbsp;</span></div><div><span style="font-size: 10pt; ">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;boy.kiss();&nbsp;</span></div><div><span style="font-size: 10pt; ">&nbsp; &nbsp; &nbsp;}&nbsp;</span></div><div><span style="font-size: 10pt; ">}</span></div><div></div><div></div><div><span style="font-size: 10pt; ">Well，这是对Girl最好的方法，只要想办法贿赂了Girl的父母，并把Boy交给他。那么我们就可以轻松的和Girl来Kiss了。看来几千年传统的父母之命还真是有用哦。至少Boy和Girl不用自己瞎忙乎了。</span></div><div></div><div></div><div><span style="font-size: 10pt; ">这就是IOC，将对象的创建和获取提取到外部。由外部容器提供需要的组件。&nbsp;</span></div><div></div><div></div><div><span style="font-size: 10pt; ">我们知道好莱坞原则：&#8220;Do not call us, we will call you.&#8221; 意思就是，You, girlie, do not call the boy. We will feed you a boy。</span></div><div></div><div></div><div><span style="font-size: 10pt; ">我们还应该知道依赖倒转原则即 Dependence Inversion Princinple，DIP</span></div><div></div><div></div><div><span style="font-size: 10pt; ">Eric Gamma说，要面向抽象编程。面向接口编程是面向对象的核心。</span></div><div></div><div></div><div><span style="font-size: 10pt; ">组件应该分为两部分，即 Service, 所提供功能的声明 Implementation, Service的实现</span></div><div></div><div></div><div><span style="font-size: 10pt; ">好处是：多实现可以任意切换，防止 &#8220;everything depends on everything&#8221; 问题．即具体依赖于具体。&nbsp;</span></div><div></div><div></div><div><span style="font-size: 10pt; ">所以，我们的Boy应该是实现Kissable接口。这样一旦Girl不想kiss可恶的Boy的话，还可以kiss可爱的kitten和慈祥的grandmother。</span></div><div><span style="font-size: 10pt; ">二、IOC的type</span></div><div>&nbsp;</div><div></div><div><span style="font-size: 10pt; ">IoC的Type指的是Girl得到Boy的几种不同方式。我们逐一来说明。</span></div><div></div><div></div><div><span style="font-size: 10pt; ">IOC type 0：不用IOC&nbsp;</span></div><div><span style="font-size: 10pt; ">public class Girl implements Servicable {&nbsp;</span></div><div><span style="font-size: 10pt; ">&nbsp; &nbsp; &nbsp;private Kissable kissable;&nbsp;</span></div><div><span style="font-size: 10pt; ">&nbsp; &nbsp; &nbsp;public Girl() {&nbsp;</span></div><div><span style="font-size: 10pt; ">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;kissable = new Boy();&nbsp;</span></div><div><span style="font-size: 10pt; ">&nbsp; &nbsp; &nbsp;}&nbsp;</span></div><div><span style="font-size: 10pt; ">&nbsp; &nbsp; &nbsp;public void kissYourKissable() {&nbsp;</span></div><div><span style="font-size: 10pt; ">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;kissable.kiss();&nbsp;</span></div><div><span style="font-size: 10pt; ">&nbsp; &nbsp; &nbsp;}&nbsp;</span></div><div><span style="font-size: 10pt; ">}</span></div><div></div><div><span style="font-size: 10pt; ">Girl自己建立自己的Boy，很难更换，很难共享给别人，只能单独使用，并负责完全的生命周期。</span></div><div></div><div></div><div><span style="font-size: 10pt; ">IOC type 1，先看代码：代码</span></div><div></div><div></div><div><span style="font-size: 10pt; ">public class Girl implements Servicable {&nbsp;</span></div><div></div><div><span style="font-size: 10pt; ">&nbsp; &nbsp; Kissable kissable;&nbsp;</span></div><div></div><div><span style="font-size: 10pt; ">&nbsp; &nbsp; &nbsp;public void service(ServiceManager mgr) {&nbsp;</span></div><div><span style="font-size: 10pt; ">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;kissable = (Kissable) mgr.lookup(&#8220;kissable&#8221;);&nbsp;</span></div><div><span style="font-size: 10pt; ">&nbsp; &nbsp; }&nbsp;</span></div><div></div><div><span style="font-size: 10pt; ">&nbsp; &nbsp; &nbsp;public void kissYourKissable() {&nbsp;</span></div><div><span style="font-size: 10pt; ">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;kissable.kiss();&nbsp;</span></div><div><span style="font-size: 10pt; ">&nbsp; &nbsp; &nbsp;}&nbsp;</span></div><div></div><div><span style="font-size: 10pt; ">}</span></div><div></div><div></div><div><span style="font-size: 10pt; ">这种情况出现于Avalon Framework。一个组件实现了Servicable接口，就必须实现service方法，并传入一个ServiceManager。其中会含有需要的其它组件。只需要在service方法中初始化需要的Boy。</span></div><div></div><div></div><div><span style="font-size: 10pt; ">另外，J2EE中从Context取得对象也属于type 1。它依赖于配置文件。</span></div><div></div><div></div><div><span style="font-size: 10pt; ">IOC type 2：</span></div><div></div><div></div><div><span style="font-size: 10pt; ">public class Girl {&nbsp;</span></div><div></div><div><span style="font-size: 10pt; ">&nbsp; &nbsp; &nbsp;private Kissable kissable;&nbsp;</span></div><div></div><div><span style="font-size: 10pt; ">&nbsp; &nbsp; &nbsp;public void setKissable(Kissable kissable) {&nbsp;</span></div><div><span style="font-size: 10pt; ">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;this.kissable = kissable;&nbsp;</span></div><div><span style="font-size: 10pt; ">&nbsp; &nbsp; }&nbsp;</span></div><div></div><div><span style="font-size: 10pt; ">&nbsp; &nbsp; public void kissYourKissable() {&nbsp;</span></div><div><span style="font-size: 10pt; ">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;kissable.kiss();&nbsp;</span></div><div><span style="font-size: 10pt; ">&nbsp; &nbsp; }&nbsp;</span></div><div></div><div><span style="font-size: 10pt; ">}</span></div><div></div><div></div><div><span style="font-size: 10pt; ">Type 2出现于Spring Framework，是通过JavaBean的set方法来将需要的Boy传递给Girl。它必须依赖于配置文件。</span></div><div></div><div><span style="font-size: 10pt; ">IOC type 3:</span></div><div></div><div></div><div><span style="font-size: 10pt; ">public class Girl {&nbsp;</span></div><div></div><div><span style="font-size: 10pt; ">&nbsp; &nbsp; private Kissable kissable;&nbsp;</span></div><div></div><div><span style="font-size: 10pt; ">&nbsp; &nbsp; public Girl(Kissable kissable) {&nbsp;</span></div><div><span style="font-size: 10pt; ">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;this.kissable = kissable;&nbsp;</span></div><div><span style="font-size: 10pt; ">&nbsp; &nbsp; }&nbsp;</span></div><div></div><div><span style="font-size: 10pt; ">&nbsp; &nbsp; &nbsp;public void kissYourKissable() {&nbsp;</span></div><div><span style="font-size: 10pt; ">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;kissable.kiss();&nbsp;</span></div><div><span style="font-size: 10pt; ">&nbsp; &nbsp; }&nbsp;</span></div><div></div><div><span style="font-size: 10pt; ">}</span></div><div></div><div></div><div><span style="font-size: 10pt; ">这就是PicoContainer的组件 。通过构造函数传递Boy给Girl&nbsp;</span></div><div></div><div><span style="font-size: 10pt; ">PicoContainer container = new DefaultPicoContainer();&nbsp;</span></div><div><span style="font-size: 10pt; ">container.registerComponentImplementation(Boy.class);&nbsp;</span></div><div><span style="font-size: 10pt; ">container.registerComponentImplementation(Girl.class);&nbsp;</span></div><div><span style="font-size: 10pt; ">Girl girl = (Girl) container.getComponentInstance(Girl.class);&nbsp;</span></div><div><span style="font-size: 10pt; ">girl.kissYourKissable();</span></div><div></div><div><span style="font-size: 10pt; ">参考资料&nbsp;</span></div><div></div><div><span style="font-size: 10pt; ">1 http://www.picocontainer.org/presentations/JavaPolis2003.ppt&nbsp;</span></div><div><span style="font-size: 10pt; ">&nbsp; http://www.picocontainer.org/presentations/JavaPolis2003.pdf&nbsp;</span></div><div></div><div><span style="font-size: 10pt; ">2 DIP， Robert C Martin, Bob大叔的优秀论文&nbsp;</span></div><div><span style="font-size: 10pt; ">&nbsp; &nbsp;http://www.objectmentor.com/resources/articles/dip.pdf&nbsp;</span></div><div></div><div><span style="font-size: 10pt; ">3 Dependency Injection 依赖注射，Matrin Fowler对DIP的扩展&nbsp;</span></div><div><span style="font-size: 10pt; ">&nbsp; &nbsp;http://www.martinfowler.com/articles/injection.html&nbsp;</span></div><div></div><div><span style="font-size: 10pt; ">4 IOC框架&nbsp;</span></div><div></div><div><span style="font-size: 10pt; ">PicoContainer 优秀的IOC框架&nbsp;</span></div><div><span style="font-size: 10pt; ">http://picocontainer.org/&nbsp;</span></div><div></div><div><span style="font-size: 10pt; ">Avalon&nbsp;</span></div><div><span style="font-size: 10pt; ">http://avalon.apache.org/&nbsp;</span></div><div></div><div><span style="font-size: 10pt; ">Spring Framework&nbsp;</span></div><div><span style="font-size: 10pt; ">http://www.springframework.org/&nbsp;</span></div><div></div><div><span style="font-size: 10pt; ">HiveMind&nbsp;</span></div><div><span style="font-size: 10pt; ">http://jakarta.apache.org/commons/hivemind&nbsp;</span></div><div>分类: C#</div></div><div style="text-align: left;"></div><pre style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; white-space: pre-wrap; word-wrap: break-word; color: #393939; font-size: 14px; font-weight: normal; text-align: left; background-color: #faf7ef; "><h1><a id="cb_post_title_url" href="http://www.cnblogs.com/jazzka702/archive/2009/06/17/1504756.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; color: #ff6600; text-decoration: none; ">什么叫IOC（编程术语</a></h1><div style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; clear: both; color: #464646; font-family: verdana, 'ms song', Arial, Helvetica, sans-serif; "></div><div style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 5px; padding-right: 2px; padding-bottom: 5px; padding-left: 5px; line-height: 1.5; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: black; font-family: verdana, 'ms song', Arial, Helvetica, sans-serif; "><div id="cnblogs_post_body" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; "><pre style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; white-space: pre-wrap; word-wrap: break-word; "><br /><br /><br /></pre></div><div id="blog_post_info_block" style="margin-top: 20px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; "><div id="blog_post_info" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; "></div></div></div></pre></h1><img src ="http://www.cppblog.com/lauer3912/aggbug/174960.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-05-15 13:11 <a href="http://www.cppblog.com/lauer3912/archive/2012/05/15/174960.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> otool 与dylib</title><link>http://www.cppblog.com/lauer3912/archive/2012/05/07/173940.html</link><dc:creator>RTY</dc:creator><author>RTY</author><pubDate>Mon, 07 May 2012 15:25:00 GMT</pubDate><guid>http://www.cppblog.com/lauer3912/archive/2012/05/07/173940.html</guid><wfw:comment>http://www.cppblog.com/lauer3912/comments/173940.html</wfw:comment><comments>http://www.cppblog.com/lauer3912/archive/2012/05/07/173940.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lauer3912/comments/commentRss/173940.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lauer3912/services/trackbacks/173940.html</trackback:ping><description><![CDATA[<blockquote style="font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">Shared by eric&nbsp;<br />mac osx的dyld是挺烦的</blockquote><p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">所谓dylib，就是bsd风格的动态库。基本可以认为等价于windows的dll和linux的so。mac基于bsd，所以也使用的是dylib。</p><p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">如果你需要引用一个第三方的dylib，在xcode下编译出cocoa程序，在本机上运行是不会出问题的。但是发布出来，给其他用户用，就可能出问题。因为用户不一定有这个库。</p><p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">这个问题给我造成了相当的困扰，我到现在也没找到正规的方法。但是我确实解决了这个问题，虽然方法不一定正宗。不管怎么说，写下来，<strong>如果暂时没有更好的办法，那么先这样做。如果谁有更好的办法，也请一定不吝留言或邮件给我</strong>。</p><p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">我的办法是这样的:</p><p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; "><br /><strong>1 otool -L yourapp.app/Contents/MacOS/yourapp</strong></p><p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">这一步的意思是对你编译出的app使用otool命令，以便获得依赖哪些dylib的信息。注意这个路径。cocoa的app在命令行下表现为目录。所有相关的东西都在里面。<br />结果如下所示：<br />yourapp.app/Contents/MacOS/yourapp:<br />/System/Library/Frameworks/Cocoa.framework/Versions/A/Cocoa (compatibility version 1.0.0, current version 12.0.0)<br /><span style="color: #ff0000; ">/usr/local/lib/lib01.dylib (compatibility version 0.0.0, current version 0.1.0)</span><br /><span style="color: #ff0000; ">/usr/local/lib/lib02.dylib (compatibility version 0.0.0, current version 0.1.0)</span><br /><span style="color: #ff0000; ">/usr/local/lib/lib03.dylib (compatibility version 0.0.0, current version 0.1.0)</span><br />/usr/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 1.0.0)<br />/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 111.0.0)<br />/usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 227.0.0)<br />/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 476.0.0)<br />/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit (compatibility version 45.0.0, current version 949.0.0)<br />/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (compatibility version 300.0.0, current version 677.12.0)</p><p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">注意我标红的地方。假如lib01,lib02,lib03是本程序引用的第三方库，那么在这个程序里面，他们的引用地址是位于/usr/local/lib上的。这是开发机上的安装情况。而使用这个程序的客户机未必安装这些东西，所以程序就要出错。</p><p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">显然，我们需要做2件事。a 把这些库附带在app上 b 把他们的引用地址修改到正确的位置。</p><p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; "><strong>2 mkdir yourapp.app/Contents/dylib</strong></p><p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">在编译出来的app中，创建dylib目录</p><p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">然后把所有需要的库复制过去</p><p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">cp /usr/local/lib/lib01.dylib yourapp.app/Contents/dylib/<br />cp /usr/local/lib/lib02.dylib yourapp.app/Contents/dylib/<br />cp /usr/local/lib/lib03.dylib yourapp.app/Contents/dylib/</p><p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; "><strong>3 install_name_tool -change /usr/local/lib/lib01.dylib @loader_path/../dylib/lib01.dylib "yourapp.app/Contents/MacOS/yourapp"<br /></strong></p><p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">install_name_tool 是苹果提供的用来修改dylib安装名称的命令。这个命令执行之后，再用otool -L 就可以看到变化了</p><p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">yourapp.app/Contents/MacOS/yourapp:<br />/System/Library/Frameworks/Cocoa.framework/Versions/A/Cocoa (compatibility version 1.0.0, current version 12.0.0)<br /><span style="color: #ff0000; ">@loader_path/../dylib/lib01.dylib (compatibility version 0.0.0, current version 0.1.0)</span><br />/usr/local/lib/lib02.dylib (compatibility version 0.0.0, current version 0.1.0)<br />/usr/local/lib/lib03.dylib (compatibility version 0.0.0, current version 0.1.0)<br />/usr/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 1.0.0)<br />/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 111.0.0)<br />/usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 227.0.0)<br />/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 476.0.0)<br />/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit (compatibility version 45.0.0, current version 949.0.0)<br />/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (compatibility version 300.0.0, current version 677.12.0)</p><p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">注意标红的位置。已经变化了。@loader_path 指的是应用程序运行的位置，也就是yourapp.app/Contents/MacOS/yourapp，所以要用一个..，以便定位到第2步创建的dylib目录。<br />重复这个命令，修改lib02,lib03</p><p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; "><br /><strong>4 otool -L yourapp.app/Contents/dylib/*.dylib</strong></p><p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">继续用otool 来检查dylib下面使用的第三方库是否还有其他依赖，install_name是否正确，重复1，2，3的步骤，把所需要的dylib复制过来，修改install_name。</p><p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">如果都改对了，那么这个app就附带上了dylib，可以在其他机器上正确运行了，不用非要寻找/usr/local/lib下面的库了。</p><p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; "><br />刚才我们修改的结果是一个build的结果。当然，每次build都这么折腾一下很麻烦。所以继续这样做：</p><p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">1 前面的步骤得到了一个完整的dylib目录。把这个dylib复制一份备用。比如放在你的xcode项目下面。</p><p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">2 编写一个脚本：</p><p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">mkdir "$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Contents/dylib"<br />cp -f /your/path/to/xcode_project_name/dylib/*.dylib "$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Contents/dylib/"</p><p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">install_name_tool -change /usr/local/lib/lib01.dylib @loader_path/../dylib/lib01.dylib "$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Contents/MacOS/$PRODUCT_NAME"<br />(用这个格式重复前面对app使用过的dylib)</p><p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">3 在xcode中，展开targets节点，右键点工程名称，在菜单中选Add-&gt;New Build Phasa-&gt;New Run Script Build Phasa，在打开的对话框里面，把刚才的脚本贴进去。如图所示。</p><p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">这个脚本会在build之后自动运行。不过我这里有个奇怪的问题，如果Shell里面写了/bin/sh，会报告找不到这个文件(实际上存在)，而让shell为空，反而可以正确的运行shell命令。<br /><a href="http://picasaweb.google.com/lh/photo/FwVjnl4gM7iRUH5yMbTXQA?feat=embedwebsite" style="color: #336699; text-decoration: none; "><img src="http://lh4.ggpht.com/_fMpSudYvJOo/Skq16lg_8yI/AAAAAAAAAhw/5PAtplmUj_Q/s400/Picture%205.png" alt="" style="border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; border-image: initial; " /></a></p><p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; "><a href="http://picasaweb.google.com/lh/photo/PO8ZsBhdcFBWogeMuu2cmQ?feat=embedwebsite" style="color: #336699; text-decoration: none; "><img src="http://lh6.ggpht.com/_fMpSudYvJOo/Skq16mBp44I/AAAAAAAAAh0/ASKXHFDGems/s400/Picture%206.png" alt="" style="border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; border-width: initial; border-color: initial; border-image: initial; " /></a></p><p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">经过这些处理，每次编译出来的app就可以拿到其他机器上运行了。可真够麻烦的...</p><img src ="http://www.cppblog.com/lauer3912/aggbug/173940.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-05-07 23:25 <a href="http://www.cppblog.com/lauer3912/archive/2012/05/07/173940.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>vc调试窗口表达式格式化资料</title><link>http://www.cppblog.com/lauer3912/archive/2012/04/24/172670.html</link><dc:creator>RTY</dc:creator><author>RTY</author><pubDate>Tue, 24 Apr 2012 14:37:00 GMT</pubDate><guid>http://www.cppblog.com/lauer3912/archive/2012/04/24/172670.html</guid><wfw:comment>http://www.cppblog.com/lauer3912/comments/172670.html</wfw:comment><comments>http://www.cppblog.com/lauer3912/archive/2012/04/24/172670.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/lauer3912/comments/commentRss/172670.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lauer3912/services/trackbacks/172670.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 2011-04-15 11:09&nbsp;46人阅读&nbsp;评论(0)&nbsp;收藏&nbsp;举报摘自msdn，列在这里方便查阅。The following tables show the format specifiers recognized by the debugger.&nbsp;SpecifierFormatExpressionValue Displayedd,isigned...&nbsp;&nbsp;<a href='http://www.cppblog.com/lauer3912/archive/2012/04/24/172670.html'>阅读全文</a><img src ="http://www.cppblog.com/lauer3912/aggbug/172670.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-04-24 22:37 <a href="http://www.cppblog.com/lauer3912/archive/2012/04/24/172670.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>@executable path, @load path and @rpath</title><link>http://www.cppblog.com/lauer3912/archive/2012/04/18/171814.html</link><dc:creator>RTY</dc:creator><author>RTY</author><pubDate>Tue, 17 Apr 2012 22:14:00 GMT</pubDate><guid>http://www.cppblog.com/lauer3912/archive/2012/04/18/171814.html</guid><wfw:comment>http://www.cppblog.com/lauer3912/comments/171814.html</wfw:comment><comments>http://www.cppblog.com/lauer3912/archive/2012/04/18/171814.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lauer3912/comments/commentRss/171814.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lauer3912/services/trackbacks/171814.html</trackback:ping><description><![CDATA[<h1>@executable path, @load path and @rpath</h1> <div> <span title="2010年11月6日 02:26:36">2010年11月6日</span> by <a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#119;&#105;&#110;&#64;&#119;&#105;&#110;&#99;&#101;&#110;&#116;&#46;&#99;&#111;&#109;">Wincent Colaiuta</a> </div>  <div> <p><strong>Note:</strong> <em>this article is actually about the <code>@executable_path</code>, <code>@load_path</code> and <code>@rpath</code> install paths used by the linker on <a href="https://wincent.com/wiki/Mac_OS_X">Mac OS X</a>; wiki titles can't include underscores, however, because they are ambiguous with spaces.</em></p> <h2>Absolute paths</h2> <p>Useful for frameworks installed in shared locations. Example:</p> <ul> <li>Install path: <code>/Library/Frameworks/Foo.framework/Versions/A/Foo</code></li> </ul> <h2><code>@executable_path</code></h2> <p>Useful for frameworks embedded inside applications, because it allows  you to specify the location of the framework relative to the  application's executable:</p> <ul> <li>Install path: <code>@executable_path/../Frameworks/Foo.framework/Versions/A/Foo</code></li> <li>Application location: <code>/Applications/Foo.app</code></li> <li>Executable path: <code>/Applications/Foo.app/Contents/MacOS</code></li> <li>Framework location: <code>/Applications/Foo.app/Contents/Frameworks/Foo.framework</code></li> <li>Linker puts all this together to figure out that the framework binary can be found at: <code>/Applications/Foo.app/Contents/MacOS/../Frameworks/Foo.framework/Versions/A/Foo</code></li> </ul> <h2>@loader_path</h2> <p>Available from <a href="https://wincent.com/wiki/Mac_OS_X">Mac OS X</a> 10.4 <a href="https://wincent.com/wiki/Tiger">Tiger</a>  onwards; useful for frameworks embedded inside plug-ins, because it  allows you to specify the location of the framework relative to the  plug-in's code (remember, plug-ins may not actually know where they are  going to be installed, relative to the application, so knowing <code>@executable_path</code> doesn't help us in this case):</p> <ul> <li>Install path: <code>@loader_path/../Frameworks/Foo.framework/Versions/A/Foo</code></li> <li>Application location: <code>/Applications/Foo.app</code></li> <li>Plug-in location: <code>/Library/Application Support/Foo/Plug-Ins/Bar.bundle</code></li> <li>Executable path: <code>/Applications/Foo.app/Contents/MacOS</code></li> <li>Loader path: <code>/Library/Application Support/Foo/Plug-Ins/Bar.bundle/Contents/MacOS</code></li> <li>Framework location: <code>/Library/Application Support/Foo/Plug-Ins/Bar.bundle/Contents/Frameworks/Foo.framework</code></li> <li>Linker puts all this together to figure out that the framework binary can be found at: <code>/Library/Application Support/Foo/Plug-Ins/Bar.bundle/Contents/MacOS/../Frameworks/Foo.framework/Versions/A/Foo</code></li> </ul> <p>Note that if the "loader" is an application rather than a plug-in, the <code>@loader_path</code> ends up being equivalent to <code>@executable_path</code>.</p> <h2><code>@rpath</code></h2> <p>New in <a href="https://wincent.com/wiki/Mac_OS_X">Mac OS X</a> 10.5 <a href="https://wincent.com/wiki/Leopard">Leopard</a> is <code>@rpath</code>. Key points:</p> <ul> <li><code>@rpath</code> instructs the dynamic linker to search a list of paths in order to locate the framework</li> <li>critically, this list is embedded in the loading application</li> <li>this means that a single framework with <code>@rpath/Foo.framework/Versions/A/Foo</code>  can be made to work in a number of different ways; that is, you are  effectively no longer limited by the choice of specifying your "install  path" using <em>either</em> <code>@executable_path</code> <em>or</em> <code>@loader_path</code></li> <li>the down side: you now have to pass additional linker flags when building the host application (eg. <code>-rpath @executable_path/../Frameworks</code> or <code>/Library/Frameworks</code>; note that specifying <em>both</em> will cause the dynamic linker to try looking in both locations)</li> </ul> <h2>Sources</h2> <ul> <li>Nice overview: <a href="http://www.mikeash.com/pyblog/friday-qa-2009-11-06-linking-and-install-names.html">http://www.mikeash.com/pyblog/friday-qa-2009-11-06-linking-and-install-names.html</a></li> <li>Docs for using PFiddlesoft Frameworks (I've never used these, but  the manual itself makes some nice general points about using  frameworks): <a href="http://pfiddlesoft.com/frameworks/downloads/Assistive_Application_Programming_Guide.pdf">http://pfiddlesoft.com/frameworks/downloads/Assistive_Application_Programming_Guide.pdf</a></li> </ul>  </div> <div> <h2>Tags</h2> <a href="https://wincent.com/tags/mac.os.x" style="font-size: 1.3244274809160306em;" title="85 items tagged with 'mac.os.x'">mac.os.x</a> <a href="https://wincent.com/tags/dynamic.linker" style="font-size: 1.0038167938931297em;" title="1 item tagged with 'dynamic.linker'">dynamic.linker</a></div><img src ="http://www.cppblog.com/lauer3912/aggbug/171814.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-04-18 06:14 <a href="http://www.cppblog.com/lauer3912/archive/2012/04/18/171814.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>error C2143: syntax error : missing ';' before 'type' in Visual C++ 可能的原因</title><link>http://www.cppblog.com/lauer3912/archive/2012/04/05/170196.html</link><dc:creator>RTY</dc:creator><author>RTY</author><pubDate>Thu, 05 Apr 2012 11:53:00 GMT</pubDate><guid>http://www.cppblog.com/lauer3912/archive/2012/04/05/170196.html</guid><wfw:comment>http://www.cppblog.com/lauer3912/comments/170196.html</wfw:comment><comments>http://www.cppblog.com/lauer3912/archive/2012/04/05/170196.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lauer3912/comments/commentRss/170196.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lauer3912/services/trackbacks/170196.html</trackback:ping><description><![CDATA[<p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">今天偶然写了下面的程序(原来我写的程序不一样，下面的只是为了把问题简化)</p><p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">&nbsp;</p><p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; "></p><div bg_cpp"="" style="width: 687px; "><div><div><strong>[cpp]</strong>&nbsp;<a href="http://blog.csdn.net/cdjogh/article/details/5961742#" title="view plain" style="background-image: url(http://static.blog.csdn.net/scripts/SyntaxHighlighter/styles/images/default/ico_plain.gif); padding-top: 1px; padding-right: 1px; padding-bottom: 1px; padding-left: 1px; display: inline-block; width: 16px; height: 16px; text-indent: -2000px; background-position: 0% 0%; background-repeat: no-repeat no-repeat; ">view plain</a><a href="http://blog.csdn.net/cdjogh/article/details/5961742#" title="copy" style="background-image: url(http://static.blog.csdn.net/scripts/SyntaxHighlighter/styles/images/default/ico_copy.gif); padding-top: 1px; padding-right: 1px; padding-bottom: 1px; padding-left: 1px; display: inline-block; width: 16px; height: 16px; text-indent: -2000px; background-position: 0% 0%; background-repeat: no-repeat no-repeat; ">copy</a><div style="position: absolute; left: 557px; top: 420px; width: 18px; height: 18px; z-index: 99; border-image: initial; "><embed id="ZeroClipboardMovie_1" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" loop="false" menu="false" quality="best" bgcolor="#ffffff" width="18" height="18" name="ZeroClipboardMovie_1" align="center" allowscriptaccess="always" allowfullscreen="false" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" flashvars="id=1&amp;width=18&amp;height=18" wmode="transparent"></div></div></div><ol start="1"><li style="border-width: initial; border-color: initial; line-height: 18px; ">void&nbsp;foo()&nbsp;&nbsp;</li><li style="border-width: initial; border-color: initial; line-height: 18px; ">{&nbsp;&nbsp;</li><li style="border-width: initial; border-color: initial; line-height: 18px; ">&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #2e8b57; font-weight: bold; ">int</span>&nbsp;p&nbsp;=&nbsp;0;&nbsp;&nbsp;</li><li style="border-width: initial; border-color: initial; line-height: 18px; ">&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(&nbsp;p&nbsp;==&nbsp;0&nbsp;)&nbsp;&nbsp;</li><li style="border-width: initial; border-color: initial; line-height: 18px; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #2e8b57; font-weight: bold; ">int</span>&nbsp;i&nbsp;=&nbsp;0;&nbsp;&nbsp;</li><li style="border-width: initial; border-color: initial; line-height: 18px; ">&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #2e8b57; font-weight: bold; ">int</span>&nbsp;a;&nbsp;&nbsp;</li><li style="border-width: initial; border-color: initial; line-height: 18px; ">}&nbsp;&nbsp;</li><li style="border-width: initial; border-color: initial; line-height: 18px; ">&nbsp;&nbsp;</li><li style="border-width: initial; border-color: initial; line-height: 18px; "><span style="color: #2e8b57; font-weight: bold; ">int</span>&nbsp;main()&nbsp;&nbsp;</li><li style="border-width: initial; border-color: initial; line-height: 18px; ">{&nbsp;&nbsp;</li><li style="border-width: initial; border-color: initial; line-height: 18px; ">&nbsp;&nbsp;&nbsp;&nbsp;foo();&nbsp;&nbsp;</li><li style="border-width: initial; border-color: initial; line-height: 18px; ">}&nbsp;&nbsp;</li></ol></div>&nbsp;&nbsp;<span style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">本文来自CSDN博客，转载请标明出处：</span><a href="http://blog.csdn.net/fancylea/archive/2009/06/10/4256793.aspx" style="color: #336699; text-decoration: none; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">http://blog.csdn.net/fancylea/archive/2009/06/10/4256793.aspx</a>&nbsp;<br /><p>&nbsp;</p><p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">&nbsp;</p><p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">不幸的是偶然将这个文件保存成了test.c，然后编译的时候出现了</p><p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">&nbsp;</p><p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">error， error C2143: syntax error : missing ';' before 'type'</p><p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">&nbsp;</p><p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">感觉很奇怪,细细看来所有的语法都似乎都是对的，更奇怪的是把文件改成cpp或者cc能正常编译，把int a;和if调换下也能正常编译。这样的错误可能发生在当变量的声明放在可执行代码之后。而这个是在K&amp;R C中规定的，但在ANSI C中废除。</p><p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">MSDN (<a href="http://support.microsoft.com/kb/58559" style="color: #336699; text-decoration: none; ">http://support.microsoft.com/kb/58559</a>)给出的解释如下：</p><p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">&nbsp;</p><hr style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">&nbsp;</p><p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">In Microsoft C, compiler errors C2143 and C2144 are defined as follows:</p><div style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; "><div><div>&nbsp;&nbsp;&nbsp; C2143: syntax error : missing 'token1' before 'token2'<br />&nbsp;&nbsp;&nbsp; C2144: syntax error : missing 'token' before type 'type'</div></div></div><p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">&nbsp;</p><p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">You may receive this error message if your program places executable code before a data declaration, an acceptable practice in Kernighan-and-Ritchie C. This practice has been outlawed in later versions of the ANSI drafts.&nbsp;<br /><br />This error message will normally occur if a required closing curly brace (}), right parenthesis [)], or semicolon (;) is missing.</p><hr style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">&nbsp;</p><p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">注: The C Programming Language的作者简称K&amp;R，也是C语言之父, 经常用K&amp;R C来和ANSI C做对比。这本书的第二版已经用ANSI.</p><p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">我用的编译器是VS2008, 看来微软向来无视标准。</p><p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">&nbsp;</p><p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">总结：</p><p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">在 ANSI C或者C++中，在可执行代码中随时定义变量是允许的，但是在K&amp;R C中是不允许的，VS2008实现的C竟然是K&amp;R C。注意这样的错误也体现在VS中要是用for (int i = 0; i++; i&lt;10)同时你的文件名是.c的也会出现这样的错误。</p><p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">&nbsp;</p><p style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">Code complete中讨论过变量名的最迟绑定有利于增加代码的可读性等。所以在VS中写c要注意了。</p><img src ="http://www.cppblog.com/lauer3912/aggbug/170196.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-04-05 19:53 <a href="http://www.cppblog.com/lauer3912/archive/2012/04/05/170196.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++查缺补漏4，赶紧的</title><link>http://www.cppblog.com/lauer3912/archive/2012/04/05/170159.html</link><dc:creator>RTY</dc:creator><author>RTY</author><pubDate>Thu, 05 Apr 2012 06:54:00 GMT</pubDate><guid>http://www.cppblog.com/lauer3912/archive/2012/04/05/170159.html</guid><wfw:comment>http://www.cppblog.com/lauer3912/comments/170159.html</wfw:comment><comments>http://www.cppblog.com/lauer3912/archive/2012/04/05/170159.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lauer3912/comments/commentRss/170159.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lauer3912/services/trackbacks/170159.html</trackback:ping><description><![CDATA[<div id="cnblogs_post_body" style="line-height: 1.8; color: #333333; font-family: Verdana,Arial,sans-serif,'Lucida Grande'; font-size: 13px; text-align: left; background-color: #d6d3d6;"><p><span style="color: #0000ff; font-family: 楷体_GB2312; font-size: medium; ">请说出const与#define 相比，有何优点？<br />答案：</span><span style="color: #0000ff; font-family: 楷体_GB2312; font-size: medium; ">1）&nbsp;<strong>const 常量有数据类型，而宏常量没有数据类型</strong>。编译器可以对前者进行类型<strong>安全检查</strong>。而对后者只进行字符替换，没有类型安全检查，并且在字符替换可能会产生意料不到的错误。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2）&nbsp;<strong>有些集成化的调试工具可以对const 常量进行调试</strong>，但是不能对宏常量进行调试。</span></p><p><span style="color: #0000ff; font-family: 楷体_GB2312; font-size: medium; ">在8086 汇编下，逻辑地址和物理地址是怎样转换的？（Intel）<br />答案：通用寄存器给出的地址，是段内偏移地址，相应段寄存器地址*10H+通用寄存器内地址，就得到了真正要访问的地址。</span></p><p><span style="color: #0000ff; font-family: 楷体_GB2312; font-size: medium; ">当一个类A 中没有生命任何成员变量与成员函数,这时sizeof(A)的值是多少，如果不是零，请解释一下编译器为什么没有让它为零。（Autodesk）<br />答案：肯定不是零。举个反例，如果是零的话，声明一个class A[10]对象数组，而每一个对象占用的空间是零，这时就没办法区分A[0],A[1]&#8230;了。</span></p><p><span style="color: #0000ff; font-family: 楷体_GB2312; font-size: medium; ">&nbsp;描述内存分配方式以及它们的区别?<br />1）<strong>&nbsp;从静态存储区域分配</strong>。内存在程序编译的时候就已经分配好，这块内存在程序的整个运行期间都存在。例如<strong>全局变量，static 变量</strong>。<br />2）&nbsp;<strong>在栈上创建</strong>。在执行函数时，<strong>函数内局部变量的存储单元都可以在栈上创建</strong>，函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集。<br />3）&nbsp;<strong>从堆上分配</strong>，<strong>亦称动态内存分配</strong>。程序在运行的时候用malloc 或new 申请任意多少的内存，程序员自己负责在何时用free 或delete 释放内存。动态内存的生存期由程序员决定，使用非常灵活，但问题也最多。</span></p><p><span style="color: #0000ff; font-family: 楷体_GB2312; font-size: medium; ">&nbsp;main 函数执行以前，还会执行什么代码？<br />答案：全局对象的构造函数会在main 函数之前执行。</span></p><p><span style="color: #0000ff; font-family: 楷体_GB2312; font-size: medium; ">&nbsp;C++是不是类型安全的？<br />答案：不是。两个不同类型的指针之间可以强制转换（用reinterpret cast)。C#是类型安全的。</span></p><p>&nbsp;</p><p><span style="color: #3300ff; font-family: 楷体_GB2312; font-size: medium; ">有哪几种情况只能用intialization list 而不能用assignment?</span></p><p>&nbsp;</p><p><span style="color: #3300ff; font-family: 楷体_GB2312; font-size: medium; ">答案：当类中含有const、reference 成员变量；基类的构造函数都需要初始化表。</span></p><p><span style="color: #3300ff; font-family: 楷体_GB2312; font-size: medium; ">define DOUBLE(x) x+x ，i = 5*DOUBLE(5)； i 是多少？<br />答案：i 为30</span></p><p><span style="color: #3300ff; font-family: 楷体_GB2312; font-size: medium; ">New delete 与malloc free 的联系与区别?<br />答案：都是在堆(heap)上进行动态的内存操作。用malloc函数需要指定内存分配的字节数并且不能初始化对象，new 会自动调用对象的构造函数。delete 会调用对象的destructor，而free 不会调用对象的destructor.</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">1、写一个&#8220;标准&#8221;宏，这个宏输入两个参数并返回较小的一个。<br />.#define Min(X, Y) ((X)&gt;(Y)?(Y):(X))//结尾没有;<br />2、嵌入式系统中经常要用到无限循环，你怎么用C编写死循环。<br />while(1){}或者for(;;)<br />3、关键字static的作用是什么？<br />定义静态变量<br />4、关键字const有什么含意？<br />表示常量不可以修改的变量。<br />5、关键字volatile有什么含意？并举出三个不同的例子？<br />提示编译器对象的值可能在编译器未监测到的情况下改变。</span></p><p>&nbsp;</p><p><br /><span style="color: #4822dd; ">int (*s[10])(int) 表示的是什么啊<br />int (*s[10])(int) 函数指针数组，每个指针指向一个int func(int param)的函数。</span></p><p>&nbsp;</p><p><br /><span style="color: #4822dd; ">1.有以下表达式：<br />int a=248; b=4;int const c=21;const int *d=&amp;a;<br />int *const e=&amp;b;int const *f const =&amp;a;<br />请问下列表达式哪些会被编译器禁止？为什么？<br />*c=32;d=&amp;b;*d=43;e=34;e=&amp;a;f=0x321f;<br />*c 这是个什么东东，禁止<br />*d 说了是const， 禁止<br />e = &amp;a 说了是const 禁止<br />const *f const =&amp;a; 禁止<br />2.交换两个变量的值，不使用第三个变量。即a=3,b=5,交换之后a=5,b=3;<br />有两种解法, 一种用算术算法, 一种用^(异或)<br />a = a + b;<br />b = a - b;<br />a = a - b;&nbsp;<br />or<br />a = a^b;// 只能对int,char..<br />b = a^b;<br />a = a^b;<br />or<br />a ^= b ^= a;<br />3.c和c++中的struct有什么不同？<br />c和c++中struct的主要区别是c中的struct不可以含有成员函数，而c++中的struct可以。c++中struct和class的主要区别在于默认的存取权限不同，struct默认为public，而class默认为private<br />4.＃i nclude &lt;stdio.h&gt;<br />＃i nclude &lt;stdlib.h&gt;<br />void getmemory(char *p)<br />{<br />&nbsp;&nbsp;&nbsp; p=(char *) malloc(100);<br />&nbsp;&nbsp;&nbsp; strcpy(p,"hello world");<br />}<br />int main( )<br />{<br />&nbsp;&nbsp;&nbsp; char *str=NULL;<br />&nbsp;&nbsp;&nbsp; getmemory(str);<br />&nbsp;&nbsp;&nbsp; printf("%s/n",str);<br />&nbsp;&nbsp;&nbsp; free(str);<br />&nbsp;&nbsp;&nbsp; return 0;<br />&nbsp;&nbsp; }<br />程序崩溃，getmemory中的malloc 不能返回动态内存， free（）对str操作很危险<br />5.char szstr[10];<br />strcpy(szstr,"0123456789");<br />产生什么结果？为什么？<br />长度不一样，会造成非法的OS<br />6.列举几种进程的同步机制，并比较其优缺点。<br />&nbsp;&nbsp; 原子操作&nbsp;<br />信号量机制<br />&nbsp;&nbsp; 自旋锁<br />&nbsp;&nbsp; 管程，会合，分布式系统</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">7.进程之间通信的途径<br />共享存储系统<br />消息传递系统<br />管道：以文件系统为基础<br />11.进程死锁的原因<br />资源竞争及进程推进顺序非法<br />12.死锁的4个必要条件<br />互斥、请求保持、不可剥夺、环路<br />13.死锁的处理<br />鸵鸟策略、预防策略、避免策略、检测与解除死锁<br />15.&nbsp;&nbsp; 操作系统中进程调度策略有哪几种？<br />FCFS(先来先服务)，优先级，时间片轮转，多级反馈<br />8.类的静态成员和非静态成员有何区别？<br />类的静态成员每个类只有一个，非静态成员每个对象一个<br />9.纯虚函数如何定义？使用时应注意什么？<br />virtual void f()=0;<br />是接口，子类必须要实现<br />10.数组和链表的区别<br />数组：数据顺序存储，固定大小<br />连表：数据可以随机存储，大小可动态改变</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">12.ISO的七层模型是什么？tcp/udp是属于哪一层？tcp/udp有何优缺点？<br />应用层<br />表示层<br />会话层<br />运输层<br />网络层<br />物理链路层<br />物理层<br />tcp /udp属于运输层<br />TCP 服务提供了数据流传输、可靠性、有效流控制、全双工操作和多路复用技术等。<br />与 TCP 不同， UDP 并不提供对 IP 协议的可靠机制、流控制以及错误恢复功能等。由于 UDP 比较简单， UDP 头包含很少的字节，比 TCP 负载消耗少。<br />tcp: 提供稳定的传输服务，有流量控制，缺点是包头大，冗余性不好<br />udp: 不提供稳定的服务，包头小，开销小&nbsp;&nbsp;</span></p><p>&nbsp;</p><p><br /><span style="color: #4822dd; ">1：(void *)ptr 和 (*(void**))ptr的结果是否相同？其中ptr为同一个指针<br />.(void *)ptr 和 (*(void**))ptr值是相同的<br />2：int main()<br />&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp; int x=3;<br />&nbsp;&nbsp;&nbsp; printf("%d",x);<br />&nbsp;&nbsp;&nbsp; return 1;<br />&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp; }<br />问函数既然不会被其它函数调用，为什么要返回1？<br />mian中，c标准认为0表示成功，非0表示错误。具体的值是某中具体出错信息</span></p><p>&nbsp;</p><p><br /><span style="color: #4822dd; ">1，要对绝对地址0x100000赋值，我们可以用<br />(unsigned int*)0x100000 = 1234;<br />那么要是想让程序跳转到绝对地址是0x100000去执行，应该怎么做？<br />*((void (*)( ))0x100000 ) ( );<br />首先要将0x100000强制转换成函数指针,即:<br />(void (*)())0x100000<br />然后再调用它:<br />*((void (*)())0x100000)();<br />用typedef可以看得更直观些:<br />typedef void(*)() voidFuncPtr;<br />*((voidFuncPtr)0x100000)();<br />2，已知一个数组table，用一个宏定义，求出数据的元素个数<br />#define NTBL<br />#define NTBL (sizeof(table)/sizeof(table[0]))</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">面试题: 线程与进程的区别和联系? 线程是否具有相同的堆栈? dll是否有独立的堆栈?<br />进程是死的，只是一些资源的集合，真正的程序执行都是线程来完成的，程序启动的时候操作系统就帮你创建了一个主线程。</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">每个线程有自己的堆栈。<br />DLL中有没有独立的堆栈，这个问题不好回答，或者说这个问题本身是否有问题。因为DLL中的代码是被某些线程所执行，只有线程拥有堆栈，如果DLL中的代码是EXE中的线程所调用，那么这个时候是不是说这个DLL没有自己独立的堆栈？如果DLL中的代码是由DLL自己创建的线程所执行，那么是不是说DLL有独立的堆栈？</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">以上讲的是堆栈，如果对于堆来说，每个DLL有自己的堆，所以如果是从DLL中动态分配的内存，最好是从DLL中删除，如果你从DLL中分配内存，然后在EXE中，或者另外一个DLL中删除，很有可能导致程序崩溃</span></p><p>&nbsp;</p><p><br /><span style="color: #4822dd; ">unsigned short A = 10;<br />printf("~A = %u\n", ~A);</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">char c=128;&nbsp;<br />printf("c=%d\n",c);</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">输出多少？并分析过程<br />第一题，～A ＝0xfffffff5,int值 为－11，但输出的是uint。所以输出4294967285<br />第二题，c＝0x10,输出的是int，最高位为1，是负数，所以它的值就是0x00的补码就是128，所以输出－128。<br />这两道题都是在考察二进制向int或uint转换时的最高位处理。</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">分析下面的程序：<br />void GetMemory(char **p,int num)<br />{<br />&nbsp;&nbsp;&nbsp; *p=(char *)malloc(num);<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />int main()<br />{<br />&nbsp;&nbsp;&nbsp; char *str=NULL;<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp; GetMemory(&amp;str,100);<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp; strcpy(str,"hello");<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp; free(str);<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp; if(str!=NULL)<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strcpy(str,"world");<br />&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp; printf("\n str is %s",str);<br />&nbsp;&nbsp;&nbsp; getchar();<br />}&nbsp;&nbsp;&nbsp;&nbsp;<br />问输出结果是什么？希望大家能说说原因，先谢谢了<br />输出str is world。<br />free 只是释放的str指向的内存空间,它本身的值还是存在的.<br />所以free之后，有一个好的习惯就是将str=NULL.<br />此时str指向空间的内存已被回收,如果输出语句之前还存在分配空间的操作的话,这段存储空间是可能被重新分配给其他变量的,<br />尽管这段程序确实是存在大大的问题（上面各位已经说得很清楚了），但是通常会打印出world来。<br />这是因为，进程中的内存管理一般不是由操作系统完成的，而是由库函数自己完成的。<br />当你malloc一块内存的时候，管理库向操作系统申请一块空间（可能会比你申请的大一些），然后在这块空间中记录一些管理信息（一般是在你申请的内存前面一点），并将可用内存的地址返回。但是释放内存的时候，管理库通常都不会将内存还给操作系统，因此你是可以继续访问这块地址的，只不过。。。。。。。。楼上都说过了，最好别这么干。</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">char a[10],strlen(a)为什么等于15？运行的结果</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">＃i nclude "stdio.h"<br />＃i nclude "string.h"</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">void main()<br />{</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">char aa[10];<br />printf("%d",strlen(aa));<br />}</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">sizeof()和初不初始化，没有关系；<br />strlen()和初始化有关。</span></p><p>&nbsp;</p><p><br /><span style="color: #4822dd; ">char (*str)[20];/*str是一个数组指针，即指向数组的指针．*/<br />char *str[20];/*str是一个指针数组，其元素为指针型数据．*/</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">long a=0x801010;<br />a+5=?<br />0x801010用二进制表示为：&#8220;1000 0000 0001 0000 0001 0000&#8221;，十进制的值为8392720，再加上5就是8392725罗</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">1)给定结构struct A&nbsp;<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char t:4;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char k:4;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned short i:8;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned long m;<br />};问sizeof(A) = ?<br />给定结构struct A&nbsp;<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char t:4; 4位<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char k:4; 4位<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned short i:8; 8位&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned long m; // 偏移2字节保证4字节对齐<br />}; // 共8字节<br />2)下面的函数实现在一个数上加一个数，有什么错误？请改正。<br />int add_n ( int n )<br />{<br />&nbsp;&nbsp;&nbsp; static int i = 100;<br />&nbsp;&nbsp;&nbsp; i += n;<br />&nbsp;&nbsp;&nbsp; return i;<br />}<br />当你第二次调用时得不到正确的结果，难道你写个函数就是为了调用一次？问题就出在 static上？</span></p><p>&nbsp;</p><p><br /><span style="color: #4822dd; ">// 帮忙分析一下<br />＃i nclude&lt;iostream.h&gt;<br />＃i nclude &lt;string.h&gt;<br />＃i nclude &lt;malloc.h&gt;<br />＃i nclude &lt;stdio.h&gt;<br />＃i nclude &lt;stdlib.h&gt;<br />＃i nclude &lt;memory.h&gt;<br />typedef struct AA<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int b1:5;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int b2:2;<br />}AA;<br />void main()<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AA aa;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char cc[100];<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strcpy(cc,"0123456789abcdefghijklmnopqrstuvwxyz");<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; memcpy(&amp;aa,cc,sizeof(AA));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cout &lt;&lt; aa.b1 &lt;&lt;endl;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cout &lt;&lt; aa.b2 &lt;&lt;endl;<br />}<br />答案是 -16和１<br />首先sizeof(AA)的大小为4,b1和b2分别占5bit和2bit.<br />经过strcpy和memcpy后,aa的4个字节所存放的值是:<br />0,1,2,3的ASC码，即00110000,00110001,00110010,00110011<br />所以，最后一步：显示的是这４个字节的前５位，和之后的２位<br />分别为：10000,和01<br />因为int是有正负之分　　所以：答案是-16和１</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">求函数返回值，输入x=9999;&nbsp;<br />int func （ x ）<br />{&nbsp;<br />&nbsp;&nbsp;&nbsp; int countx = 0;&nbsp;<br />&nbsp;&nbsp;&nbsp; while ( x )&nbsp;<br />&nbsp;&nbsp;&nbsp; {&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; countx ++;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; x = x&amp;(x-1);&nbsp;<br />&nbsp;&nbsp;&nbsp; }&nbsp;<br />&nbsp;&nbsp;&nbsp; return countx;&nbsp;<br />}&nbsp;<br />结果呢？<br />知道了这是统计9999的二进制数值中有多少个1的函数，且有<br />9999＝9&#215;1024＋512＋256＋15</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">9&#215;1024中含有1的个数为2；<br />512中含有1的个数为1；<br />256中含有1的个数为1；<br />15中含有1的个数为4；<br />故共有1的个数为8，结果为8。<br />1000 - 1 = 0111，正好是原数取反。这就是原理。<br />用这种方法来求1的个数是很效率很高的。<br />不必去一个一个地移位。循环次数最少。</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">int a,b,c 请写函数实现C=a+b ,不可以改变数据类型,如将c改为long int,关键是如何处理溢出问题<br />bool add (int a, int b,int *c)<br />{<br />*c=a+b;<br />return (a&gt;0 &amp;&amp; b&gt;0 &amp;&amp;(*c&lt;a || *c&lt;b) || (a&lt;0 &amp;&amp; b&lt;0 &amp;&amp;(*c&gt;a || *c&gt;b)));<br />}</span></p><p>&nbsp;</p><p><br /><span style="color: #4822dd; ">分析：<br />struct bit&nbsp;<br />{&nbsp;&nbsp; int a:3;&nbsp;<br />&nbsp;&nbsp;&nbsp; int b:2;&nbsp;<br />&nbsp;&nbsp;&nbsp; int c:3;&nbsp;<br />};&nbsp;<br />int main()&nbsp;<br />{&nbsp;<br />bit s;&nbsp;<br />char *c=(char*)&amp;s;&nbsp;<br />&nbsp;&nbsp; cout&lt;&lt;sizeof(bit)&lt;&lt;endl;<br />*c=0x99;<br />&nbsp;&nbsp; cout &lt;&lt; s.a &lt;&lt;endl &lt;&lt;s.b&lt;&lt;endl&lt;&lt;s.c&lt;&lt;endl;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp; int a=-1;<br />&nbsp;&nbsp; printf("%x",a);<br />return 0;&nbsp;<br />}&nbsp;<br />输出为什么是<br />4<br />1<br />-1<br />-4<br />ffffffff<br />因为0x99在内存中表示为 100 11 001 , a = 001, b = 11, c = 100<br />当c为有符合数时, c = 100, 最高1为表示c为负数，负数在计算机用补码表示，所以c = -4;同理&nbsp;<br />b = -1;<br />当c为有符合数时, c = 100,即 c = 4,同理 b = 3</span></p><p>&nbsp;</p><p><br /><span style="color: #4822dd; ">位域 ：&nbsp;&nbsp;&nbsp;<br />有些信息在存储时，并不需要占用一个完整的字节， 而只需占几个或一个二进制位。例如在存放一个开关量时，只有0和1 两种状态， 用一位二进位即可。为了节省存储空间，并使处理简便，Ｃ语言又提供了一种数据结构，称为&#8220;位域&#8221;或&#8220;位段&#8221;。所谓&#8220;位域&#8221;是把一个字节中的二进位划分为几个不同的区域， 并说明每个区域的位数。每个域有一个域名，允许在程序中按域名进行操作。 这样就可以把几个不同的对象用一个字节的二进制位域来表示。一、位域的定义和位域变量的说明位域定义与结构定义相仿，其形式为：&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />struct 位域结构名&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />{ 位域列表 };&nbsp;&nbsp;&nbsp;&nbsp;<br />其中位域列表的形式为： 类型说明符 位域名：位域长度&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />例如：&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />struct bs&nbsp;&nbsp;&nbsp;&nbsp;<br />{&nbsp;&nbsp;&nbsp;&nbsp;<br />int a:8;&nbsp;&nbsp;&nbsp;&nbsp;<br />int b:2;&nbsp;&nbsp;&nbsp;&nbsp;<br />int c:6;&nbsp;&nbsp;&nbsp;&nbsp;<br />};&nbsp;&nbsp;&nbsp;&nbsp;<br />位域变量的说明与结构变量说明的方式相同。 可采用先定义后说明，同时定义说明或者直接说明这三种方式。例如：&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />struct bs&nbsp;&nbsp;&nbsp;&nbsp;<br />{&nbsp;&nbsp;&nbsp;&nbsp;<br />int a:8;&nbsp;&nbsp;&nbsp;&nbsp;<br />int b:2;&nbsp;&nbsp;&nbsp;&nbsp;<br />int c:6;&nbsp;&nbsp;&nbsp;&nbsp;<br />}data;&nbsp;&nbsp;&nbsp;&nbsp;<br />说明data为bs变量，共占两个字节。其中位域a占8位，位域b占2位，位域c占6位。对于位域的定义尚有以下几点说明：&nbsp;&nbsp;&nbsp;</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">1. 一个位域必须存储在同一个字节中，不能跨两个字节。如一个字节所剩空间不够存放另一位域时，应从下一单元起存放该位域。也可以有意使某位域从下一单元开始。例如：&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />struct bs&nbsp;&nbsp;&nbsp;&nbsp;<br />{&nbsp;&nbsp;&nbsp;&nbsp;<br />unsigned a:4&nbsp;&nbsp;&nbsp;&nbsp;<br />unsigned :0 /*空域*/&nbsp;&nbsp;&nbsp;&nbsp;<br />unsigned b:4 /*从下一单元开始存放*/&nbsp;&nbsp;&nbsp;&nbsp;<br />unsigned c:4&nbsp;&nbsp;&nbsp;&nbsp;<br />}&nbsp;&nbsp;&nbsp;&nbsp;<br />在这个位域定义中，a占第一字节的4位，后4位填0表示不使用，b从第二字节开始，占用4位，c占用4位。&nbsp;&nbsp;&nbsp;</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">2. 由于位域不允许跨两个字节，因此位域的长度不能大于一个字节的长度，也就是说不能超过8位二进位。&nbsp;&nbsp;&nbsp;</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">3. 位域可以无位域名，这时它只用来作填充或调整位置。无名的位域是不能使用的。例如：&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />struct k&nbsp;&nbsp;&nbsp;&nbsp;<br />{&nbsp;&nbsp;&nbsp;&nbsp;<br />int a:1&nbsp;&nbsp;&nbsp;&nbsp;<br />int :2 /*该2位不能使用*/&nbsp;&nbsp;&nbsp;&nbsp;<br />int b:3&nbsp;&nbsp;&nbsp;&nbsp;<br />int c:2&nbsp;&nbsp;&nbsp;&nbsp;<br />};&nbsp;&nbsp;&nbsp;&nbsp;<br />从以上分析可以看出，位域在本质上就是一种结构类型， 不过其成员是按二进位分配的。&nbsp;&nbsp;&nbsp;</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">二、位域的使用位域的使用和结构成员的使用相同，其一般形式为： 位域变量名&#8226;位域名 位域允许用各种格式输出。&nbsp;&nbsp;&nbsp;&nbsp;<br />main(){&nbsp;&nbsp;&nbsp;&nbsp;<br />struct bs&nbsp;&nbsp;&nbsp;&nbsp;<br />{&nbsp;&nbsp;&nbsp;&nbsp;<br />unsigned a:1;&nbsp;&nbsp;&nbsp;&nbsp;<br />unsigned b:3;&nbsp;&nbsp;&nbsp;&nbsp;<br />unsigned c:4;&nbsp;&nbsp;&nbsp;&nbsp;<br />} bit,*pbit;&nbsp;&nbsp;&nbsp;&nbsp;<br />bit.a=1;&nbsp;&nbsp;&nbsp;&nbsp;<br />bit.b=7;&nbsp;&nbsp;&nbsp;&nbsp;<br />bit.c=15;&nbsp;&nbsp;&nbsp;&nbsp;<br />pri</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">改错：<br />＃i nclude &lt;stdio.h&gt;</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">int main(void) {</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">&nbsp;&nbsp;&nbsp; int **p;<br />&nbsp;&nbsp;&nbsp; int arr[100];</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">&nbsp;&nbsp;&nbsp; p = &amp;arr;</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">&nbsp;&nbsp;&nbsp; return 0;<br />}<br />解答：<br />搞错了,是指针类型不同,<br />int **p; //二级指针<br />&amp;arr; //得到的是指向第一维为100的数组的指针<br />＃i nclude &lt;stdio.h&gt;<br />int main(void) {<br />int **p, *q;<br />int arr[100];<br />q = arr;<br />p = &amp;q;<br />return 0;<br />}</span></p><p>&nbsp;</p><p><br /><span style="color: #4822dd; ">下面这个程序执行后会有什么错误或者效果:<br />#define MAX 255<br />int main()<br />{<br />&nbsp;&nbsp; unsigned char A[MAX],i;//i被定义为unsigned char<br />&nbsp;&nbsp; for (i=0;i&lt;=MAX;i++)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A[i]=i;<br />}<br />解答：死循环加数组越界访问（C/C++不进行数组越界检查）<br />MAX=255&nbsp;<br />数组A的下标范围为:0..MAX-1,这是其一..<br />其二.当i循环到255时,循环内执行:<br />A[255]=255;<br />这句本身没有问题..但是返回for (i=0;i&lt;=MAX;i++)语句时,<br />由于unsigned char的取值范围在(0..255),i++以后i又为0了..无限循环下去.</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">struct name1{<br />&nbsp;&nbsp; char str;<br />&nbsp;&nbsp; short x;<br />&nbsp;&nbsp; int&nbsp;&nbsp; num;<br />}</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">struct name2{<br />&nbsp;&nbsp; char str;<br />&nbsp;&nbsp; int num;<br />&nbsp;&nbsp; short x;<br />}</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">sizeof(struct name1)=8,sizeof(struct name2)=12<br />在第二个结构中，为保证num按四个字节对齐，char后必须留出3字节的空间；同时为保证整个结构的自然对齐（这里是4字节对齐），在x后还要补齐2个字节，这样就是12字节。</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">intel：<br />A.c 和B.c两个c文件中使用了两个相同名字的static变量,编译的时候会不会有问题?这两个static变量会保存到哪里（栈还是堆或者其他的）?<br />static的全局变量，表明这个变量仅在本模块中有意义，不会影响其他模块。<br />他们都放在数据区，但是编译器对他们的命名是不同的。<br />如果要使变量在其他模块也有意义的话，需要使用extern关键字。</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">struct s1<br />{<br />int i: 8;<br />int j: 4;<br />int a: 3;<br />double b;<br />};</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">struct s2<br />{<br />int i: 8;<br />int j: 4;<br />double b;<br />int a:3;<br />};</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">printf("sizeof(s1)= %d\n", sizeof(s1));<br />printf("sizeof(s2)= %d\n", sizeof(s2));<br />result: 16, 24<br />第一个struct s1<br />{<br />int i: 8;<br />int j: 4;<br />int a: 3;<br />double b;<br />};<br />理论上是这样的，首先是i在相对0的位置，占8位一个字节，然后，j就在相对一个字节的位置，由于一个位置的字节数是4位的倍数，因此不用对齐，就放在那里了，然后是a，要在3位的倍数关系的位置上，因此要移一位，在15位的位置上放下，目前总共是18位，折算过来是2字节2位的样子，由于double是8字节的，因此要在相对0要是8个字节的位置上放下，因此从18位开始到8个字节之间的位置被忽略，直接放在8字节的位置了，因此，总共是16字节。</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">第二个最后会对照是不是结构体内最大数据的倍数，不是的话，会补成是最大数据的倍数</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">上面是基本问题，接下来是编程问题：</span></p><p>&nbsp;</p><p><br /><span style="color: #4822dd; ">本人很弱，这几个题也搞不定，特来求救：<br />1）读文件file1.txt的内容（例如）：<br />12<br />34<br />56<br />输出到file2.txt：<br />56<br />34<br />12<br />（逆序）<br />2）输出和为一个给定整数的所有组合<br />例如n=5<br />5=1+4；5=2+3（相加的数不能重复）<br />则输出<br />1，4；2，3。<br />望高手赐教！！</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">第一题,注意可增长数组的应用.<br />＃i nclude &lt;stdio.h&gt;<br />＃i nclude &lt;stdlib.h&gt;</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">int main(void)<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int MAX = 10;<br />int *a = (int *)malloc(MAX * sizeof(int));<br />int *b;<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />FILE *fp1;<br />FILE *fp2;</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">fp1 = fopen("a.txt","r");<br />if(fp1 == NULL)<br />{printf("error1");<br />&nbsp;&nbsp;&nbsp; exit(-1);<br />}</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">&nbsp;&nbsp;&nbsp; fp2 = fopen("b.txt","w");<br />if(fp2 == NULL)<br />{printf("error2");<br />&nbsp;&nbsp;&nbsp; exit(-1);<br />}</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">int i = 0;<br />&nbsp;&nbsp;&nbsp; int j = 0;</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">while(fscanf(fp1,"%d",&amp;a[i]) != EOF)<br />{<br />i++;<br />j++;<br />if(i &gt;= MAX)<br />{<br />MAX = 2 * MAX;<br />b = (int*)realloc(a,MAX * sizeof(int));<br />if(b == NULL)<br />{<br />printf("error3");<br />exit(-1);<br />}<br />a = b;<br />}<br />}</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">for(;--j &gt;= 0;)<br />&nbsp;&nbsp; fprintf(fp2,"%d\n",a[j]);</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">fclose(fp1);<br />fclose(fp2);</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">return 0;<br />}</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">第二题.<br />＃i nclude &lt;stdio.h&gt;</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">int main(void)<br />{<br />unsigned long int i,j,k;</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">printf("please input the number\n");<br />scanf("%d",&amp;i);<br />&nbsp;&nbsp;&nbsp; if( i % 2 == 0)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; j = i / 2;<br />else<br />j = i / 2 + 1;</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">printf("The result is \n");<br />&nbsp;&nbsp;&nbsp; for(k = 0; k &lt; j; k++)<br />&nbsp;&nbsp;&nbsp;&nbsp; printf("%d = %d + %d\n",i,k,i - k);<br />return 0;<br />}</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">＃i nclude &lt;stdio.h&gt;<br />void main()<br />{<br />unsigned long int a,i=1;<br />scanf("%d",&amp;a);<br />if(a%2==0)<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp; for(i=1;i&lt;a/2;i++)<br />&nbsp;&nbsp;&nbsp;&nbsp; printf("%d",a,a-i);<br />}<br />else<br />for(i=1;i&lt;=a/2;i++)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf(" %d, %d",i,a-i);<br />}</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">兄弟,这样的题目若是做不出来实在是有些不应该, 给你一个递规反向输出字符串的例子,可谓是反序的经典例程.</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">void inverse(char *p)<br />{<br />&nbsp;&nbsp;&nbsp; if( *p = = '\0' )&nbsp;<br />return;<br />&nbsp;&nbsp;&nbsp; inverse( p+1 );<br />&nbsp;&nbsp;&nbsp; printf( "%c", *p );<br />}</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">int main(int argc, char *argv[])<br />{<br />&nbsp;&nbsp;&nbsp; inverse("abc\0");</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">&nbsp;&nbsp;&nbsp; return 0;<br />}</span></p><p>&nbsp;</p><p><span style="color: #4822dd; ">借签了楼上的&#8220;递规反向输出&#8221;<br />＃i nclude &lt;stdio.h&gt;<br />void test(FILE *fread, FILE *fwrite)<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char buf[1024] = {0};<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!fgets(buf, sizeof(buf), fread))<br />}</span></p><p><span style="color: #3300ff; font-family: 楷体_GB2312; font-size: medium; "><br /></span></p><p><span style="color: #0000ff; font-family: 楷体_GB2312; font-size: medium;"><br /></span></p></div><div id="MySignature" style="margin-top: 10px; color: #333333; font-family: Verdana, Arial, sans-serif, 'Lucida Grande'; font-size: 13px; line-height: 19px; text-align: left; background-color: #d6d3d6; "><p style="margin-bottom:0pt; margin-top:0pt; "><a href="http://www.lianzhiwei.com/"><span style=" color:#0000ff; text-decoration:underline ;font-family:'宋体'; ">奶茶加盟</span></a><a href="http://www.lianzhiwei.com/"><span style=" color:#0000ff; text-decoration:underline ;font-family:'宋体'; ">http://www.lianzhiwei.com/</span></a><span style=" font-size:10.5000pt; font-family:'宋体'; ">奶茶店加盟</span></p></div><img src ="http://www.cppblog.com/lauer3912/aggbug/170159.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-04-05 14:54 <a href="http://www.cppblog.com/lauer3912/archive/2012/04/05/170159.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>Vim 秘籍表 </title><link>http://www.cppblog.com/lauer3912/archive/2011/09/25/156746.html</link><dc:creator>RTY</dc:creator><author>RTY</author><pubDate>Sun, 25 Sep 2011 02:24:00 GMT</pubDate><guid>http://www.cppblog.com/lauer3912/archive/2011/09/25/156746.html</guid><wfw:comment>http://www.cppblog.com/lauer3912/comments/156746.html</wfw:comment><comments>http://www.cppblog.com/lauer3912/archive/2011/09/25/156746.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lauer3912/comments/commentRss/156746.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lauer3912/services/trackbacks/156746.html</trackback:ping><description><![CDATA[原文：http://www.lupaworld.com/article-213530-1.html<br /><div><div><p>2011-9-20 16:43|发布者: <a href="http://home.lupaworld.com/space-uid-436905.html">红黑魂</a>|查看数: 965|评论数: 2|来自: <a href="http://linuxtoy.org/archives/vim-cheat-sheet-2.html" target="_blank">linuxtoy</a></p></div><div id="diysummarytop"></div><div><div><strong>摘要</strong>: 献给程序员们，彩色风格的 VIM 速查秘籍表，适合打印、设为桌面背景或放置到专门的数码相框。   同时还提供 300DPI 的清晰版、黑白和适用于红绿色盲人士的版本，请移步至作者站点下载。 ... ...</div></div><div id="diysummarybottom"></div><div><div id="diycontenttop"></div><table cellspacing="0" cellpadding="0"><tbody><tr><td><span style="font: normal normal normal 14px/19px tahoma, sans-serif; ">       献给程序员们，彩色风格的 VIM 速查秘籍表，适合打印、设为桌面背景或放置到专门的数码相框。</span><div><span style="font: 14px/19px tahoma, sans-serif; text-align: left; color: #000000; text-transform: none; text-indent: 0px; letter-spacing: normal; word-spacing: 0px; white-space: normal; orphans: 2; widows: 2; font-size-adjust: none; font-stretch: normal; -webkit-text-decorations-in-effect: none; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px;"><p><a style="color: #bb5500; text-decoration: none;" href="http://linuxtoy.org/img/2011/09/vim_cheat_sheet_for_programmers_screen.png"></a></p><p>同时还提供 300DPI 的清晰版、黑白和适用于红绿色盲人士的版本，请移步至<a style="color: #bb5500; text-decoration: none;" href="http://michael.peopleofhonoronly.com/vim/">作者站点</a>下载。</p><p><a href="http://www.lupaworld.com/data/attachment/portal/201109/20/164303mctvdswfvcudffym.png" target="_blank"><img src="http://www.lupaworld.com/data/attachment/portal/201109/20/164303mctvdswfvcudffym.png"  alt="" /></a></p></span></div></td></tr></tbody></table></div></div><img src ="http://www.cppblog.com/lauer3912/aggbug/156746.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-25 10:24 <a href="http://www.cppblog.com/lauer3912/archive/2011/09/25/156746.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ANSI</title><link>http://www.cppblog.com/lauer3912/archive/2011/08/21/154040.html</link><dc:creator>RTY</dc:creator><author>RTY</author><pubDate>Sun, 21 Aug 2011 15:04:00 GMT</pubDate><guid>http://www.cppblog.com/lauer3912/archive/2011/08/21/154040.html</guid><wfw:comment>http://www.cppblog.com/lauer3912/comments/154040.html</wfw:comment><comments>http://www.cppblog.com/lauer3912/archive/2011/08/21/154040.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lauer3912/comments/commentRss/154040.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lauer3912/services/trackbacks/154040.html</trackback:ping><description><![CDATA[<span style="font-family: arial, 宋体, sans-serif; font-size: 14px; line-height: 24px; ">　<strong>ANSI编码<div style="height: 14px; line-height: 14px; font-size: 12px; overflow-x: hidden; overflow-y: hidden; "></div>　　</strong>　unicode和ansi都是字符代码的一种表示形式。<div style="height: 14px; line-height: 14px; font-size: 12px; overflow-x: hidden; overflow-y: hidden; "></div>　　为使计算机支持更多语言，通常使用 0x80~0xFF 范围的 2 个<a target="_blank" href="http://baike.baidu.com/view/60408.htm" style="text-decoration: underline; color: #136ec2; ">字节</a>来表示 1 个字符。比如：汉字 '中' 在<div style="background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: #f6f6f6; padding-top: 5px; padding-right: 5px; padding-bottom: 3px; padding-left: 5px; margin-top: 5px; margin-right: 5px; margin-bottom: 5px; margin-left: 5px; text-align: center; float: right; width: 200px; position: relative; visibility: visible; "><a title="查看图片" href="http://baike.baidu.com/image/4a77b2af4c78a3ab7dd92a29" target="_blank" style="text-decoration: none; color: #136ec2; background-image: url(http://img.baidu.com/img/baike/s/zoom.gif); background-attachment: initial; background-origin: initial; background-clip: initial; background-color: initial; width: 14px; height: 14px; font-size: 0px; line-height: 0; display: block; position: absolute; right: 4px; bottom: 4px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-style: solid; border-right-style: solid; border-bottom-style: solid; border-left-style: solid; border-top-color: #f6f6f6; border-right-color: #f6f6f6; border-bottom-color: #f6f6f6; border-left-color: #f6f6f6; background-position: 0px 0px; background-repeat: no-repeat no-repeat; ">&nbsp;&nbsp;</a><a href="http://baike.baidu.com/image/4a77b2af4c78a3ab7dd92a29" target="_blank" style="text-decoration: underline; color: #136ec2; "><img log-set-param"="" log-set-param="img_view" title="ANSI编码" src="http://imgsrc.baidu.com/baike/abpic/item/4a77b2af4c78a3ab7dd92a29.jpg" style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 0px; border-style: initial; border-color: initial; display: block; "  alt="" /></a><p style="margin-top: 3px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 10px; padding-bottom: 0px; padding-left: 0px; color: #666666; font-size: 12px; font-family: 宋体; display: inline; word-wrap: break-word; word-break: break-all; font-style: normal; line-height: 18px; min-height: 18px; zoom: 1; ">ANSI编码</p></div>中文<a target="_blank" href="http://baike.baidu.com/view/880.htm" style="text-decoration: underline; color: #136ec2; ">操作系统</a>中，使用 [0xD6,0xD0] 这两个字节存储。<div style="height: 14px; line-height: 14px; font-size: 12px; overflow-x: hidden; overflow-y: hidden; "></div>　　不同的国家和地区制定了不同的标准，由此产生了 GB2312, BIG5, JIS 等各自的编码标准。这些使用 2 个字节来代表一个字符的各种汉字延伸编码方式，称为 ANSI 编码。在简体中文系统下，ANSI 编码代表 GB2312 编码，在日文操作系统下，ANSI 编码代表 JIS 编码。<div style="height: 14px; line-height: 14px; font-size: 12px; overflow-x: hidden; overflow-y: hidden; "></div>　　不同 ANSI 编码之间互不兼容，当信息在国际间交流时，无法将属于两种语言的文字，存储在同一段 ANSI 编码的文本中。</span><img src ="http://www.cppblog.com/lauer3912/aggbug/154040.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-08-21 23:04 <a href="http://www.cppblog.com/lauer3912/archive/2011/08/21/154040.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++ const 用法</title><link>http://www.cppblog.com/lauer3912/archive/2011/08/10/152913.html</link><dc:creator>RTY</dc:creator><author>RTY</author><pubDate>Tue, 09 Aug 2011 23:25:00 GMT</pubDate><guid>http://www.cppblog.com/lauer3912/archive/2011/08/10/152913.html</guid><wfw:comment>http://www.cppblog.com/lauer3912/comments/152913.html</wfw:comment><comments>http://www.cppblog.com/lauer3912/archive/2011/08/10/152913.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lauer3912/comments/commentRss/152913.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lauer3912/services/trackbacks/152913.html</trackback:ping><description><![CDATA[<div><a href="http://www.diybl.com/course/3_program/c++/cppjs/20091022/179910.html">http://www.diybl.com/course/3_program/c++/cppjs/20091022/179910.html<br /><br /><span class="Apple-style-span" style="color: #383838; font-family: Arial, Helvetica, sans-serif; line-height: 24px; -webkit-text-decorations-in-effect: none; "><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; "><font face="Verdana" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; ">1. const修饰普通变量和指针</font></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; "><font face="Verdana" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; ">const修饰变量，一般有两种写法：</font></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; "><font face="Verdana" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; ">const TYPE value;</font></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; "><font face="Verdana" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; ">TYPE const value;</font></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; "><font face="Verdana" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; ">这两种写法在本质上是一样的。它的含义是：const修饰的类型为TYPE的变量value是不可变的。</font></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; "><font face="Verdana" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; ">对于一个非指针的类型TYPE，无论怎么写，都是一个含义，即value只不可变。</font></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; "><font face="Verdana" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; ">例如：</font></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; "><font face="Verdana" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; ">const int nValue；&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //nValue是const</font></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; "><font face="Verdana" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; ">int const nValue；&nbsp;&nbsp;&nbsp; // nValue是const</font></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; "><font face="Verdana" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; ">但是对于指针类型的TYPE，不同的写法会有不同情况，例如：</font></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; "><font face="Verdana" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; ">A. const char *pContent;</font></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; "><font face="Verdana" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; ">B. char * const pContent;</font></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; "><font face="Verdana" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; ">C. char const *pContent;</font></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; "><font face="Verdana" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; ">D. const char* const pContent;</font></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; "><font face="Verdana" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; ">对于前三种写法，我们可以换个方式，给其加上括号</font></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; "><font face="Verdana" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; ">A. const (char) *pContent;</font></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; "><font face="Verdana" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; ">B. (char*) const pContent;</font></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; "><font face="Verdana" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; ">C. (char) const *pContent;</font></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; "><font face="Verdana" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; ">这样就一目了然。根据对于const修饰非指针变量的规则，很明显，A=C.</font></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; "><font face="Verdana" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; ">- 对于A,C, const修饰的类型为char的变量*pContent为常量，因此，pContent的内容为常量不可变.</font></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; "><font face="Verdana" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; ">- 对于B, 其实还有一种写法： const (char*) pContent;</font></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; "><font face="Verdana" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; ">含义为：const修饰的类型为char*的变量pContent为常量，因此，pContent指针本身为常量不可变.</font></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; "><font face="Verdana" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; ">- 对于D, 其实是A和B的混合体，表示指针本身和指针内容两者皆为常量不可变</font></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; "><font face="Verdana" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; ">总结:</font></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; "><font face="Verdana" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; ">(1)&nbsp; 指针本身是常量不可变</font></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; "><font face="Verdana" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; ">(char*) const pContent;</font></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; "><font face="Verdana" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; ">const (char*) pContent;</font></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; "><font face="Verdana" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; ">(2)&nbsp; 指针所指向的内容是常量不可变</font></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; "><font face="Verdana" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; ">const (char) *pContent;</font></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; "><font face="Verdana" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; ">(char) const *pContent;</font></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; "><font face="Verdana" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; ">(3)&nbsp; 两者都不可变</font></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; "><font face="Verdana" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; ">const char* const pContent;</font></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; "><font face="Verdana" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; ">还有其中区别方法：</font></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; "><font face="Verdana" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; ">沿着*号划一条线，</font></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; "><font face="Verdana" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; ">如果const位于*的左侧，则const就是用来修饰指针所指向的变量，即指针指向为常量；</font></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; "><font face="Verdana" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; ">如果const位于*的右侧，const就是修饰指针本身，即指针本身是常量。&nbsp;</font><font face="Verdana" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; "><br style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; " />2. const修饰函数参数</font></p><font face="Verdana" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; 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; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">const修饰函数参数是它最广泛的一种用途，它表示函数体中不能修改参数的值(包括参数本身的值或者参数其中包含的值)。它可以很好&nbsp;<br style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; " />void function(const int Var); //传递过来的参数在函数内不可以改变(无意义，因为Var本身就是形参)</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">void function(const char* Var); //参数指针所指内容为常量不可变</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">void function(char* const Var); //参数指针本身为常量不可变(也无意义， 因为char* Var也是形参)</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; "></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">参数为引用，为了增加效率同时防止修改。</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">修饰引用参数时：</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">void function(const Class&amp; Var);//引用参数在函数内不可以改变</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">void function(const TYPE&amp; Var); //引用参数在函数内为常量不可变</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; "></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">3. const 修饰函数返回值</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">const修饰函数返回值其实用的并不是很多，它的含义和const修饰普通变量以及指针的含义基本相同。</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">(1) const int fun1() 这个其实无意义，因为参数返回本身就是赋值。</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">(2) const int * fun2()</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">调用时 const int *pValue = fun2();</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">我们可以把fun2()看作成一个变量，那么就是我们上面所说的1.(1)的写法，即指针内容不可变。</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">(3) int* const fun3()</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">调用时 int * const pValue = fun2();</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">我们可以把fun2()看作成一个变量，那么就是我们上面所说的1.(2)的写法，即指针本身不可变。</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">4. const修饰类对象/对象指针/对象引用</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">const修饰类对象表示该对象为常量对象，其中的任何成员都不能被修改。对于对象指针和对象引用也是一样。</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">const修饰的对象，该对象的任何非const成员函数都不能被调用，因为任何非const成员函数会有修改成员变量的企图。</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">例如：</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">class AAA</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">{<br style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; " />&nbsp;&nbsp; void func1();</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">void func2() const;</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">}</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">const AAA aObj;</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">aObj.func1(); &#215;</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">aObj.func2(); 正确</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; "></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">const AAA* aObj = new AAA();</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">aObj-&gt;func1(); &#215;</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">aObj-&gt;func2(); 正确&nbsp;<br style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; " />5. const修饰成员变量</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">const修饰类的成员函数，表示成员常量，不能被修改，同时它只能在初始化列表中赋值。&nbsp;<br style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; " />class A</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">{</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">&nbsp;&nbsp; &#8230;</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">&nbsp;&nbsp; const int nValue;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //成员常量不能被修改</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">&nbsp;&nbsp; &#8230;</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">&nbsp;&nbsp; A(int x): nValue(x) {}; //只能在初始化列表中赋值</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">}&nbsp;<br style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; " />6. const修饰成员函数</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">const修饰类的成员函数，则该成员函数不能修改类中任何非const成员函数。一般写在函数的最后来修饰。<br style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; " />class A</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">{</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">&nbsp;&nbsp; &#8230;</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">void function()const; //常成员函数, 它不改变对象的成员变量. 也不能调用类中任何非const成员函数。</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">}</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">对于const类对象/指针/引用，只能调用类的const成员函数，因此，const修饰成员函数的最重要作用就是限制对于const对象的使用。&nbsp;<br style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; " />7. const常量与define宏定义的区别</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">(1) 编译器处理方式不同</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">define宏是在预处理阶段展开。</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">const常量是编译运行阶段使用。</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">(2) 类型和安全检查不同</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">define宏没有类型，不做任何类型检查，仅仅是展开。</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">const常量有具体的类型，在编译阶段会执行类型检查。</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">(3) 存储方式不同</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; ">define宏仅仅是展开，有多少地方使用，就展开多少次，不会分配内存。</p></font><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; line-height: 24px; color: #383838; font-size: 14px; "><font face="Verdana" style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; ">const常量会在内存中分配(可以是堆中也可以是栈中)。</font></p></span><br /></a></div><img src ="http://www.cppblog.com/lauer3912/aggbug/152913.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-08-10 07:25 <a href="http://www.cppblog.com/lauer3912/archive/2011/08/10/152913.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用OllDbg中断在dll的入口</title><link>http://www.cppblog.com/lauer3912/archive/2011/08/10/152911.html</link><dc:creator>RTY</dc:creator><author>RTY</author><pubDate>Tue, 09 Aug 2011 23:11:00 GMT</pubDate><guid>http://www.cppblog.com/lauer3912/archive/2011/08/10/152911.html</guid><wfw:comment>http://www.cppblog.com/lauer3912/comments/152911.html</wfw:comment><comments>http://www.cppblog.com/lauer3912/archive/2011/08/10/152911.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lauer3912/comments/commentRss/152911.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lauer3912/services/trackbacks/152911.html</trackback:ping><description><![CDATA[<span class="Apple-style-span" style="font-family: Simsun; font-size: 12px; line-height: normal; background-color: #ffffff; ">用OllDbg中断在dll的入口<br /><br /><br /></span><div><a href="http://www.pediy.com/bbshtml/bbs7/pediy7-613.htm">http://www.pediy.com/bbshtml/bbs7/pediy7-613.htm<br /><br /><br /><span class="Apple-style-span" style="color: #000000; font-family: Simsun; line-height: normal; -webkit-text-decorations-in-effect: none; background-color: #ffffff; font-size: medium; "><p><font color="blue"><span class="pediy" style="font-size: 9pt; ">标 题:</span></font><span class="pediy" style="font-size: 9pt; "><span class="pediy" style="font-size: 9pt; "><span class="pediy" style="font-size: 9pt; ">&nbsp;Diy OllyDbg's Loaddll.exe<br /><font color="blue">发帖人:</font>jingulong<br /><font color="blue">时 间:&nbsp;</font><font color="#666686">2005-08-13 13:02&nbsp;</font><br /><font color="blue">原文链接:</font><font color="#999999"><a href="http://bbs.pediy.com/showthread.php?threadid=16140" style="color: #000099; text-decoration: none; "><font color="#666686">http://bbs.pediy.com/showthread.php?threadid=16140</font></a></font>&nbsp;<br /><font color="blue">详细信息:</font>&nbsp;</span></span></span><br /></p><blockquote><p class="pediy" style="font-size: 9pt; ">目的：用OllDbg中断在dll的入口<br />程序下载：http://bbs.pediy.com/showthread.php?s=&amp;threadid=16082&nbsp;<br /><br />一、编写plugin&nbsp;:<br />1.&nbsp;&nbsp;DllEntryPoint如图：<br /><img src="http://www.pediy.com/bbshtml/bbs7/pediy7-613/snap000.jpg" border="0" alt="" width="432" height="232" />&nbsp;<br />2．ODBG_Plugininit如图：新启一个线程，由此线程的函数WinMain创建一个（隐藏）窗口。<br /><img src="http://www.pediy.com/bbshtml/bbs7/pediy7-613/snap0001.jpg" border="0" alt="" width="497" height="145" />&nbsp;<br />3．窗口回调函数如图：<br /><img src="http://www.pediy.com/bbshtml/bbs7/pediy7-613/snap0002.jpg" border="0" alt="" width="454" height="175" />&nbsp;<br />至此，plugin设计完成，它所起的作用是：<br />1．&nbsp;&nbsp;当得到WM_USER消息时返回dll入口地址（在MyLdrpCallInitRoutine中）<br />2．&nbsp;&nbsp;当得到WM_USER＋1消息时在wParam指示的地址（就是你调试点dll入口点）设置临时断点（Tempbreakpoint）。<br /><br />二、&nbsp;&nbsp;在dll的加载进程中与plugin通讯。<br />&nbsp;&nbsp;&nbsp;&nbsp;为此，可以写一个加载程序（就好像DLL_Loader），我这里选择diy&nbsp;llyDbg配套的loaddll.exe，因为只有它可与OD&#8220;无缝对接&#8221;。用LordPE打开程序看看结构，当看到它的ExportTable时，感觉实在妙！下面是其截图：<br /><img src="http://www.pediy.com/bbshtml/bbs7/pediy7-613/snap003.jpg" border="0" alt="" width="331" height="123" />&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;每个导出函数名称叫人看了都如此受用，特别是PatchArea！Oleh&nbsp;Yuschuk为我们考虑得实在周到。Thanks<br />Diy&nbsp;过程：<br />1．&nbsp;&nbsp;把ImportTable中的GetCommandLineA改成GetProcAddress<br />2．&nbsp;&nbsp;Patch&nbsp;<br /><img src="http://www.pediy.com/bbshtml/bbs7/pediy7-613/snap004.jpg" border="0" alt="" width="292" height="10" />&nbsp;<br />&nbsp;&nbsp;&nbsp;为：<br /><img src="http://www.pediy.com/bbshtml/bbs7/pediy7-613/snap0006.jpg" border="0" alt="" width="199" height="11" />&nbsp;<br />并在Patcharea（410298）处键入：<br /><img src="http://www.pediy.com/bbshtml/bbs7/pediy7-613/snap008.jpg" border="0" alt="" width="535" height="71" />&nbsp;<br />以完成原语句的功能。这里程序中原来没有的字符串（如&#8220;GetCommandLineA&#8221;）等在data区或rsrc区段空白处录入。<br />3．Patch<br /><img src="http://www.pediy.com/bbshtml/bbs7/pediy7-613/snap005.jpg_990.jpg" border="0" alt="" width="280" height="10" />&nbsp;<br />为&nbsp;call&nbsp;&nbsp;4102B5，并在4102B5开始写入如下代码：<br /><img src="http://www.pediy.com/bbshtml/bbs7/pediy7-613/snap009.jpg" border="0" alt="" width="565" height="369" />&nbsp;<br /><img src="http://www.pediy.com/bbshtml/bbs7/pediy7-613/snap010.jpg" border="0" alt="" width="655" height="424" />&nbsp;<br /><img src="http://www.pediy.com/bbshtml/bbs7/pediy7-613/snap011.jpg" border="0" alt="" width="594" height="227" />&nbsp;<br />以上代码完成的主要任务是hook&nbsp;dll&nbsp;entrypoint，当程序查到入口处是调试点dll&nbsp;oep&nbsp;时通知plugin设置断点，使OllyDbg中断在那里，这样我们可以少飞许多手脚。</p></blockquote></span><br /></a></div><img src ="http://www.cppblog.com/lauer3912/aggbug/152911.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-08-10 07:11 <a href="http://www.cppblog.com/lauer3912/archive/2011/08/10/152911.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>dll中非导出函数的调用</title><link>http://www.cppblog.com/lauer3912/archive/2011/08/10/152910.html</link><dc:creator>RTY</dc:creator><author>RTY</author><pubDate>Tue, 09 Aug 2011 23:08:00 GMT</pubDate><guid>http://www.cppblog.com/lauer3912/archive/2011/08/10/152910.html</guid><wfw:comment>http://www.cppblog.com/lauer3912/comments/152910.html</wfw:comment><comments>http://www.cppblog.com/lauer3912/archive/2011/08/10/152910.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lauer3912/comments/commentRss/152910.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lauer3912/services/trackbacks/152910.html</trackback:ping><description><![CDATA[<div><a href="http://hi.baidu.com/51634780/blog/item/6e99de041d682689e850cd2a.html">http://hi.baidu.com/51634780/blog/item/6e99de041d682689e850cd2a.html<br /><span class="Apple-style-span" style="color: #555b6e; font-family: Arial; font-size: 12px; line-height: 18px; -webkit-text-decorations-in-effect: none; background-color: #ffffff; "><table style="table-layout: fixed; width: 898px; "><tbody><tr><td style="font-family: Arial; word-wrap: break-word; word-break: break-all; visibility: visible !important; zoom: 1 !important; filter: none; font-size: 12px; line-height: 18px; "><div id="blog_text" class="cnt" style="font-family: Arial; word-wrap: break-word; word-break: normal; visibility: visible !important; zoom: 1 !important; filter: none; font-size: 12px; line-height: 18px; overflow-x: hidden; overflow-y: hidden; position: relative !important; "><p style="line-height: normal; ">dll中非导出函数的调用<br style="line-height: normal; " />这几天在学习pe，对输出表特别兴趣。程序对导出函数调用，最终是对导出<br style="line-height: normal; " />函数入口地址的调用，非导出函数的入口地址不在导出表里，所以其他程序</p><p style="line-height: normal; ">无法</p><p style="line-height: normal; ">调用非导出函数。<br style="line-height: normal; " />本人刚开始的想法是在内存里修改dll的导出表，使非导出函数变成导出函</p><p style="line-height: normal; ">数，</p><p style="line-height: normal; ">供自己的程序调用。后来想到可不可以直接调用非导出函数的入口地址呢？</p><p style="line-height: normal; ">感觉</p><p style="line-height: normal; ">应该可以，有了这个想法后就马上开始动手试验。</p><p style="line-height: normal; ">自己写了个简单的dll，其中有2个导出函数a1,b1，1个内部函数c1<br style="line-height: normal; " />先用call的方法调用导出函数：a1<br style="line-height: normal; " />invoke LoadLibrary,CTEXT('testdll.dll')<br style="line-height: normal; " />mov @var1,eax&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;模块句柄（也是模块的基址）存入@var1&nbsp;<br style="line-height: normal; " />invoke GetProcAddress,@var1,CTEXT('a1')<br style="line-height: normal; " />mov @lpProc,eax ;导出函数的入口地址存入@lpProc<br style="line-height: normal; " />push 5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;函数参数3<br style="line-height: normal; " />push 9&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;函数参数2<br style="line-height: normal; " />push hWnd&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;函数参数1<br style="line-height: normal; " />call @lpProc&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;函数调用<br style="line-height: normal; " />一切正常。</p><p style="line-height: normal; ">再调用非导出函数试试，在调用非导出函数c1前，必须找到c1的相对虚拟<br style="line-height: normal; " />地址，<br style="line-height: normal; " />先用程序装入dll<br style="line-height: normal; " />invoke LoadLibrary,CTEXT('testdll.dll')<br style="line-height: normal; " />mov @var1,eax&nbsp;&nbsp;&nbsp; ;模块句柄，也是模块的基址<br style="line-height: normal; " />然后运行程序，用OD调试，找到c1的入口地址：1000100C<br style="line-height: normal; " />1000100C&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 55&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push ebp<br style="line-height: normal; " />1000100D&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 8BEC&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov ebp,esp<br style="line-height: normal; " />1000100F&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 8B45 08&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov eax,dword ptr ss:[ebp+8]<br style="line-height: normal; " />10001012&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0345 0C&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; add eax,dword ptr ss:[ebp+C]<br style="line-height: normal; " />10001015&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; C9&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; leave<br style="line-height: normal; " />10001016&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; C2 0800&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; retn 8</p><p style="line-height: normal; ">通过 @var1获得模块的基址是10000000<br style="line-height: normal; " />那末c1的相对虚拟地址就是：1000100C－10000000＝100c<br style="line-height: normal; " />做好了这一步后，就可以在自己的程序里调用非导出函数了。<br style="line-height: normal; " />调用非导出函数：<br style="line-height: normal; " />invoke LoadLibrary,CTEXT('testdll.dll')<br style="line-height: normal; " />mov @var1,eax&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;模块句柄（也是模块的基址）存入@var1<br style="line-height: normal; " />add @var1,100ch&nbsp;&nbsp;&nbsp; ;得到c1的入口地址<br style="line-height: normal; " />push 5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;函数参数2<br style="line-height: normal; " />push 9&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;函数参数1<br style="line-height: normal; " />call @var1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;函数调用<br style="line-height: normal; " />得到了想要的结果，一切ok!</p><p style="line-height: normal; ">用这种方法可以调用别人dll的非导出函数。</p></div></td></tr></tbody></table></span><br /></a></div><img src ="http://www.cppblog.com/lauer3912/aggbug/152910.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-08-10 07:08 <a href="http://www.cppblog.com/lauer3912/archive/2011/08/10/152910.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>虚函数，虚析构函数，纯虚函数，抽象类</title><link>http://www.cppblog.com/lauer3912/archive/2011/07/26/151838.html</link><dc:creator>RTY</dc:creator><author>RTY</author><pubDate>Mon, 25 Jul 2011 22:59:00 GMT</pubDate><guid>http://www.cppblog.com/lauer3912/archive/2011/07/26/151838.html</guid><wfw:comment>http://www.cppblog.com/lauer3912/comments/151838.html</wfw:comment><comments>http://www.cppblog.com/lauer3912/archive/2011/07/26/151838.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lauer3912/comments/commentRss/151838.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lauer3912/services/trackbacks/151838.html</trackback:ping><description><![CDATA[<span class="Apple-style-span" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff; "><div><span style="font-size: 13px; "><strong>1.虚函数</strong></span></div><div><span style="font-size: 13px; "><u>1.1虚函数的作用</u></span></div><div><span style="font-size: 13px; color: red; ">虚函数的作用是允许在派生类中重新定义与基类同名的函数，并且可以通过基类指针或引用来访问基类和派生类中的同名函数。</span></div><table border="1" cellpadding="0" cellspacing="0"><tbody><tr><td valign="top"><div align="left"><span style="font-size: 13px; ">class Time{</span></div><div align="left"><span style="font-size: 13px; ">&nbsp; public:</span></div><div align="left"><span style="font-size: 13px; ">&nbsp;&nbsp;&nbsp; Time(int=0,int=0,int=0);</span></div><div align="left"><span style="font-size: 13px; ">&nbsp;&nbsp;&nbsp; void show();</span></div><div align="left"><span style="font-size: 13px; ">&nbsp; protected:</span></div><div align="left"><span style="font-size: 13px; ">&nbsp;&nbsp;&nbsp; int hour;</span></div><div align="left"><span style="font-size: 13px; ">&nbsp;&nbsp;&nbsp; int min;</span></div><div align="left"><span style="font-size: 13px; ">&nbsp;&nbsp;&nbsp; int sec;</span></div><div align="left"><span style="font-size: 13px; ">};</span></div><div align="left"><span style="font-size: 13px; ">&nbsp;</span></div><div align="left"><span style="font-size: 13px; ">class LocalTime:public Time{</span></div><div align="left"><span style="font-size: 13px; ">&nbsp; public:</span></div><div align="left"><span style="font-size: 13px; ">&nbsp;&nbsp;&nbsp; LocalTime(int=0,int=0,int=0,string="+8");</span></div><div align="left"><span style="font-size: 13px; ">&nbsp;&nbsp;&nbsp; void show();</span></div><div align="left"><span style="font-size: 13px; ">&nbsp; protected:</span></div><div align="left"><span style="font-size: 13px; ">&nbsp;&nbsp;&nbsp; string zone;</span></div><div align="left"><span style="font-size: 13px; ">};</span></div><div align="left"><span style="font-size: 13px; ">&nbsp;</span></div><div align="left"><span style="font-size: 13px; ">Time::Time(int h,int m,int s):hour(h),min(m),sec(s){}</span></div><div align="left"><span style="font-size: 13px; ">&nbsp;</span></div><div align="left"><span style="font-size: 13px; ">void Time::show(){</span></div><div align="left"><span style="font-size: 13px; ">&nbsp; cout&lt;&lt;hour&lt;&lt;":"&lt;&lt;min&lt;&lt;":"&lt;&lt;sec&lt;&lt;endl;</span></div><div align="left"><span style="font-size: 13px; ">}</span></div><div align="left"><span style="font-size: 13px; ">&nbsp;</span></div><div align="left"><span style="font-size: 13px; ">LocalTime::LocalTime(int h,int m,int s,string z):Time(h,m,s),zone(z){}</span></div><div align="left"><span style="font-size: 13px; ">&nbsp;</span></div><div align="left"><span style="font-size: 13px; ">void LocalTime::show(){</span></div><div align="left"><span style="font-size: 13px; ">&nbsp; cout&lt;&lt;hour&lt;&lt;":"&lt;&lt;min&lt;&lt;":"&lt;&lt;sec&lt;&lt;"@"&lt;&lt;zone&lt;&lt;endl;&nbsp;&nbsp;&nbsp;&nbsp;</span></div><div align="left"><span style="font-size: 13px; ">}</span></div><div align="left"><span style="font-size: 13px; ">&nbsp;</span></div><div align="left"><span style="font-size: 13px; ">int main(){</span></div><div align="left"><span style="font-size: 13px; ">&nbsp; Time t;</span></div><div align="left"><span style="font-size: 13px; ">&nbsp; LocalTime lt;</span></div><div align="left"><span style="font-size: 13px; ">&nbsp; Time *pt=&amp;t;</span></div><div align="left"><span style="font-size: 13px; ">&nbsp; pt-&gt;show();</span></div><div align="left"><span style="font-size: 13px; ">&nbsp; pt=&amp;lt;</span></div><div align="left"><span style="font-size: 13px; ">&nbsp; pt-&gt;show();</span></div><div align="left"><span style="font-size: 13px; ">&nbsp; system("PAUSE");</span></div><div align="left"><span style="font-size: 13px; ">&nbsp; return EXIT_SUCCESS;</span></div><div><span style="font-size: 13px; ">}</span></div></td></tr></tbody></table><div><span style="font-size: 13px; ">结果：</span></div><div><span style="font-size: 13px; ">0:0:0</span></div><div><span style="font-size: 13px; ">0:0:0</span></div><div><span style="font-size: 13px; ">这里通过指针找到派生类，但无法调用派生类show()。如果使用虚函数。</span></div><div><span style="font-size: 13px; ">将基类Time中的show()函数声明为虚函数， 其余不变。</span></div><table border="1" cellpadding="0" cellspacing="0"><tbody><tr><td valign="top"><div align="left"><span style="font-size: 13px; ">class Time{</span></div><div align="left"><span style="font-size: 13px; ">&nbsp; public:</span></div><div align="left"><span style="font-size: 13px; ">&nbsp;&nbsp;&nbsp; Time(int=0,int=0,int=0);</span></div><div align="left"><span style="font-size: 13px; ">&nbsp;&nbsp;&nbsp; virtual void show();</span></div><div align="left"><span style="font-size: 13px; ">&#8230;</span></div><div><span style="font-size: 13px; ">};</span></div></td></tr></tbody></table><div><span style="font-size: 13px; ">结果：</span></div><div><span style="font-size: 13px; ">0:0:0</span></div><div><span style="font-size: 13px; ">0:0:0@+8</span></div><div><span style="font-size: 13px; ">本来，基类指针是指向基类对象的，如果用它指向派生类对象，则进行指针类型转换，将派生类对象的指针先转换为基类指针，所以基类指针指向的是派生类对象中的基类部分。在程序修改前，是无法通过基类指针去调用派生类对象中的成员函数的。</span></div><div><span style="font-size: 13px; ">虚函数突破这一限制，在派生类的基类部分中，派生类的虚函数取代了基类原来的虚函数，因此在使用基类指针指向派生类对象后，调用虚函数时就调用了派生类的虚函数。</span></div><div><span style="font-size: 13px; ">&nbsp;</span></div><div><span style="font-size: 13px; "><u>1.2虚函数的使用方法</u></span></div><div><span style="font-size: 13px; ">【1】在基类用virtual声明成员函数为虚函数。这样就可以在派生类中重新定义此函数，为它赋予新的功能，并能方便地被调用。</span></div><div><span style="font-size: 13px; ">&nbsp;</span></div><div><span style="font-size: 13px; ">【2】</span><span style="font-size: 13px; color: red; ">在派生类中重新定义此函数，要求函数名、函数（返回）类型、函数参数个数和类型与基函数的虚函数相同。如果在派生类中没有对基类的虚函数重定义，则派生类简单地继承直接基类的虚函数。</span></div><div><span style="font-size: 13px; ">有一种情况例外，在这种情况下派生类与基类的成员函数返回类型不同，但仍起到虚函数的作用。即基类虚函数返回一个基类指针或基类引用，而子类的虚函数返回一个子类的指针或子类的引用。</span></div><table border="1" cellpadding="0" cellspacing="0"><tbody><tr><td valign="top"><div align="left"><span style="font-size: 13px; ">class Base{</span></div><div align="left"><span style="font-size: 13px; ">&nbsp; public:</span></div><div align="left"><span style="font-size: 13px; ">&nbsp;&nbsp;&nbsp; virtual Base *fun(){</span></div><div align="left"><span style="font-size: 13px; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cout&lt;&lt;"Base's fun()."&lt;&lt;endl;</span></div><div align="left"><span style="font-size: 13px; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return this;</span></div><div align="left"><span style="font-size: 13px; ">&nbsp;&nbsp;&nbsp; }</span></div><div align="left"><span style="font-size: 13px; ">};</span></div><div align="left"><span style="font-size: 13px; ">&nbsp;</span></div><div align="left"><span style="font-size: 13px; ">class Derived:public Base{</span></div><div align="left"><span style="font-size: 13px; ">&nbsp; public:</span></div><div align="left"><span style="font-size: 13px; ">&nbsp;&nbsp;&nbsp; virtual Derived *fun(){</span></div><div align="left"><span style="font-size: 13px; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cout&lt;&lt;"Derived's fun()."&lt;&lt;endl;</span></div><div align="left"><span style="font-size: 13px; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return this;</span></div><div align="left"><span style="font-size: 13px; ">&nbsp;&nbsp;&nbsp; }</span></div><div align="left"><span style="font-size: 13px; ">};</span></div><div align="left"><span style="font-size: 13px; ">&nbsp;</span></div><div align="left"><span style="font-size: 13px; ">void test(Base &amp;x){</span></div><div align="left"><span style="font-size: 13px; ">&nbsp; Base *b;</span></div><div align="left"><span style="font-size: 13px; ">&nbsp; b=x.fun();</span></div><div align="left"><span style="font-size: 13px; ">}</span></div><div align="left"><span style="font-size: 13px; ">&nbsp;</span></div><div align="left"><span style="font-size: 13px; ">int main(){</span></div><div align="left"><span style="font-size: 13px; ">&nbsp; Base b;&nbsp;</span></div><div align="left"><span style="font-size: 13px; ">&nbsp; Derived d;</span></div><div align="left"><span style="font-size: 13px; ">&nbsp; test(b);</span></div><div align="left"><span style="font-size: 13px; ">&nbsp; test(d);&nbsp;&nbsp;&nbsp;</span></div><div align="left"><span style="font-size: 13px; ">&nbsp; system("PAUSE");</span></div><div align="left"><span style="font-size: 13px; ">&nbsp; return EXIT_SUCCESS;</span></div><div><span style="font-size: 13px; ">}</span></div></td></tr></tbody></table><div><span style="font-size: 13px; ">结果：</span></div><div><span style="font-size: 13px; ">Base's fun().</span></div><div><span style="font-size: 13px; ">Derived's fun().</span></div><div><span style="font-size: 13px; ">&nbsp;</span></div><div><span style="font-size: 13px; ">【3】C++规定，当一个成员函数被声明为虚函数后，其派生类中的同名函数（符合2中定义的函数）都自动成为虚函数。</span></div><div><span style="font-size: 13px; ">&nbsp;</span></div><div><span style="font-size: 13px; ">【4】定义一个指向基类对象的指针变量，并使其指向同一类族中的某个对象。通过该指针变量调用此函数，此时调用的就是指针变量指向的对象的同名函数。</span></div><div><span style="font-size: 13px; ">&nbsp;</span></div><div><span style="font-size: 13px; "><u>1.3声明虚函数的限制</u></span></div><div><span style="font-size: 13px; ">【1】只能用virtual声明类的成员函数，使它成为虚函数，而不能将类外的普通函数声明为虚函数。</span></div><div><span style="font-size: 13px; ">&nbsp;</span></div><div><span style="font-size: 13px; ">【2】一个成员函数被声明为虚函数后，在同一类族中的类就不能再定义一个非virtual的但与该虚函数具有相同参数（个数与类型）和函数返回值类型的同名函数。</span></div><div><span style="font-size: 13px; ">&nbsp;</span></div><div><span style="font-size: 13px; ">【3】静态成员函数不能是虚函数，因为静态成员函数不受限于某个对象。</span></div><div><span style="font-size: 13px; ">&nbsp;</span></div><div><span style="font-size: 13px; ">【4】inline函数不能是虚函数，因为inline函数是不能在运行中动态确定其位置的。即使虚函数在类的内部定义，编译时，仍将其视为非inline的。</span></div><div><span style="font-size: 13px; ">&nbsp;</span></div><div><span style="font-size: 13px; ">【5】使用虚函数，系统要有一定的空间开销。当一个类带有虚函数时，编译器会为该类构造一个虚函数表(virtual function tanle,vtable)，它是一个指针数组，存放每个虚函数的入口地址。</span></div><div><span style="font-size: 13px; "><strong>2.虚析构函数</strong></span></div><table border="1" cellpadding="0" cellspacing="0"><tbody><tr><td valign="top"><div align="left"><span style="font-size: 13px; ">class Time{</span></div><div align="left"><span style="font-size: 13px; ">&nbsp; public:</span></div><div align="left"><span style="font-size: 13px; ">&nbsp;&nbsp;&nbsp; Time(int=0,int=0,int=0);</span></div><div align="left"><span style="font-size: 13px; ">&nbsp;&nbsp;&nbsp; ~Time(){</span></div><div align="left"><span style="font-size: 13px; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cout&lt;&lt;"Time destructor"&lt;&lt;endl;</span></div><div align="left"><span style="font-size: 13px; ">&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div><div align="left"><span style="font-size: 13px; ">&nbsp; protected:</span></div><div align="left"><span style="font-size: 13px; ">&nbsp;&nbsp;&nbsp; int hour;</span></div><div align="left"><span style="font-size: 13px; ">&nbsp;&nbsp;&nbsp; int min;</span></div><div align="left"><span style="font-size: 13px; ">&nbsp;&nbsp;&nbsp; int sec;</span></div><div align="left"><span style="font-size: 13px; ">};&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div><div align="left"><span style="font-size: 13px; ">&nbsp;</span></div><div align="left"><span style="font-size: 13px; ">class LocalTime:public Time{</span></div><div align="left"><span style="font-size: 13px; ">&nbsp; public:</span></div><div align="left"><span style="font-size: 13px; ">&nbsp;&nbsp;&nbsp; LocalTime(int=0,int=0,int=0,string="+8");</span></div><div align="left"><span style="font-size: 13px; ">&nbsp;&nbsp;&nbsp; ~LocalTime(){</span></div><div align="left"><span style="font-size: 13px; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cout&lt;&lt;"LocalTime destructor"&lt;&lt;endl;</span></div><div align="left"><span style="font-size: 13px; ">&nbsp;&nbsp;&nbsp; }</span></div><div align="left"><span style="font-size: 13px; ">&nbsp; protected:</span></div><div align="left"><span style="font-size: 13px; ">&nbsp;&nbsp;&nbsp; string zone;</span></div><div align="left"><span style="font-size: 13px; ">};&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div><div align="left"><span style="font-size: 13px; ">&nbsp;</span></div><div align="left"><span style="font-size: 13px; ">Time::Time(int h,int m,int s):hour(h),min(m),sec(s){}</span></div><div align="left"><span style="font-size: 13px; ">&nbsp;</span></div><div align="left"><span style="font-size: 13px; ">LocalTime::LocalTime(int h,int m,int s,string z):Time(h,m,s),zone(z){}</span></div><div align="left"><span style="font-size: 13px; ">&nbsp;</span></div><div align="left"><span style="font-size: 13px; ">int main(){</span></div><div align="left"><span style="font-size: 13px; ">&nbsp; Time *p=new LocalTime;//指向派生类</span></div><div align="left"><span style="font-size: 13px; ">&nbsp; delete p;&nbsp;&nbsp;</span></div><div align="left"><span style="font-size: 13px; ">&nbsp; system("PAUSE");</span></div><div align="left"><span style="font-size: 13px; ">&nbsp; return EXIT_SUCCESS;</span></div><div><span style="font-size: 13px; ">}</span></div></td></tr></tbody></table><div><span style="font-size: 13px; ">结果：</span></div><div><span style="font-size: 13px; ">Time destructor</span></div><div><span style="font-size: 13px; ">从结果可以看出，执行的还是基类的析构函数，而程序的本意是希望执行派生类的析构函数。此时将基类的析构函数声明为虚析构函数，</span></div><table border="1" cellpadding="0" cellspacing="0"><tbody><tr><td valign="top"><div align="left"><span style="font-size: 13px; ">virtual ~Time(){</span></div><div align="left"><span style="font-size: 13px; ">&nbsp; cout&lt;&lt;"Time destructor"&lt;&lt;endl;</span></div><div><span style="font-size: 13px; ">}</span></div></td></tr></tbody></table><div><span style="font-size: 13px; ">结果：</span></div><div><span style="font-size: 13px; ">LocalTime destructor</span></div><div><span style="font-size: 13px; ">Time destructor</span></div><div><span style="font-size: 13px; ">如果将基类的析构函数声明为虚函数，由该基类所派生的所有派生类的析构函数也自动成为虚函数。</span></div><div><span style="font-size: 13px; ">把基类的析构函数声明为虚函数的好处是，如果程序中delete一个对象，而delete运算符的操作对象是指向派生类对象的基类指针，则系统会调用相应类的析构函数。</span></div><div><span style="font-size: 13px; ">构造函数不能声明为虚函数。</span></div><div><span style="font-size: 13px; "><strong>3.纯虚函数</strong></span></div><table border="1" cellpadding="0" cellspacing="0"><tbody><tr><td valign="top"><div><span style="font-size: 13px; ">virtual void show()=0;//纯虚函数</span></div></td></tr></tbody></table><div><span style="font-size: 13px; ">这里将show()声明为<strong>纯虚函数(pure virtual function)</strong>。纯虚函数是在声明虚函数时被&#8220;初始化&#8221;为0的虚函数。</span></div><div><span style="font-size: 13px; ">声明纯虚函数的一般形式为，</span></div><table border="1" cellpadding="0" cellspacing="0"><tbody><tr><td valign="top"><div><span style="font-size: 13px; ">virtual 函数类型 函数名(参数列表)=0;</span></div></td></tr></tbody></table><div><span style="font-size: 13px; ">纯虚函数没有函数体；最后的&#8220;=0&#8221;并不代表函数返回值为0，它只起形式上的作用，告诉编译器&#8220;这是纯虚函数&#8221;；这个一个声明语句，最后有分号。</span></div><div><span style="font-size: 13px; ">声明纯虚函数是告诉编译器，&#8220;在这里声明了一个虚函数，留待派生类中定义&#8221;。在派生类中对此函数提供了定义后，它才能具备函数的功能，可以被调用。</span></div><div><span style="font-size: 13px; ">纯虚函数的作用是在基类中为其派生类保留了一个函数的名字，以便派生类根据需要对它进行定义。</span></div><div><span style="font-size: 13px; ">如果在一个类中声明了纯虚函数，而在其派生类中没有对该函数定义，则该函数在派生类中仍为纯虚函数。</span></div><div><span style="font-size: 13px; "><strong>4.抽象类</strong></span></div><div><span style="font-size: 13px; ">将不用来定义对象而只作为一种基本类型用作继承的类，称为<strong>抽象类(abstract class)</strong>，由于它常用作基类，通常称为抽象基类。</span><span style="font-size: 13px; color: red; ">凡是包含纯虚函数的类都是抽象类。</span></div><div><span style="font-size: 13px; ">如果在派生类中没有对所有的纯虚函数进行定义，则此派生类仍然是抽象类，不能用来定义对象。</span></div><div><span style="font-size: 13px; ">可以定义指向抽象类数据的指针变量。当派生类成为具体类后，就可以用这个指针指向派生类对象，然后通过该指针调用虚函数。</span></div><div><span style="font-size: 13px; "><br /></span></div></span><img src ="http://www.cppblog.com/lauer3912/aggbug/151838.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 06:59 <a href="http://www.cppblog.com/lauer3912/archive/2011/07/26/151838.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>KBuild MakeFile介绍</title><link>http://www.cppblog.com/lauer3912/archive/2011/07/21/151523.html</link><dc:creator>RTY</dc:creator><author>RTY</author><pubDate>Wed, 20 Jul 2011 23:24:00 GMT</pubDate><guid>http://www.cppblog.com/lauer3912/archive/2011/07/21/151523.html</guid><wfw:comment>http://www.cppblog.com/lauer3912/comments/151523.html</wfw:comment><comments>http://www.cppblog.com/lauer3912/archive/2011/07/21/151523.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lauer3912/comments/commentRss/151523.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lauer3912/services/trackbacks/151523.html</trackback:ping><description><![CDATA[<span class="Apple-style-span" style="font-family: song, Verdana; font-size: 12px; line-height: normal; background-color: #f0f3fa; "><table cellspacing="0" cellpadding="0" style="word-wrap: break-word; empty-cells: show; border-collapse: collapse; line-height: normal; width: 1009px; table-layout: fixed; margin-left: 1px; "><tbody style="word-wrap: break-word; line-height: normal; "><tr style="word-wrap: break-word; line-height: normal; "><td class="t_msgfont" id="postmessage_14162165" style="word-wrap: break-word; color: #000000; font: normal normal normal 12px/normal song, Verdana; line-height: 1.6em; font-size: 14px; "><br style="word-wrap: break-word; line-height: normal; " /><br style="word-wrap: break-word; line-height: normal; " />&nbsp; &nbsp; 从Linux内核2.6开始，<font color="#0000ff" style="word-wrap: break-word; line-height: normal; ">Linux内核的编译采用Kbuild<span href="tag.php?name=%CF%B5%CD%B3" class="t_tag" style="word-wrap: break-word; line-height: normal; cursor: pointer; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: #ff0000; white-space: nowrap; ">系统</span></font>，这同过去的编译系统有很大的不同，尤其对于Linux内核模块的编译。<font color="#0000ff" style="word-wrap: break-word; line-height: normal; ">在新的系统下，Linux编译系统会两次扫描Linux的Makefile：首先编译系统会读取Linux内核顶层的Makefile，然后根据读到的内容第二次读取Kbuild的Makefile来编译Linux内核。</font><br style="word-wrap: break-word; line-height: normal; " /><strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">Linux</strong><strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">内核Makefile分类</strong><br style="word-wrap: break-word; line-height: normal; " />&#183;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; "><font color="#ff0000" style="word-wrap: break-word; line-height: normal; ">Kernel Makefile</font>&nbsp;</strong><br style="word-wrap: break-word; line-height: normal; " />Kernel Makefile位于Linux内核源代码的顶层目录，也叫 Top Makefile。它主要用于指定编译Linux Kernel目标<span href="tag.php?name=%CE%C4%BC%FE" class="t_tag" style="word-wrap: break-word; line-height: normal; cursor: pointer; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: #ff0000; white-space: nowrap; ">文件</span>（vm<span href="tag.php?name=linux" class="t_tag" style="word-wrap: break-word; line-height: normal; cursor: pointer; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: #ff0000; white-space: nowrap; ">linux</span>）和模块（module）。这编译内核或模块是，这个文件会被首先读取，并根据读到的内容配置编译环境变量。对于内核或驱动<span href="tag.php?name=%BF%AA%B7%A2" class="t_tag" style="word-wrap: break-word; line-height: normal; cursor: pointer; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: #ff0000; white-space: nowrap; ">开发</span>人员来说，这个文件几乎不用任何修改。<br style="word-wrap: break-word; line-height: normal; " />&#183;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; "><font color="#ff0000" style="word-wrap: break-word; line-height: normal; ">Kbuild Makefile</font>&nbsp;</strong><br style="word-wrap: break-word; line-height: normal; " />Kbuild系统使用Kbuild Makefile来编译内核或模块。当Kernel Makefile被解析完成后，Kbuild会读取相关的Kbuild Makefile进行内核或模块的编译。Kbuild Makefile有特定的语法指定哪些编译进内核中、哪些编译为模块、及对应的源文件是什么等。内核及驱动开发人员需要编写这个Kbuild Makefile文件。<br style="word-wrap: break-word; line-height: normal; " />&#183;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; "><font color="#ff0000" style="word-wrap: break-word; line-height: normal; ">ARCH Makefile</font>&nbsp;</strong><br style="word-wrap: break-word; line-height: normal; " />ARCH Makefile位于ARCH/$(ARCH)/Makefile，是系统对应平台的Makefile。Kernel Top Makefile会包含这个文件来指定平台相关信息。只有平台开发人员会关心这个文件。<br style="word-wrap: break-word; line-height: normal; " /><strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">Kbuild Makefile</strong><br style="word-wrap: break-word; line-height: normal; " />&nbsp; &nbsp; Kbuild Makefile的文件名不一定是<strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">Makefile</strong>，尽管推荐使用Makefile这个名字。大多的Kbuild文件的名字都是Makefile。为了与其他Makefile文件相区别，你也可以指定Kbuild Makefile的名字为<strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">Kbuild</strong>。而且如果&#8220;Makefile&#8221;和&#8220;Kbuild&#8221;文件同时存在，则Kbuild系统会使用&#8220;Kbuild&#8221;文件。<br style="word-wrap: break-word; line-height: normal; " />&#183;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">目标定义&nbsp;</strong><br style="word-wrap: break-word; line-height: normal; " />Kbuild Makefile的一个最主要功能就是指定编译什么，这个功能是通过下面两个对象指定的obj-?和xxx-objs：<br style="word-wrap: break-word; line-height: normal; " />&#183;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">obj-?</strong><br style="word-wrap: break-word; line-height: normal; " />obj-?指定编译什么，怎么编译？其中的&#8220;?&#8221;可能是&#8220;y&#8221;或&#8220;m&#8221;，&#8220;y&#8221;指定把对象编译进内核中，&#8220;m&#8221;指定把对象编译为模块。语法如下;<br style="word-wrap: break-word; line-height: normal; " />&nbsp; &nbsp; obj-? = $(target).o<br style="word-wrap: break-word; line-height: normal; " />target为编译对象的名字。如果没有指定xxx-objs，这编译这个对象需要的源文件就是$(target).c或$(target).s。如果指定了$(target)-objs，则编译这个对象需要的源文件由$(target)-objs指定，并且不能有$(target).c或$(target).s文件。<br style="word-wrap: break-word; line-height: normal; " />&#183;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">xxx-objs&nbsp;</strong><br style="word-wrap: break-word; line-height: normal; " />xxx-objs指定了编译对象需要的文件，一般只有在源文件是多个时才需要它。<br style="word-wrap: break-word; line-height: normal; " />只要包含了这两行，Kbuild Makefile就应该可以工作了。<br style="word-wrap: break-word; line-height: normal; " />&#183;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">嵌套编译&nbsp;</strong><br style="word-wrap: break-word; line-height: normal; " />有时一个对象可能嵌入到另一个对象的目录下，那个如何编译子目录下的对象呢?其实很简单，只要指定obj_?的对象为子目录的名字就可以了：<br style="word-wrap: break-word; line-height: normal; " />obj-? = $(sub_target)/<br style="word-wrap: break-word; line-height: normal; " />其中&#8220;?&#8221;可以是&#8220;y&#8221;或&#8220;m&#8221;，$(sub_target)是子目录名字。<br style="word-wrap: break-word; line-height: normal; " />&#183;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">编译器选项&nbsp;</strong><br style="word-wrap: break-word; line-height: normal; " />尽管在大多数情况下不需要指定编译器选项，有时我们还是需要指定一些编译选项的。<br style="word-wrap: break-word; line-height: normal; " />&#183;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">ccflags-y, asflags-y and ldflags-y&nbsp;</strong><br style="word-wrap: break-word; line-height: normal; " />这些编译选项用于指定cc、as和ld的编译选项<br style="word-wrap: break-word; line-height: normal; " /><strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">编译外部模块</strong><br style="word-wrap: break-word; line-height: normal; " />有时候我们需要在内核源代码数的外面编译内核模块，编译的基本命令是：<br style="word-wrap: break-word; line-height: normal; " />&nbsp; &nbsp; make -C $(KERNEL_DIR) M=`pwd` modules<br style="word-wrap: break-word; line-height: normal; " />我们可以把这个命令集成到Makefile里，这样我们就可以只输入&#8220;make&#8221;命令就可以了。回想上一章的那个Makefile，它把Normal Makefile 和Kbuild&nbsp;&nbsp;Makefile集成到一个文件中了。为了区别Kbuild Makefile 和Normal Makefile，这样我们改写Makefile为如下形式，并且添加Kbuild Makefile - &#8220;Kbuild&#8221;。<br style="word-wrap: break-word; line-height: normal; " />##Makefile<br style="word-wrap: break-word; line-height: normal; " />ifneq ($(KERNELRELEASE),)<br style="word-wrap: break-word; line-height: normal; " />include "Kbuild"<br style="word-wrap: break-word; line-height: normal; " />else<br style="word-wrap: break-word; line-height: normal; " />KERNEL_DIR = /lib/modules/`uname -r`/build<br style="word-wrap: break-word; line-height: normal; " />MODULEDIR := $(<span href="tag.php?name=shell" class="t_tag" style="word-wrap: break-word; line-height: normal; cursor: pointer; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: #ff0000; white-space: nowrap; ">shell</span>&nbsp;pwd)<br style="word-wrap: break-word; line-height: normal; " />.PHONY: modules<br style="word-wrap: break-word; line-height: normal; " />default: modules<br style="word-wrap: break-word; line-height: normal; " />modules:<br style="word-wrap: break-word; line-height: normal; " />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;make -C $(KERNEL_DIR)&nbsp;&nbsp;M=$(MODULEDIR) modules<br style="word-wrap: break-word; line-height: normal; " />clean distclean:<br style="word-wrap: break-word; line-height: normal; " />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;rm -f *.o *.mod.c .*.*.cmd *.ko<br style="word-wrap: break-word; line-height: normal; " />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;rm -rf .tmp_versions<br style="word-wrap: break-word; line-height: normal; " />endif<br style="word-wrap: break-word; line-height: normal; " /><br style="word-wrap: break-word; line-height: normal; " />## Kbuild<br style="word-wrap: break-word; line-height: normal; " />MODULE_NAME = helloworld<br style="word-wrap: break-word; line-height: normal; " />$(MODULE_NAME)-objs := hello.o<br style="word-wrap: break-word; line-height: normal; " />obj-m&nbsp; &nbsp;:= $(MODULE_NAME).o<br style="word-wrap: break-word; line-height: normal; " />一般不需要在Makefile里包含如下代码,这样写完全是为了兼容老版本的Kbuild系统。KERNELRELEASE变量在Kernel Makefile里定义的，因此只有在第二次由Kbuild读取这个Makefile文件时才会解析到Kbuild的内容。&nbsp;<br style="word-wrap: break-word; line-height: normal; " />ifneq ($(KERNELRELEASE),)<br style="word-wrap: break-word; line-height: normal; " />include "Kbuild"<br style="word-wrap: break-word; line-height: normal; " />else<br style="word-wrap: break-word; line-height: normal; " />...<br style="word-wrap: break-word; line-height: normal; " />endif<br style="word-wrap: break-word; line-height: normal; " /><strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">外部头文件</strong><br style="word-wrap: break-word; line-height: normal; " />有时需要连接内核源代码外部的系统头文件，但Kbuild系统默认的系统头文件都在内核源代码内部，如何使用外部的头文件呢？这个可以借助于Kbuild系统的特殊规则:<br style="word-wrap: break-word; line-height: normal; " />&#183;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">EXTRA_CFLAGS&nbsp;</strong><br style="word-wrap: break-word; line-height: normal; " />EXTRA_CFLAGS可以给Kbuild系统添加外部系统头文件，<br style="word-wrap: break-word; line-height: normal; " />&nbsp; &nbsp; EXTRA_CFLAGS += $(ext_include_path)<br style="word-wrap: break-word; line-height: normal; " />一般外部头文件可能位于外部模块源文件的目录内，如何指定呢？这可以借助$(src)或$(obj)<br style="word-wrap: break-word; line-height: normal; " />&#183;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">$(src)/$(obj)&nbsp;</strong><br style="word-wrap: break-word; line-height: normal; " />$(src)是一个相对路径，它就是Makefile/Kbuild文件所在的路径。同样$(obj)就是编译目标保存的路径，默认就是源代码所在路径。<br style="word-wrap: break-word; line-height: normal; " />因此，我们修改Kbuild文件添加 EXTRA_CFLAGS 来包含外部头文件尽管在这个驱动里没有引用外部系统头文件：<br style="word-wrap: break-word; line-height: normal; " />## Kbuild<br style="word-wrap: break-word; line-height: normal; " />MODULE_NAME = helloworld<br style="word-wrap: break-word; line-height: normal; " />$(MODULE_NAME)-objs := hello.o<br style="word-wrap: break-word; line-height: normal; " />EXTRA_CFLAGS := -I$(src)/include<br style="word-wrap: break-word; line-height: normal; " />obj-m&nbsp; &nbsp;:= $(MODULE_NAME).o<br style="word-wrap: break-word; line-height: normal; " /><br style="word-wrap: break-word; line-height: normal; " /><br style="word-wrap: break-word; line-height: normal; " /><br style="word-wrap: break-word; line-height: normal; " />&#183;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;Goal definitions<br style="word-wrap: break-word; line-height: normal; " /><strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">Example:</strong><br style="word-wrap: break-word; line-height: normal; " />&nbsp; &nbsp;obj-y += foo.o<br style="word-wrap: break-word; line-height: normal; " />告诉kbuild，在文件夹中又一个叫做foo.o的object。foo.o将会被从foo.c或者foo.S被构建。<br style="word-wrap: break-word; line-height: normal; " /><br style="word-wrap: break-word; line-height: normal; " />如果foo.o被构建成一个<strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">模块</strong>，则将使用变量<strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">obj-m</strong>。<strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">Example</strong>:<br style="word-wrap: break-word; line-height: normal; " />&nbsp; &nbsp;obj-$(CONFIG_FOO) += foo.o<br style="word-wrap: break-word; line-height: normal; " />$(CONFIG_FOO)要么是y(built-in)要么是m(module)。如果CONFIG_FOO既不是y也不是m，那么文件将不会被编译也不会被连接。<br style="word-wrap: break-word; line-height: normal; " />&#183;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;Built-in object goals - obj-y<br style="word-wrap: break-word; line-height: normal; " />kbuild Makefiles在<strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">$(obj-y)</strong>列表中为vmlinux指明object文件。这个列表依靠内核的配置。<br style="word-wrap: break-word; line-height: normal; " />在<strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">$(obj-y)</strong>中的文件的顺序是非常重要的。列表中允许两个相同的文件：<strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">第一个</strong>实体将被连接到built-in.o，后面的实体将会<strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">被忽略。</strong><br style="word-wrap: break-word; line-height: normal; " />连接的顺序也很重要，因为在boot过程中某些函数(module_init()/_initcall)将会<strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">按顺序出现。</strong>因此，如果改变了连接顺序，将会改变你的SCSI控制器的检测顺序，你的磁盘也同时被重新编号了。<br style="word-wrap: break-word; line-height: normal; " /><strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">Example:</strong><br style="word-wrap: break-word; line-height: normal; " />&nbsp;&nbsp;#drivers/isdn/i4l/Makefile<br style="word-wrap: break-word; line-height: normal; " />&nbsp;&nbsp;# Makefile for the kernel ISDN subsystem and device drivers.<br style="word-wrap: break-word; line-height: normal; " />&nbsp;&nbsp;# Each configuration option enables a list of files.<br style="word-wrap: break-word; line-height: normal; " />&nbsp;&nbsp;obj-$(CONFIG_ISDN)&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; += isdn.o<br style="word-wrap: break-word; line-height: normal; " />&nbsp;&nbsp;obj-$(CONFIG_ISDN_PPP_BSDCOMP) += isdn_bsdcomp.o<br style="word-wrap: break-word; line-height: normal; " />&#183;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;Loadable module goals - obj-m<br style="word-wrap: break-word; line-height: normal; " /><strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">$(obj-m)</strong>指明object文件作为可装载的内核模块被构建。一个模块可能从一个或者多个源文件被构建。kbuild maefile只是简单的将源文件加到%(obj-m)<br style="word-wrap: break-word; line-height: normal; " /><strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">Example:</strong><strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; "><br style="word-wrap: break-word; line-height: normal; " /></strong>&nbsp;&nbsp;#drivers/isdn/i4l/Makefile<br style="word-wrap: break-word; line-height: normal; " />&nbsp;&nbsp;obj-$(CONFIG_ISDN_PPP_BSDCOMP) += isdn_bsdcomp.o<br style="word-wrap: break-word; line-height: normal; " />注意这里$(CONFIG_ISDN_PPP_BSDCOMP)是m.<br style="word-wrap: break-word; line-height: normal; " />Note: In this example $(CONFIG_ISDN_PPP_BSDCOMP) evaluates to 'm'。<br style="word-wrap: break-word; line-height: normal; " />如果一个内核模块从多个源文件构建，KBuild就必须要知道你想从哪些部分构建模块。因此，你不得不设置<strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">$(-objs</strong>)变量来告诉KBuild。<br style="word-wrap: break-word; line-height: normal; " /><strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">Example:</strong><br style="word-wrap: break-word; line-height: normal; " />&nbsp;&nbsp;#drivers/isdn/i4l/Makefile<br style="word-wrap: break-word; line-height: normal; " />&nbsp;&nbsp;obj-$(CONFIG_ISDN) += isdn.o<br style="word-wrap: break-word; line-height: normal; " />&nbsp;&nbsp;isdn-objs := isdn_net_lib.o isdn_v110.o isdn_common.o<br style="word-wrap: break-word; line-height: normal; " />在这个例子中，模块名是isdn.o,Kbuild将会编译列在<strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">$(isdn-objs)</strong><strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">的</strong>object文件，然后在这些文件的列表中调用"$(LD) -r"来产生isdn.o。<br style="word-wrap: break-word; line-height: normal; " />Kbuild使用<strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">后缀-objs,-y</strong>来识别混合的object文件。这允许Makefiles使用变量<strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">CONFIG_sambol</strong>来决定一个object是否是混合object的的一部分。<br style="word-wrap: break-word; line-height: normal; " /><strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">Example:</strong><br style="word-wrap: break-word; line-height: normal; " />&nbsp;&nbsp;#fs/ext2/Makefile<br style="word-wrap: break-word; line-height: normal; " />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;obj-$(CONFIG_EXT2_FS)&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;+= ext2.o<br style="word-wrap: break-word; line-height: normal; " />&nbsp; &nbsp;ext2-y&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;:= balloc.o bitmap.o<br style="word-wrap: break-word; line-height: normal; " />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;ext2-$(CONFIG_EXT2_FS_XATTR) += xattr.o<br style="word-wrap: break-word; line-height: normal; " /><br style="word-wrap: break-word; line-height: normal; " />在这个例子中，如果$(CONFIG_EXT2_FS_XATTR)是y，则xattr.o只是混合object文件ext2.o的一部分。<br style="word-wrap: break-word; line-height: normal; " />注意，当你构造一个objects到内核中时，上面的语法当然也能够工作。因此，如果你让CONFIG_EXT2=Y,KBuild将会为你构建一个独立的ext2.o文件，并且连接到built-in.o。<br style="word-wrap: break-word; line-height: normal; " />&#183;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">Library file goals - lib-y</strong><br style="word-wrap: break-word; line-height: normal; " />用<strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">obj-*</strong>连接的Objects在指明的文件夹中被用作模块或者综合进built-in.o。也又可能被列出的objects将会被包含进一个库,lib.a。所有用lib-y列出的objects在那个文件夹中被综合进单独的一个库。列在obj-y和附加列在lib-y中的Objects将不会被包含在库中，因为他们将会被任意的存取。对于被连接在lib-m中，连续的objects将会被包含在lib.a中。值得注意的是kbuild makefile可能列出文件用作built-in，并且作为库的一部分。因此，同一个文件夹可能包含一个built-in.o和lib.a文件。<br style="word-wrap: break-word; line-height: normal; " /><strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">Example:</strong><strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; "><br style="word-wrap: break-word; line-height: normal; " /></strong>&nbsp;&nbsp;#arch/i386/lib/Makefile<br style="word-wrap: break-word; line-height: normal; " />&nbsp;&nbsp;lib-y&nbsp; &nbsp; := checksum.o delay.o<br style="word-wrap: break-word; line-height: normal; " />这里讲会创建一个基于checksum.o和delay.o的库文件。对于kbuild，识别一个lib.a正在被构建，这个文件夹应该被列在<strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">libs-y</strong>中。<strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">lib-y</strong>的使用方法通常被限制在lib/和arc/*/lib中。<br style="word-wrap: break-word; line-height: normal; " />&#183;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">Descending down in directories</strong><br style="word-wrap: break-word; line-height: normal; " />一个Makefile只负责在他自己的文件夹中构建objects。 在子文件夹中的文件应该由子文件夹中的Makefiles来照顾。如果你知道他们，build系统将会自动递归地用在子文件夹中的make。<br style="word-wrap: break-word; line-height: normal; " />在这种情况下<strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">obj-y</strong>和<strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">obj-m</strong>就被使用了。ext2存在于不同的文件夹中，Makefile出现在fs/，则告诉kbuild从后面的参数下来。<br style="word-wrap: break-word; line-height: normal; " /><strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">Example:</strong><br style="word-wrap: break-word; line-height: normal; " />&nbsp;&nbsp;#fs/Makefile<br style="word-wrap: break-word; line-height: normal; " />&nbsp;&nbsp;obj-$(CONFIG_EXT2_FS) += ext2/<br style="word-wrap: break-word; line-height: normal; " />如果CONFIG_EXT2_FS被设置成y(built-in)或者m(modular)，相应的obj-变量将会被设置，并且kbuild将会从ext2文件夹继承下来。Kbuild只会使用这些信息来决定它需要访问这些文件夹，而在子文件夹中的Makefile来指明哪些是modules哪些是built-in。<br style="word-wrap: break-word; line-height: normal; " />当赋值文件夹名字的时候，使用CONFIG_variable是很好的选择。这允许kbuild完全的跳过文件夹，而不管CONFIG_option是否是y或者m。<br style="word-wrap: break-word; line-height: normal; " />&#183;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">Compilation flags</strong><br style="word-wrap: break-word; line-height: normal; " />&nbsp; &nbsp; EXTRA_CFLAGS, EXTRA_AFLAGS, EXTRA_LDFLAGS, EXTRA_ARFLAGS。<br style="word-wrap: break-word; line-height: normal; " />所有的EXTRA_ variables只<span href="tag.php?name=%D3%A6%D3%C3" class="t_tag" style="word-wrap: break-word; line-height: normal; cursor: pointer; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: #ff0000; white-space: nowrap; ">应用</span>在kbuild中，他们被赋值的地方。EXTRA_variables应用在kbuild makefile中所有的可执行的命令。<strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">$(EXTRA_CFLAGS)</strong>&nbsp;指明用$(CC)编译C文件的时候的选项。<br style="word-wrap: break-word; line-height: normal; " /><strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">Example:</strong><strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; "><br style="word-wrap: break-word; line-height: normal; " /></strong>&nbsp;&nbsp;# drivers/sound/emu10k1/Makefile<br style="word-wrap: break-word; line-height: normal; " />&nbsp;&nbsp;EXTRA_CFLAGS += -I$(obj)<br style="word-wrap: break-word; line-height: normal; " />&nbsp;&nbsp;ifdef DEBUG<br style="word-wrap: break-word; line-height: normal; " />&nbsp; &nbsp;&nbsp; &nbsp;EXTRA_CFLAGS += -DEMU10K1_DEBUG<br style="word-wrap: break-word; line-height: normal; " />&nbsp;&nbsp;endif<br style="word-wrap: break-word; line-height: normal; " />这里的变量是必须的，因为顶层的Makefile拥有变量<strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">$(CFLAGS)</strong>并且用它来作为整个树的编译标志当编译汇编源文件的时候<strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">$(EXTRA_AFLAGS)</strong>，和每个文件夹的选项是相似的。<br style="word-wrap: break-word; line-height: normal; " /><strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">Example:</strong><br style="word-wrap: break-word; line-height: normal; " />&nbsp;&nbsp;#arch/x86_64/kernel/Makefile<br style="word-wrap: break-word; line-height: normal; " />&nbsp;&nbsp;EXTRA_AFLAGS := -traditional<br style="word-wrap: break-word; line-height: normal; " /><strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">$(EXTRA_LDFLAGS)</strong>和<strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">$(EXTRA_ARFLAGS)</strong>&nbsp;对于每个文件夹的$(LD)和$(AR)选项是类似的。<br style="word-wrap: break-word; line-height: normal; " /><strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">Example:</strong><br style="word-wrap: break-word; line-height: normal; " />&nbsp;&nbsp;#arch/m68k/fpsp040/Makefile<br style="word-wrap: break-word; line-height: normal; " />&nbsp;&nbsp;EXTRA_LDFLAGS := -x<br style="word-wrap: break-word; line-height: normal; " />CFLAGS_$@, AFLAGS_$@<br style="word-wrap: break-word; line-height: normal; " /><strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">CFLAGS_$@</strong>和<strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">AFLAGS_$@</strong>只应用到当前kbuild makefile的命令。<br style="word-wrap: break-word; line-height: normal; " /><strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">$(CFLAGS_$@)</strong>&nbsp;为每个文件的$(CC)指明选项。<strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">$@</strong><strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; "><br style="word-wrap: break-word; line-height: normal; " /></strong>部分有一个字面上的值，指明它是为那个文件。<br style="word-wrap: break-word; line-height: normal; " /><strong style="word-wrap: break-word; text-align: left; font-style: normal; line-height: normal; font-weight: bold; ">Example:</strong><br style="word-wrap: break-word; line-height: normal; " />&nbsp;&nbsp;# drivers/scsi/Makefile<br style="word-wrap: break-word; line-height: normal; " />&nbsp;&nbsp;CFLAGS_aha152x.o =&nbsp; &nbsp;-DAHA152X_STAT -DAUTOCONF<br style="word-wrap: break-word; line-height: normal; " />&nbsp;&nbsp;CFLAGS_gdth.o&nbsp; &nbsp; = # -DDEBUG_GDTH=2 -D__SERIAL__ -D__COM2__ \<br style="word-wrap: break-word; line-height: normal; " />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;-DGDTH_STATISTICS<br style="word-wrap: break-word; line-height: normal; " />&nbsp;&nbsp;CFLAGS_seagate.o =&nbsp; &nbsp;-DARBITRATE -DPARITY -DSEAGATE_USE_ASM<br style="word-wrap: break-word; line-height: normal; " />These three lines specify compilation flags for aha152x.o,<br style="word-wrap: break-word; line-height: normal; " />gdth.o, and seagate.o<br style="word-wrap: break-word; line-height: normal; " />$(AFLAGS_$@) is a similar feature for source files in assembly<br style="word-wrap: break-word; line-height: normal; " />languages.<br style="word-wrap: break-word; line-height: normal; " />Example:<br style="word-wrap: break-word; line-height: normal; " />&nbsp;&nbsp;# arch/arm/kernel/Makefile<br style="word-wrap: break-word; line-height: normal; " />&nbsp;&nbsp;AFLAGS_head-armv.o := -DTEXTADDR=$(TEXTADDR) -traditional<br style="word-wrap: break-word; line-height: normal; " />&nbsp;&nbsp;AFLAGS_head-armo.o := -DTEXTADDR=$(TEXTADDR) -traditional<br style="word-wrap: break-word; line-height: normal; " /></td></tr></tbody></table></span><img src ="http://www.cppblog.com/lauer3912/aggbug/151523.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-21 07:24 <a href="http://www.cppblog.com/lauer3912/archive/2011/07/21/151523.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux on Power 上的调试工具和技术 (gdb ）及valgrind</title><link>http://www.cppblog.com/lauer3912/archive/2011/07/19/151353.html</link><dc:creator>RTY</dc:creator><author>RTY</author><pubDate>Mon, 18 Jul 2011 23:30:00 GMT</pubDate><guid>http://www.cppblog.com/lauer3912/archive/2011/07/19/151353.html</guid><wfw:comment>http://www.cppblog.com/lauer3912/comments/151353.html</wfw:comment><comments>http://www.cppblog.com/lauer3912/archive/2011/07/19/151353.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lauer3912/comments/commentRss/151353.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lauer3912/services/trackbacks/151353.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Linux on Power 上的调试工具和技术Calvin Sze&nbsp;(calvins@us.ibm.com), Linux 顾问 , EMC简介：&nbsp;调试是一项主要的软件开发活动，作为应用程序开发人员，您无法避免对程序进行调试。有效的调试不仅能缩短软件开发周期，而且可以节省成本。本文简要介绍了在用户空间的 C/C++ 和 Java? 应用程序中查找 bug 的技术，并介绍了一些...&nbsp;&nbsp;<a href='http://www.cppblog.com/lauer3912/archive/2011/07/19/151353.html'>阅读全文</a><img src ="http://www.cppblog.com/lauer3912/aggbug/151353.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-19 07:30 <a href="http://www.cppblog.com/lauer3912/archive/2011/07/19/151353.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>mainCRTStartup VC++</title><link>http://www.cppblog.com/lauer3912/archive/2011/06/18/148910.html</link><dc:creator>RTY</dc:creator><author>RTY</author><pubDate>Sat, 18 Jun 2011 07:09:00 GMT</pubDate><guid>http://www.cppblog.com/lauer3912/archive/2011/06/18/148910.html</guid><wfw:comment>http://www.cppblog.com/lauer3912/comments/148910.html</wfw:comment><comments>http://www.cppblog.com/lauer3912/archive/2011/06/18/148910.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lauer3912/comments/commentRss/148910.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lauer3912/services/trackbacks/148910.html</trackback:ping><description><![CDATA[<div id="blog_text"><p>操作系统装载应用程序后，做完初始化工作就转到程序的入口点执行。程序的默认入 口点由连接程序设置， 不同的连接器选择的入口函数也不尽相同。在VC++下，连接器对控制台程序设置的入口函数是  mainCRTStartup，mainCRTStartup 再调用main 函数；对图形用户界面（GUI）程序设置的入口函数是  WinMainCRTStartup，WinMainCRTStartup 调用你自己写的 WinMain  函数。具体设置哪个入口点是由连接器的&#8220;/subsystem:&#8221;选项确定的，它告诉操作系统如何运行编译生成的.EXE文件。可以指定四种方 式：CONSOLE|WINDOWS|NATIVE|POSIX。如果这个选项参数的值为  WINDOWS，则表示该应用程序运行时不需要控制台，有关连接器参数选项的详细说明请参考 MSDN 库。 </p> <p>以下四种组合，可以实现console和windows模式的混合，可以达到不弹出DOS窗口的效果，也可以达到在Windows程序中向控制台输出printf信息的目的。<br />#pragma comment( linker, "/subsystem:windows /entry:WinMainCRTStartup" )<br />#pragma comment( linker, "/subsystem:windows /entry:mainCRTStartup" )<br />#pragma comment( linker, "/subsystem:console /entry:mainCRTStartup" )<br />#pragma comment( linker, "/subsystem:console /entry:WinMainCRTStartup" )</p> <p>int APIENTRY WinMain(HINSTANCE hInstance,<br />HINSTANCE hPrevInstance,<br />LPSTR&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lpCmdLine,<br />int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nCmdShow)<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // ...<br />}</p> <p>int main(void)<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // ...<br />} </p></div><img src ="http://www.cppblog.com/lauer3912/aggbug/148910.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-06-18 15:09 <a href="http://www.cppblog.com/lauer3912/archive/2011/06/18/148910.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C/C++中如何获取数组的长度？</title><link>http://www.cppblog.com/lauer3912/archive/2011/06/12/148523.html</link><dc:creator>RTY</dc:creator><author>RTY</author><pubDate>Sun, 12 Jun 2011 02:36:00 GMT</pubDate><guid>http://www.cppblog.com/lauer3912/archive/2011/06/12/148523.html</guid><wfw:comment>http://www.cppblog.com/lauer3912/comments/148523.html</wfw:comment><comments>http://www.cppblog.com/lauer3912/archive/2011/06/12/148523.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lauer3912/comments/commentRss/148523.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lauer3912/services/trackbacks/148523.html</trackback:ping><description><![CDATA[<span class="Apple-style-span" style="color: #2e3b00; font-family: Arial; font-weight: bold; line-height: 26px; "><span style="font-family: Arial; line-height: 18px; "><table style="table-layout: fixed; width: 968px; "><tbody><tr><td style="font-family: Arial; word-wrap: break-word; word-break: break-all; visibility: visible !important; zoom: 1 !important; filter: none; font-size: 12px; line-height: 18px; "><div id="blog_text" style="font-family: Arial; word-wrap: break-word; word-break: normal; visibility: visible !important; zoom: 1 !important; filter: none; font-size: 14px; line-height: 18px; color: #666666; overflow-x: hidden; overflow-y: hidden; position: relative !important; "><div style="font-family: Arial; word-wrap: break-word; word-break: break-all; visibility: visible !important; zoom: 1 !important; filter: none; font-size: 12px; line-height: normal; "><p style="line-height: normal; "><span style="line-height: normal; font-size: 10.5pt; ">C、C++中没有提供直接获取数组长度的函数，对于存放字符串的字符数组提供了一个strlen函数获取长度，那么对于其他类型的数组如何获取他们的长度呢？其中一种方法是使用sizeof(array) / sizeof(array[0]),&nbsp;</span><span style="line-height: normal; font-size: 10.5pt; ">在C语言中习惯上在&nbsp;</span><span style="line-height: normal; font-size: 10.5pt; ">使用时都把它定义成一个宏，比如#define GET_ARRAY_LEN(array,len) {len = (sizeof(array) / sizeof(array[0]));}&nbsp;</span><span style="line-height: normal; font-size: 10.5pt; ">。而在C++中则可以使用模板技术定义一个函数，比如：</span></p><p style="line-height: normal; "><span style="line-height: normal; font-size: 10.5pt; ">template &lt;class T&gt;</span></p><p style="line-height: normal; "><span style="line-height: normal; font-size: 10.5pt; ">int getArrayLen(T&amp; array)</span></p><p style="line-height: normal; "><span style="line-height: normal; font-size: 10.5pt; ">{</span></p><p style="line-height: normal; "><span style="line-height: normal; font-size: 10.5pt; ">return (sizeof(array) / sizeof(array[0]));</span></p><p style="line-height: normal; "><span style="line-height: normal; font-size: 10.5pt; ">}</span></p><p style="line-height: normal; "><span style="line-height: normal; font-size: 10.5pt; ">这样对于不同类型的数组都可以使用这个宏或者这个函数来获取数组的长度了。以下是两个Demo程序，一个C语言的，一个C++的：</span></p><p style="line-height: normal; "><span style="line-height: normal; font-size: 10.5pt; ">P.S：若数组为存储字符串的字符数组，则所求得的长度还需要减一，即对于宏定义：&nbsp;</span><span style="line-height: normal; font-size: 10.5pt; ">#define GET_ARRAY_LEN(array,len) {len = (sizeof(array) / sizeof(array[0])</span>&nbsp;<span style="line-height: normal; font-size: 10.5pt; ">- 1</span>&nbsp;<span style="line-height: normal; font-size: 10.5pt; ">);}</span>&nbsp;<span style="line-height: normal; font-size: 10.5pt; ">，对于函数定义：</span></p><p style="line-height: normal; "><span style="line-height: normal; font-size: 10.5pt; ">template &lt;class T&gt;</span></p><p style="line-height: normal; "><span style="line-height: normal; font-size: 10.5pt; ">int getArrayLen(T&amp; array)</span></p><p style="line-height: normal; "><span style="line-height: normal; font-size: 10.5pt; ">{</span></p><p style="line-height: normal; "><span style="line-height: normal; font-size: 10.5pt; ">return (sizeof(array) / sizeof(array[0]) - 1);</span></p><p style="line-height: normal; "><span style="line-height: normal; font-size: 10.5pt; ">}</span></p><p style="line-height: normal; "><span style="line-height: normal; font-size: 10.5pt; ">原因为存储字符串的字符数组末尾有一个'\0'字符，需要去掉它。</span></p><p style="line-height: normal; "><span style="line-height: normal; font-size: 10.5pt; ">【C语言】</span></p><p style="line-height: normal; "><span style="line-height: normal; font-size: 10.5pt; ">#include &lt;stdio.h&gt;</span></p><p style="line-height: normal; "><span style="line-height: normal; font-size: 10.5pt; ">#include &lt;stdlib.h&gt;</span></p><p style="line-height: normal; "></p><p style="line-height: normal; "><span style="line-height: normal; font-size: 10.5pt; ">#define GET_ARRAY_LEN(array,len){len = (sizeof(array) / sizeof(array[0]));}</span></p><p style="line-height: normal; "><span style="line-height: normal; font-size: 10.5pt; ">//定义一个带参数的宏，将数组长度存储在变量len中</span></p><p style="line-height: normal; "><span style="line-height: normal; font-size: 10.5pt; ">int main()</span></p><p style="line-height: normal; "><span style="line-height: normal; font-size: 10.5pt; ">{</span></p><p style="line-height: normal; "><span style="line-height: normal; font-size: 10.5pt; ">char a[] = {'1','2','3','4'};</span></p><p style="line-height: normal; "><span style="line-height: normal; font-size: 10.5pt; ">int len;</span></p><p style="line-height: normal; "></p><p style="line-height: normal; "><span style="line-height: normal; font-size: 10.5pt; ">GET_ARRAY_LEN(a,len)</span></p><p style="line-height: normal; text-indent: 21pt; "><span style="line-height: normal; font-size: 10.5pt; ">//调用预定义的宏，取得数组a的长度，并将其存储在变量len中</span></p><p style="line-height: normal; "><span style="line-height: normal; font-size: 10.5pt; ">printf("%d\n",len);</span></p><p style="line-height: normal; "></p><p style="line-height: normal; "><span style="line-height: normal; font-size: 10.5pt; ">system("pause");</span></p><p style="line-height: normal; "><span style="line-height: normal; font-size: 10.5pt; ">return 0;</span></p><p style="line-height: normal; "><span style="line-height: normal; font-size: 10.5pt; ">}</span></p><p style="line-height: normal; "><span style="line-height: normal; font-size: 10.5pt; ">【C++】</span></p><p style="line-height: normal; "><span style="line-height: normal; font-size: 10.5pt; ">#include &lt;iostream&gt;</span></p><p style="line-height: normal; "><span style="line-height: normal; font-size: 10.5pt; ">using namespace std;</span></p><p style="line-height: normal; "></p><p style="line-height: normal; "><span style="line-height: normal; font-size: 10.5pt; ">template &lt;class T&gt;</span></p><p style="line-height: normal; "><span style="line-height: normal; font-size: 10.5pt; ">int getArrayLen(T&amp; array)</span></p><p style="line-height: normal; "><span style="line-height: normal; font-size: 10.5pt; ">{//使用模板定义一个函数getArrayLen,该函数将返回数组array的长度</span></p><p style="line-height: normal; "><span style="line-height: normal; font-size: 10.5pt; ">return (sizeof(array) / sizeof(array[0]));</span></p><p style="line-height: normal; "><span style="line-height: normal; font-size: 10.5pt; ">}</span></p><p style="line-height: normal; "><span style="line-height: normal; font-size: 10.5pt; ">int main()</span></p><p style="line-height: normal; "><span style="line-height: normal; font-size: 10.5pt; ">{</span></p><p style="line-height: normal; "><span style="line-height: normal; font-size: 10.5pt; ">char a[] = {'1','2','3'};</span></p><p style="line-height: normal; "><span style="line-height: normal; font-size: 10.5pt; ">cout &lt;&lt; getArrayLen(a) &lt;&lt; endl;</span></p><p style="line-height: normal; "></p><p style="line-height: normal; "><span style="line-height: normal; font-size: 10.5pt; ">return 0;</span></p><p style="line-height: normal; "><span style="line-height: normal; font-size: 10.5pt; ">}</span></p><div><span style="line-height: normal; font-size: 10.5pt; "><br /></span></div></div></div></td></tr></tbody></table></span>C/C++中如何获取数组的长度？</span><img src ="http://www.cppblog.com/lauer3912/aggbug/148523.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-06-12 10:36 <a href="http://www.cppblog.com/lauer3912/archive/2011/06/12/148523.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在VC中编译、运行程序的小知识点</title><link>http://www.cppblog.com/lauer3912/archive/2011/05/12/146286.html</link><dc:creator>RTY</dc:creator><author>RTY</author><pubDate>Thu, 12 May 2011 14:06:00 GMT</pubDate><guid>http://www.cppblog.com/lauer3912/archive/2011/05/12/146286.html</guid><wfw:comment>http://www.cppblog.com/lauer3912/comments/146286.html</wfw:comment><comments>http://www.cppblog.com/lauer3912/archive/2011/05/12/146286.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lauer3912/comments/commentRss/146286.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lauer3912/services/trackbacks/146286.html</trackback:ping><description><![CDATA[<span  style="font-family: Simsun; "><h3>1、Run-Time Library</h3>Run-Time Library是编译器提供的标准库，提供一些基本的库函数和系统调用。<br>我们一般使用的Run-Time Library是C Run-Time Libraries。当然也有Standard C++ libraries。&nbsp;<br>C Run-Time Libraries实现ANSI C的标准库。VC安装目录的CRT目录有C Run-Time库的大部分源代码。<p>C Run-Time Libraries有静态库版本，也有动态链接库版本；有单线程版本，也有多线程版本；还有调试和非调试版本。<br>可以在"project"-"settings"-"C/C++"-"Code Generation"中选择Run-Time Library的版本。</p><p>动态链接库版本：<br>/MD Multithreaded DLL 使用导入库MSVCRT.LIB<br>/MDd Debug Multithreaded DLL 使用导入库MSVCRTD.LIB</p><p>静态库版本：<br>/ML Single-Threaded 使用静态库LIBC.LIB&nbsp;<br>/MLd Debug Single-Threaded 使用静态库LIBCD.LIB<br>/MT Multithreaded 使用静态库LIBCMT.LIB<br>/MTd Debug Multithreaded 使用静态库LIBCMTD.LIB</p><p>C Run-Time Library的标准io部分与操作系统的关系很密切，在Windows上，CRT的io部分代码只是一个包装，底层要用到操作系统内核kernel32.dll中的函数，在编译时使用导入库kernel32.lib。这也就是为什么在嵌入式环境中，我们一般不能直接使用C标准库。<br>在Linux环境当然也有C标准库，例如：<br>ld -o output /lib/crt0.o hello.o -lc<br>参数"-lc"就是在引用C标准库libc.a。猜一猜"-lm"引用哪个库文件？</p><h3>2、常见的编译参数</h3>VC建立项目时总会定义"Win32"。控制台程序会定义"_CONSOLE"，否则会定义"_WINDOWS"。Debug版定义"_DEBUG"，Release版定义"NDEBUG"<p>&#160;</p><p>与MFC DLL有关的编译常数包括：<br>_WINDLL 表示要做一个用到MFC的DLL<br>_USRDLL 表示做一个用户DLL（相对MFC扩展DLL而言）&nbsp;<br>_AFXDLL 表示使用MFC动态链接库<br>_AFXEXT 表示要做一个MFC扩展DLL<br>所以：<br>Regular, statically linked to MFC _WINDLL,_USRDLL&nbsp;<br>Regular, using the shared MFC DLL _WINDLL,_USRDLL,_AFXDLL<br>Extension DLL _WINDLL,_AFXDLL,_AFXEXT</p><p>CL.EXE编译所有源文件，LINK.EXE链接EXE和DLL，LIB.EXE产生静态库。</p><h3>3、subsystem和可执行文件的启动</h3>LINK的时候需要指定/subsystem，这个链接选项告诉Windows如何运行可执行文件。<br>控制台程序是/subsystem:"console"<br>其它程序一般都是/subsystem:"windows "<p>&#160;</p><p>将 subsystem 选成"console"后，Windows在进入可执行文件的代码前（如mainCRTStartup），就会产生一个控制台窗口。<br>如果选择"windows"，操作系统就不产生console窗口,该类型应用程序的窗口由用户自己创建。</p><p>可执行文件都有一个Entry Point，LINK时可以用/entry指定。缺省情况下，如果subsystem是&#8220;console&#8221;，Entry Point是 mainCRTStartup(ANSI)或wmainCRTStartuup(UNICODE)，即：<br>/subsystem:"console" /entry:"mainCRTStartup" (ANSI)<br>/subsystem:"console" /entry:"wmainCRTStartuup" (UNICODE)<br>mainCRTStartup 或 wmainCRTStartuup 会调用main或wmain。<br>值得一提的是，在进入应用程序的Entry Point前，Windows的装载器已经做过C变量的初始化，有初值的全局变量拥有了它们的初值，没有初值的变量被设为0。</p><p>如果subsystem是&#8220;windows&#8221;，Entry Point是WinMain(ANSI)或wWinMain(UINCODE)，即：<br>/subsystem:"windows" /entry:"WinMainCRTStartup" (ANSI)<br>/sbusystem:"windows" /entry:"wWinMainCRTStartup" (UINCODE)<br>WinMainCRTStartup 或 wWinMainCRTStartup 会调用 WinMain 或 wWinMain。</p><p>这些入口点函数，在CRT目录都可以看到源代码，例如（为了简洁，我删除了原代码的一些条件编译）：</p><pre>void mainCRTStartup(void)
{
        int mainret;

        /* Get the full Win32 version */
        _osver = GetVersion();
        _winminor = (_osver &gt;&gt; 8) &amp; 0x00FF ;
        _winmajor = _osver &amp; 0x00FF ;
        _winver = (_winmajor &lt;&lt; 8) + _winminor;
        _osver = (_osver &gt;&gt; 16) &amp; 0x00FFFF ;

#ifdef _MT
        if ( !_heap_init(1) )               /* initialize heap */
#else  /* _MT */
        if ( !_heap_init(0) )               /* initialize heap */
#endif  /* _MT */
            fast_error_exit(_RT_HEAPINIT);  /* write message and die */

#ifdef _MT
        if( !_mtinit() )                    /* initialize multi-thread */
            fast_error_exit(_RT_THREAD);    /* write message and die */
#endif  /* _MT */

        __try {
            _ioinit();                      /* initialize lowio */
            _acmdln = (char *)GetCommandLineA();        /* get cmd line info */
            _aenvptr = (char *)__crtGetEnvironmentStringsA();        /* get environ info */
            _setargv();
            _setenvp();
            __initenv = _environ;
            mainret = main(__argc, __argv, _environ);
            exit(mainret);
        }
        __except ( _XcptFilter(GetExceptionCode(), GetExceptionInformation()) )
        {
            _exit( GetExceptionCode() );        /* Should never reach here */
        } /* end of try - except */
}  </pre><p>如果使用MFC框架，WinMain也会被埋藏在MFC库中（APPMODUL.CPP）：<br>extern "C" int WINAPI<br>_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,<br>LPTSTR lpCmdLine, int nCmdShow)<br>{<br>// call shared/exported WinMain<br>return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);<br>}<br>对于ANSI版本，"_tWinMain"就是"WinMain"；对于UINCODE版本，"_tWinMain"就是"wWinMain"。可参见afx.h：</p><p>#ifdef _UNICODE<br>#define _tmain wmain<br>#define _tWinMain wWinMain<br>#else<br>#define _tmain main<br>#define _tWinMain WinMain<br>#endif</p><p>全局C++对象的构造函数是在什么地方调用的？答案是在进入应用程序的Entry Point后，在调用main函数前的初始化操作中。所以MFC的theApp的构造函数是在_tWinMain之前调用的。</p><h3>4、不显示Console窗口的Console程序</h3>在默认情况下/subsystem 和/entry开关是匹配的,也就是：<br>"console"对应"mainCRTStartup"或者"wmainCRTStartup"<br>"windows"对应"WinMain"或者"wWinMain"<br>我们可以通过手动修改的方法使他们不匹配。例如：<p>&#160;</p><p>#include "windows.h"<br>#pragma comment( linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"" ) // 设置入口地址&nbsp;<br>void main(void)<br>{<br>MessageBox(NULL, "hello", "Notice", MB_OK);<br>}</p><p>这个Console程序就不会显示Console窗口。如果选/MLd的话，这个程序只需要链接LIBCD.LIB user32.lib kernel32.lib。</p><p>其实如果不想看到Console窗口，还有一个更直接的方法：那就是直接在EXE文件中将PE文件头的Subsystem从3改成2。在EXE文件中，PE文件头的偏移地址是0x3c，Subsystem是一个WORD，它在PE文件头中的偏移是0x5c。</p><h3>5、MFC的库文件</h3>MFC的库可以静态链接，也可以动态链接。静态库和动态库又有Debug和Release，ANSI和Unicode版本之分。<p>&#160;</p><p>静态MFC库主要有：<br>ANSI Debug NAFXCWD.LIB<br>ANSI Release NAFXCW.LIB<br>Unicode Debug UAFXCWD.LIB<br>Unicode Release UAFXCW.LIB&nbsp;<br><br>动态链接库主要有;<br>ANSI Debug MFCxxD.LIB (core，MFCxxD.DLL),&nbsp;<br>MFCOxxD.LIB (OLE，MFCOxxD.DLL),&nbsp;<br>MFCDxxD.LIB (database，MFCDxxD.DLL),&nbsp;<br>MFCNxxD.LIB (network，MFCNxxD.DLL),&nbsp;<br>MFCSxxD.LIB (static)</p><p>ANSI Release MFCxx.LIB (combined，MFCxx.DLL)<br>MFCSxx.LIB (static)</p><p>Unicode Debug MFCxxUD.LIB (core，MFCxxUD.DLL),&nbsp;<br>MFCOxxUD.LIB (OLE，MFCOxxUD.DLL),&nbsp;<br>MFCDxxUD.LIB (database，MFCDxxUD.DLL),&nbsp;<br>MFCNxxUD.LIB (network，MFCNxxUD.DLL),&nbsp;<br>MFCSxxUD.LIB (static)</p><p>Unicode Release MFCxxU.DLL (combined，MFCxxU.DLL),&nbsp;<br>MFCSxxU.LIB (static)</p><p>上面的LIB文件除了MFCSxx(D、U、UD).LIB以外都是导入库。<br>MFC动态链接库版本也需要静态链接一些文件，这些文件就放在MFCSxx(D、U、UD).LIB中。例如包含_tWinMain的appmodul.cpp。</p><h3>6、结束语</h3><p>研究这些问题的动机是想弄清楚我们的程序是如何装载、运行的。但是，由于Windows不是开源平台，我也只能研究到PE文件（Windows上可执行文件的格式）。entry point、subsystem都是PE文件头的一部分。</p><p>Windows在进入PE文件的entry point之前做了些什么，就看不到了，只能大概推测：应该是创建一个进程，装载PE文件和所有需要的DLL，初始化C变量，然后从某个起点函数开始运行。不同的subsystem，应该有不同的起点。调用这个起点函数时应该传入PE文件的entry point地址。</p><p>&nbsp;</p></span>
<img src ="http://www.cppblog.com/lauer3912/aggbug/146286.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-05-12 22:06 <a href="http://www.cppblog.com/lauer3912/archive/2011/05/12/146286.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>函数调用约定和堆栈(转载)</title><link>http://www.cppblog.com/lauer3912/archive/2011/05/12/146285.html</link><dc:creator>RTY</dc:creator><author>RTY</author><pubDate>Thu, 12 May 2011 14:00:00 GMT</pubDate><guid>http://www.cppblog.com/lauer3912/archive/2011/05/12/146285.html</guid><wfw:comment>http://www.cppblog.com/lauer3912/comments/146285.html</wfw:comment><comments>http://www.cppblog.com/lauer3912/archive/2011/05/12/146285.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lauer3912/comments/commentRss/146285.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lauer3912/services/trackbacks/146285.html</trackback:ping><description><![CDATA[<span  style="font-family: Simsun; "><h3>1 什么是堆栈</h3><p>编译器一般使用堆栈实现函数调用。堆栈是存储器的一个区域，嵌入式环境有时需要程序员自己定义一个数组作为堆栈。Windows为每个线程自动维护一个堆栈，堆栈的大小可以设置。编译器使用堆栈来堆放每个函数的参数、局部变量等信息。</p><p>函数调用经常是嵌套的，在同一时刻，堆栈中会有多个函数的信息，每个函数占用一个连续的区域。一个函数占用的区域被称作帧（frame）。</p><p><img src="http://www.fmddlmyy.cn/images/stack.JPG" width="518" height="379"></p><p>编译器从高地址开始使用堆栈。 假设我们定义一个数组a[1024]作为堆栈空间，一开始栈顶指针指向a[1023]。如果栈里有两个函数a和b，且a调用了b，栈顶指针会指向函数b的帧。如果函数b返回。栈顶指针就指向函数a的帧。如果在栈里放了太多东西造成溢出，破坏的是a[0]上面的东西。</p><p>在多线程（任务）环境，CPU的堆栈指针指向的存储器区域就是当前使用的堆栈。切换线程的一个重要工作，就是将堆栈指针设为当前线程的堆栈栈顶地址。</p><p>不同CPU，不同编译器的堆栈布局、函数调用方法都可能不同，但堆栈的基本概念是一样的。</p><h3>2 函数调用约定</h3><p>函数调用约定包括传递参数的顺序，谁负责清理参数占用的堆栈等，例如 ：</p><table width="52%" border="1"><tbody><tr><td width="16%">&nbsp;</td><td width="24%">参数传递顺序</td><td width="60%">谁负责清理参数占用的堆栈</td></tr><tr><td>__pascal</td><td>从左到右</td><td>调用者</td></tr><tr><td>__stdcall</td><td>从右到左</td><td>被调函数</td></tr><tr><td>__cdecl</td><td>从右到左</td><td>调用者</td></tr></tbody></table><p>调用函数的代码和被调函数必须采用相同的函数的调用约定，程序才能正常运行。在Windows上，__cdecl是C/C++程序的缺省函数调用约定。</p><p>在有的cpu上，编译器会用寄存器传递参数，函数使用的堆栈由被调函数分配和释放。这种调用约定在行为上和__cdecl有一个共同点：实参和形参数目不符不会导致堆栈错误。</p><p>不过，即使用寄存器传递参数，编译器在进入函数时，还是会将寄存器里的参数存入堆栈指定位置。参数和局部变量一样应该在堆栈中有一席之地。参数可以被理解为由调用函数指定初值的局部变量。</p><h3>3 例子：__cdecl和__stdcall</h3><p>不同的CPU，不同的编译器，堆栈的布局可能是不同的。本文以x86，VC++的编译器为例。</p><p>VC++编译器的已经不再支持__pascal, __fortran, __syscall等函数调用约定。目前只支持__cdecl和__stdcall。</p><p>采用__cdecl或__stdcall调用方式的程序，在刚进入子函数时，堆栈内容是一样的。esp指向的栈顶是返回地址。这是被call指令压入堆栈的。下面是参数，左边参数在上，右边参数在下（先入栈）。</p><p>如前表所示，__cdecl和__stdcall的区别是：__cdecl是调用者清理参数占用的堆栈，__stdcall是被调函数清理参数占用的堆栈。</p><p>由于__stdcall的被调函数在编译时就必须知道传入参数的准确数目（被调函数要清理堆栈），所以不能支持变参数函数，例如printf。而且如果调用者使用了不正确的参数数目，会导致堆栈错误。</p><p>通过查看汇编代码，__cdecl函数调用在call语句后会有一个堆栈调整语句，例如：</p><ul>a = 0x1234;<br>b = 0x5678;<br>c = add(a, b);</ul><p>对应x86汇编：</p><ul>mov dword ptr [ebp-4],1234h<br>mov dword ptr [ebp-8],5678h<br>mov eax,dword ptr [ebp-8]<br>push eax<br>mov ecx,dword ptr [ebp-4]<br>push ecx<br>call 0040100a<br><strong>add esp,8<br></strong>mov dword ptr [ebp-0Ch],eax</ul><p><br>__stdcall的函数调用则不需要调整堆栈：</p><ul>call 00401005<br>mov dword ptr [ebp-0Ch],eax</ul><p>函数</p><ul>int __cdecl add(int a, int b)<br>{<br>return a+b;<br>}</ul><p>产生以下汇编代码（Debug版本）：</p><ul>push ebp<br>mov ebp,esp<br>sub esp,40h<br>push ebx<br>push esi<br>push edi<br>lea edi,[ebp-40h]<br>mov ecx,10h<br>mov eax,0CCCCCCCCh<br>rep stos dword ptr [edi]<br>mov eax,dword ptr [ebp+8]<br>add eax,dword ptr [ebp+0Ch]<br>pop edi<br>pop esi<br>pop ebx<br>mov esp,ebp<br>pop ebp<br><strong>ret // 跳转到esp所指地址,并将esp+4,使esp指向进入函数时的第一个参数</strong></ul><p>再查看__stdcall函数的实现，会发现与__cdecl函数只有最后一行不同：</p><ul>ret 8 // 执行ret并清理参数占用的堆栈</ul><p>对于调试版本，VC++编译器在&#8220;直接调用地址&#8221;时会增加检查esp的代码，例如：</p><ul>ta = (TAdd)add; // TAdd定义：typedef int (__cdecl *TAdd)(int a, int b);<br>c = ta(a, b);</ul><p>产生以下汇编代码：</p><ul>mov [ebp-10h],0040100a<br>mov esi,esp<br>mov ecx,dword ptr [ebp-8]<br>push ecx<br>mov edx,dword ptr [ebp-4]<br>push edx<br><strong>call dword ptr [ebp-10h]<br>add esp,8<br></strong>cmp esi,esp<br><strong>call __chkesp (004011e0)</strong><br>mov dword ptr [ebp-0Ch],eax</ul><p>__chkesp 代码如下。如果esp不等于函数调用前保存的值，就会转到错误处理代码。</p><ul>004011E0 jne __chkesp+3 (004011e3)<br>004011E2 ret<br>004011E3 ;错误处理代码</ul><p>__chkesp的错误处理会弹出对话框，报告函数调用造成esp值不正确。 Release版本的汇编代码要简洁得多。也不会增加 __chkesp。如果发生esp错误，程序会继续运行，直到&#8220;遇到问题需要关闭&#8221;。</p><h3>3 补充说明</h3><p>函数调用约定只是&#8220;调用函数的代码&#8221;和被调用函数之间的关系。</p><p>假设函数A是__stdcall，函数B调用函数A。你必须通过函数声明告诉编译器，函数A是__stdcall。编译器自然会产生正确的调用代码。</p><p>如果函数A是__stdcall。但在引用函数A的地方，你却告诉编译器，函数A是__cdecl方式，编译器产生__cdecl方式的代码，与函数A的调用约定不一致，就会发生错误。</p><p>以delphi调用VC函数为例，delphi的函数缺省采用__pascal约定，VC的函数缺省采用__cdecl约定。我们一般将VC的函数设为__stdcall，例如：</p><ul>int __stdcall add(int a, int b);</ul><p>在delphi中将这个函数也声明为__stdcall，就可以调用了：</p><ul>function add(a: Integer; b: Integer): Integer;<br>stdcall; external 'a.dll';</ul><p>因为考虑到可能被其它语言的程序调用，不少API采用__stdcall的调用约定。</p></span>
<img src ="http://www.cppblog.com/lauer3912/aggbug/146285.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-05-12 22:00 <a href="http://www.cppblog.com/lauer3912/archive/2011/05/12/146285.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>浅谈文字编码和Unicode（下）(转载)</title><link>http://www.cppblog.com/lauer3912/archive/2011/05/12/146284.html</link><dc:creator>RTY</dc:creator><author>RTY</author><pubDate>Thu, 12 May 2011 13:58:00 GMT</pubDate><guid>http://www.cppblog.com/lauer3912/archive/2011/05/12/146284.html</guid><wfw:comment>http://www.cppblog.com/lauer3912/comments/146284.html</wfw:comment><comments>http://www.cppblog.com/lauer3912/archive/2011/05/12/146284.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lauer3912/comments/commentRss/146284.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lauer3912/services/trackbacks/146284.html</trackback:ping><description><![CDATA[<span  style="font-family: Simsun; "><h2>3 字符编码模型</h2><p>程序员经常会面对复杂的问题，而降低复杂性的最简单的方法就是分而治之。Peter Constable在他的文章<a href="http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&amp;item_id=IWS-Chapter03" target="_top">"Character set encoding basics Understanding character set encodings and legacy encodings"</a>中描述了字符编码的四层模型。我觉得这种说法确实可以更清晰地展现字符编码中发生的事情，所以在这里也介绍一下。</p><h3>3.1 字符的范围（Abstract character repertoire）</h3><p>设计字符编码的第一层就是确定字符的范围，即要支持哪些字符。有些编码方案的字符范围是固定的，例如ASCII、ISO 8859 系列。有些编码方案的字符范围是开放的，例如Unicode的字符范围就是世界上所有的字符。</p><h3>3.2 用数字表示字符（Coded character set）</h3><p>设计字符编码的第二层是将字符和数字对应起来。可以将这个层次理解成数学家（即从数学角度）看到的字符编码。数学家看到的字符编码是一个正整数。例如在Unicode中：汉字&#8220;字&#8221;对应的数字是23383。汉字&#8220;<img src="http://www.fmddlmyy.cn/images/134192.png" width="14" height="15" border="0" alt="">&#8221;对应的数字是134192。</p><p>在写html文件时，可以通过输入"&#23383;"来插入字符&#8220;字&#8221;。不过在设计字符编码时，我们还是习惯用16进制表示数字。即将23383写成0x5BD7，将134192写成0x20C30。</p><h3>3.3 用基本数据类型表示字符（Character encoding form）</h3><p>设计字符编码的第三层是用编程语言中的基本数据类型来表示字符。可以将这个层次理解成程序员看到的字符编码。在Unicode中，我们有很多方式将数字23383表示成程序中的数据，包括：UTF-8、UTF-16、UTF-32。UTF是&#8220;UCS Transformation Format&#8221;的缩写，可以翻译成Unicode字符集转换格式，即怎样将Unicode定义的数字转换成程序数据。例如，&#8220;汉字&#8221;对应的数字是0x6c49和0x5b57，而编码的程序数据是：</p><p>&#160;</p><pre>	BYTE data_utf8[] = {0xE6, 0xB1, 0x89, 0xE5, 0xAD, 0x97};	// UTF-8编码
	WORD data_utf16[] = {0x6c49, 0x5b57};				// UTF-16编码
	DWORD data_utf32[] = {0x6c49, 0x5b57};				// UTF-32编码</pre><p>&#160;</p><p>这里用BYTE、WORD、DWORD分别表示无符号8位整数，无符号16位整数和无符号32位整数。UTF-8、UTF-16、UTF-32分别以BYTE、WORD、DWORD作为编码单位。</p><p>&#8220;汉字&#8221;的UTF-8编码需要6个字节。&#8220;汉字&#8221;的UTF-16编码需要两个WORD，大小是4个字节。&#8220;汉字&#8221;的UTF-32编码需要两个DWORD，大小是8个字节。4.2节会介绍将数字映射到UTF编码的规则。</p><h3>3.4 作为字节流的字符（Character encoding scheme）</h3><p>字符编码的第四层是计算机看到的字符，即在文件或内存中的字节流。例如，&#8220;字&#8221;的UTF-32编码是0x5b57，如果用little endian表示，字节流是&#8220;57 5b 00 00&#8221;。如果用big endian表示，字节流是&#8220;00 00 5b 57&#8221;。</p><p>字符编码的第三层规定了一个字符由哪些编码单位按什么顺序表示。字符编码的第四层在第三层的基础上又考虑了编码单位内部的字节序。UTF-8的编码单位是字节，不受字节序的影响。UTF-16、UTF-32根据字节序的不同，又衍生出UTF-16LE、UTF-16BE、UTF-32LE、UTF-32BE四种编码方案。LE和BE分别是Little Endian和Big Endian的缩写。</p><h3>3.5 小结</h3><p>通过四层模型，我们又把字符编码中发生的这些事情梳理了一遍。其实大多数代码页都不需要完整的四层模型，例如GB18030以字节为编码单位，直接规定了字节序列和字符的映射关系，跳过了第二层，也不需要第四层。</p><h2>4 再谈Unicode</h2><p>Unicode是国际组织制定的可以容纳世界上所有文字和符号的字符编码方案。Unicode用数字0-0x10FFFF来映射这些字符，最多可以容纳1114112个字符，或者说有1114112个码位。码位就是可以分配给字符的数字。UTF-8、UTF-16、UTF-32都是将数字转换到程序数据的编码方案。</p><p>Unicode字符集可以简写为UCS（Unicode Character Set）。早期的Unicode标准有UCS-2、UCS-4的说法。UCS-2用两个字节编码，UCS-4用4个字节编码。UCS-4根据最高位为0的最高字节分成2^7=128个group。每个group再根据次高字节分为256个平面（plane）。每个平面根据第3个字节分为256行 （row），每行有256个码位（cell）。group 0的平面0被称作BMP（Basic Multilingual Plane）。将UCS-4的BMP去掉前面的两个零字节就得到了UCS-2。</p><p>Unicode标准计划使用group 0 的17个平面: 从BMP（平面0）到平面16，即数字0-0x10FFFF。《谈谈Unicode编码》主要介绍了BMP的编码，本文将介绍完整的Unicode编码，并从多个角度浏览Unicode。本文的介绍基于Unicode 5.0.0版本。</p><h3>4.1 浏览Unicode</h3><p>先看一些数字：每个平面有2^16=65536个码位。Unicode计划使用了17个平面，一共有17*65536=1114112个码位。其实，现在已定义的码位只有238605个，分布在平面0、平面1、平面2、平面14、平面15、平面16。其中平面15和平面16上只是定义了两个各占65534个码位的专用区（Private Use Area），分别是0xF0000-0xFFFFD和0x100000-0x10FFFD。所谓专用区，就是保留给大家放自定义字符的区域，可以简写为PUA。</p><p>平面0也有一个专用区：0xE000-0xF8FF，有6400个码位。平面0的0xD800-0xDFFF，共2048个码位，是一个被称作代理区（Surrogate）的特殊区域。它的用途将在4.2节介绍。</p><p>238605-65534*2-6400-2408=99089。余下的99089个已定义码位分布在平面0、平面1、平面2和平面14上，它们对应着Unicode目前定义的99089个字符，其中包括71226个汉字。平面0、平面1、平面2和平面14上分别定义了52080、3419、43253和337个字符。平面2的43253个字符都是汉字。平面0上定义了27973个汉字。</p><p>在更深入地了解Unicode字符前，我们先了解一下UCD。</p><h4>4.1.1 什么是UCD</h4><p>UCD是Unicode字符数据库（Unicode Character Database）的缩写。UCD由一些描述Unicode字符属性和内部关系的纯文本或html文件组成。大家可以在Unicode组织的网站看到UCD的<a href="http://www.unicode.org/ucd/">最新版本</a>。</p><p>UCD中的文本文件大都是适合于程序分析的Unicode相关数据。其中的html文件解释了数据库的组织，数据的格式和含义。UCD中最庞大的文件无疑就是描述汉字属性的文件Unihan.txt。在UCD 5.0,0中，Unihan.txt文件大小有28,221K字节。Unihan.txt中包含了很多有参考价值的索引，例如汉字部首、笔划、拼音、使用频度、四角号码排序等。这些索引都是基于一些比较权威的辞典，但大多数索引只能检索部分汉字。</p><p>我介绍UCD的目的主要是为了使用其中的两个概念：Block和Script。</p><h4>4.1.2 Block</h4><p>UCD中的Blocks.txt将Unicode的码位分割成一些连续的Block，并描述了每个Block的用途：</p><table border="1"><tbody><tr><td>开始码位</td><td>结束码位</td><td>Block名称（英文）</td><td>Block名称（中文）</td></tr><tr><td>0000</td><td>007F</td><td>Basic Latin</td><td>基本拉丁字母</td></tr><tr><td>0080</td><td>00FF</td><td>Latin-1 Supplement</td><td>拉丁字母补充-1</td></tr><tr><td>0100</td><td>017F</td><td>Latin Extended-A</td><td>拉丁字母扩充-A</td></tr><tr><td>0180</td><td>024F</td><td>Latin Extended-B</td><td>拉丁字母扩充-B</td></tr><tr><td>0250</td><td>02AF</td><td>IPA Extensions</td><td>国际音标扩充</td></tr><tr><td>02B0</td><td>02FF</td><td>Spacing Modifier Letters</td><td>进格修饰字符</td></tr><tr><td>0300</td><td>036F</td><td>Combining Diacritical Marks</td><td>组合附加符号</td></tr><tr><td>0370</td><td>03FF</td><td>Greek and Coptic</td><td>希腊文和哥普特文</td></tr><tr><td>0400</td><td>04FF</td><td>Cyrillic</td><td>西里尔文</td></tr><tr><td>0500</td><td>052F</td><td>Cyrillic Supplement</td><td>西里尔文补充</td></tr><tr><td>0530</td><td>058F</td><td>Armenian</td><td>亚美尼亚文</td></tr><tr><td>0590</td><td>05FF</td><td>Hebrew</td><td>希伯来文</td></tr><tr><td>0600</td><td>06FF</td><td>Arabic</td><td>基本阿拉伯文</td></tr><tr><td>0700</td><td>074F</td><td>Syriac</td><td>叙利亚文</td></tr><tr><td>0750</td><td>077F</td><td>Arabic Supplement</td><td>阿拉伯文补充</td></tr><tr><td>0780</td><td>07BF</td><td>Thaana</td><td>塔纳文</td></tr><tr><td>07C0</td><td>07FF</td><td>NKo</td><td>N'Ko字母表</td></tr><tr><td>0900</td><td>097F</td><td>Devanagari</td><td>天成文书（梵文）</td></tr><tr><td>0980</td><td>09FF</td><td>Bengali</td><td>孟加拉文</td></tr><tr><td>0A00</td><td>0A7F</td><td>Gurmukhi</td><td>锡克教文</td></tr><tr><td>0A80</td><td>0AFF</td><td>Gujarati</td><td>古吉拉特文</td></tr><tr><td>0B00</td><td>0B7F</td><td>Oriya</td><td>奥里亚文</td></tr><tr><td>0B80</td><td>0BFF</td><td>Tamil</td><td>泰米尔文</td></tr><tr><td>0C00</td><td>0C7F</td><td>Telugu</td><td>泰卢固文</td></tr><tr><td>0C80</td><td>0CFF</td><td>Kannada</td><td>卡纳达文</td></tr><tr><td>0D00</td><td>0D7F</td><td>Malayalam</td><td>德拉维族文</td></tr><tr><td>0D80</td><td>0DFF</td><td>Sinhala</td><td>僧伽罗文</td></tr><tr><td>0E00</td><td>0E7F</td><td>Thai</td><td>泰文</td></tr><tr><td>0E80</td><td>0EFF</td><td>Lao</td><td>老挝文</td></tr><tr><td>0F00</td><td>0FFF</td><td>Tibetan</td><td>藏文</td></tr><tr><td>1000</td><td>109F</td><td>Myanmar</td><td>缅甸文</td></tr><tr><td>10A0</td><td>10FF</td><td>Georgian</td><td>格鲁吉亚文</td></tr><tr><td>1100</td><td>11FF</td><td>Hangul Jamo</td><td>朝鲜文</td></tr><tr><td>1200</td><td>137F</td><td>Ethiopic</td><td>埃塞俄比亚文</td></tr><tr><td>1380</td><td>139F</td><td>Ethiopic Supplement</td><td>埃塞俄比亚文补充</td></tr><tr><td>13A0</td><td>13FF</td><td>Cherokee</td><td>切罗基文</td></tr><tr><td>1400</td><td>167F</td><td>Unified Canadian Aboriginal Syllabics</td><td>加拿大印第安方言</td></tr><tr><td>1680</td><td>169F</td><td>Ogham</td><td>欧甘文</td></tr><tr><td>16A0</td><td>16FF</td><td>Runic</td><td>北欧古字</td></tr><tr><td>1700</td><td>171F</td><td>Tagalog</td><td>塔加路文</td></tr><tr><td>1720</td><td>173F</td><td>Hanunoo</td><td>哈努诺文</td></tr><tr><td>1740</td><td>175F</td><td>Buhid</td><td>布迪文</td></tr><tr><td>1760</td><td>177F</td><td>Tagbanwa</td><td>Tagbanwa文</td></tr><tr><td>1780</td><td>17FF</td><td>Khmer</td><td>高棉文</td></tr><tr><td>1800</td><td>18AF</td><td>Mongolian</td><td>蒙古文</td></tr><tr><td>1900</td><td>194F</td><td>Limbu</td><td>林布文</td></tr><tr><td>1950</td><td>197F</td><td>Tai Le</td><td>德宏傣文</td></tr><tr><td>1980</td><td>19DF</td><td>New Tai Lue</td><td>新傣文</td></tr><tr><td>19E0</td><td>19FF</td><td>Khmer Symbols</td><td>高棉文</td></tr><tr><td>1A00</td><td>1A1F</td><td>Buginese</td><td>布吉文</td></tr><tr><td>1B00</td><td>1B7F</td><td>Balinese</td><td>巴厘文</td></tr><tr><td>1D00</td><td>1D7F</td><td>Phonetic Extensions</td><td>拉丁字母音标扩充</td></tr><tr><td>1D80</td><td>1DBF</td><td>Phonetic Extensions Supplement</td><td>拉丁字母音标扩充增补</td></tr><tr><td>1DC0</td><td>1DFF</td><td>Combining Diacritical Marks Supplement</td><td>组合附加符号补充</td></tr><tr><td>1E00</td><td>1EFF</td><td>Latin Extended Additional</td><td>拉丁字母扩充附加</td></tr><tr><td>1F00</td><td>1FFF</td><td>Greek Extended</td><td>希腊文扩充</td></tr><tr><td>2000</td><td>206F</td><td>General Punctuation</td><td>一般标点符号</td></tr><tr><td>2070</td><td>209F</td><td>Superscripts and Subscripts</td><td>上标和下标</td></tr><tr><td>20A0</td><td>20CF</td><td>Currency Symbols</td><td>货币符号</td></tr><tr><td>20D0</td><td>20FF</td><td>Combining Diacritical Marks for Symbols</td><td>符号用组合附加符号</td></tr><tr><td>2100</td><td>214F</td><td>Letterlike Symbols</td><td>似字母符号</td></tr><tr><td>2150</td><td>218F</td><td>Number Forms</td><td>数字形式</td></tr><tr><td>2190</td><td>21FF</td><td>Arrows</td><td>箭头符号</td></tr><tr><td>2200</td><td>22FF</td><td>Mathematical Operators</td><td>数学运算符号</td></tr><tr><td>2300</td><td>23FF</td><td>Miscellaneous Technical</td><td>零杂技术用符号</td></tr><tr><td>2400</td><td>243F</td><td>Control Pictures</td><td>控制图符</td></tr><tr><td>2440</td><td>245F</td><td>Optical Character Recognition</td><td>光学字符识别</td></tr><tr><td>2460</td><td>24FF</td><td>Enclosed Alphanumerics</td><td>带括号的字母数字</td></tr><tr><td>2500</td><td>257F</td><td>Box Drawing</td><td>制表符</td></tr><tr><td>2580</td><td>259F</td><td>Block Elements</td><td>方块元素</td></tr><tr><td>25A0</td><td>25FF</td><td>Geometric Shapes</td><td>几何形状</td></tr><tr><td>2600</td><td>26FF</td><td>Miscellaneous Symbols</td><td>零杂符号</td></tr><tr><td>2700</td><td>27BF</td><td>Dingbats</td><td>杂锦字型</td></tr><tr><td>27C0</td><td>27EF</td><td>Miscellaneous Mathematical Symbols-A</td><td>零杂数学符号-A</td></tr><tr><td>27F0</td><td>27FF</td><td>Supplemental Arrows-A</td><td>箭头符号补充-A</td></tr><tr><td>2800</td><td>28FF</td><td>Braille Patterns</td><td>盲文</td></tr><tr><td>2900</td><td>297F</td><td>Supplemental Arrows-B</td><td>箭头符号补充-B</td></tr><tr><td>2980</td><td>29FF</td><td>Miscellaneous Mathematical Symbols-B</td><td>零杂数学符号-B</td></tr><tr><td>2A00</td><td>2AFF</td><td>Supplemental Mathematical Operators</td><td>数学运算符号</td></tr><tr><td>2B00</td><td>2BFF</td><td>Miscellaneous Symbols and Arrows</td><td>零杂符号和箭头</td></tr><tr><td>2C00</td><td>2C5F</td><td>Glagolitic</td><td>格拉哥里字母表</td></tr><tr><td>2C60</td><td>2C7F</td><td>Latin Extended-C</td><td>拉丁字母扩充-C</td></tr><tr><td>2C80</td><td>2CFF</td><td>Coptic</td><td>科普特文</td></tr><tr><td>2D00</td><td>2D2F</td><td>Georgian Supplement</td><td>格鲁吉亚文补充</td></tr><tr><td>2D30</td><td>2D7F</td><td>Tifinagh</td><td>提非纳字母</td></tr><tr><td>2D80</td><td>2DDF</td><td>Ethiopic Extended</td><td>埃塞俄比亚文扩充</td></tr><tr><td>2E00</td><td>2E7F</td><td>Supplemental Punctuation</td><td>标点符号补充</td></tr><tr><td>2E80</td><td>2EFF</td><td>CJK Radicals Supplement</td><td>中日韩部首补充</td></tr><tr><td>2F00</td><td>2FDF</td><td>Kangxi Radicals</td><td>康熙字典部首</td></tr><tr><td>2FF0</td><td>2FFF</td><td>Ideographic Description Characters</td><td>汉字结构描述字符</td></tr><tr><td>3000</td><td>303F</td><td>CJK Symbols and Punctuation</td><td>中日韩符号和标点</td></tr><tr><td>3040</td><td>309F</td><td>Hiragana</td><td>平假名</td></tr><tr><td>30A0</td><td>30FF</td><td>Katakana</td><td>片假名</td></tr><tr><td>3100</td><td>312F</td><td>Bopomofo</td><td>注音符号</td></tr><tr><td>3130</td><td>318F</td><td>Hangul Compatibility Jamo</td><td>朝鲜文兼容字母</td></tr><tr><td>3190</td><td>319F</td><td>Kanbun</td><td>日文的汉字批注</td></tr><tr><td>31A0</td><td>31BF</td><td>Bopomofo Extended</td><td>注音符号扩充</td></tr><tr><td>31C0</td><td>31EF</td><td>CJK Strokes</td><td>中日韩笔划</td></tr><tr><td>31F0</td><td>31FF</td><td>Katakana Phonetic Extensions</td><td>片假名音标扩充</td></tr><tr><td>3200</td><td>32FF</td><td>Enclosed CJK Letters and Months</td><td>带括号的中日韩字母及月份</td></tr><tr><td>3300</td><td>33FF</td><td>CJK Compatibility</td><td>中日韩兼容字符</td></tr><tr><td>3400</td><td>4DBF</td><td>CJK Unified Ideographs Extension A</td><td>中日韩统一表意文字扩充A</td></tr><tr><td>4DC0</td><td>4DFF</td><td>Yijing Hexagram Symbols</td><td>易经六十四卦象</td></tr><tr><td>4E00</td><td>9FFF</td><td>CJK Unified Ideographs</td><td>中日韩统一表意文字</td></tr><tr><td>A000</td><td>A48F</td><td>Yi Syllables</td><td>彝文音节</td></tr><tr><td>A490</td><td>A4CF</td><td>Yi Radicals</td><td>彝文字根</td></tr><tr><td>A700</td><td>A71F</td><td>Modifier Tone Letters</td><td>声调修饰字母</td></tr><tr><td>A720</td><td>A7FF</td><td>Latin Extended-D</td><td>拉丁字母扩充-D</td></tr><tr><td>A800</td><td>A82F</td><td>Syloti Nagri</td><td>Syloti Nagri字母表</td></tr><tr><td>A840</td><td>A87F</td><td>Phags-pa</td><td>Phags-pa字母表</td></tr><tr><td>AC00</td><td>D7AF</td><td>Hangul Syllables</td><td>朝鲜文音节</td></tr><tr><td>D800</td><td>DB7F</td><td>High Surrogates</td><td>高位替代</td></tr><tr><td>DB80</td><td>DBFF</td><td>High Private Use Surrogates</td><td>高位专用替代</td></tr><tr><td>DC00</td><td>DFFF</td><td>Low Surrogates</td><td>低位替代</td></tr><tr><td>E000</td><td>F8FF</td><td>Private Use Area</td><td>专用区</td></tr><tr><td>F900</td><td>FAFF</td><td>CJK Compatibility Ideographs</td><td>中日韩兼容表意文字</td></tr><tr><td>FB00</td><td>FB4F</td><td>Alphabetic Presentation Forms</td><td>字母变体显现形式</td></tr><tr><td>FB50</td><td>FDFF</td><td>Arabic Presentation Forms-A</td><td>阿拉伯文变体显现形式-A</td></tr><tr><td>FE00</td><td>FE0F</td><td>Variation Selectors</td><td>字型变换选取器</td></tr><tr><td>FE10</td><td>FE1F</td><td>Vertical Forms</td><td>竖排标点符号</td></tr><tr><td>FE20</td><td>FE2F</td><td>Combining Half Marks</td><td>组合半角标示</td></tr><tr><td>FE30</td><td>FE4F</td><td>CJK Compatibility Forms</td><td>中日韩兼容形式</td></tr><tr><td>FE50</td><td>FE6F</td><td>Small Form Variants</td><td>小型变体形式</td></tr><tr><td>FE70</td><td>FEFF</td><td>Arabic Presentation Forms-B</td><td>阿拉伯文变体显现形式-B</td></tr><tr><td>FF00</td><td>FFEF</td><td>Halfwidth and Fullwidth Forms</td><td>半角及全角字符</td></tr><tr><td>FFF0</td><td>FFFF</td><td>Specials</td><td>特殊区域</td></tr><tr><td>10000</td><td>1007F</td><td>Linear B Syllabary</td><td>线形文字B音节文字</td></tr><tr><td>10080</td><td>100FF</td><td>Linear B Ideograms</td><td>线形文字B表意文字</td></tr><tr><td>10100</td><td>1013F</td><td>Aegean Numbers</td><td>爱琴海数字</td></tr><tr><td>10140</td><td>1018F</td><td>Ancient Greek Numbers</td><td>古希腊数字</td></tr><tr><td>10300</td><td>1032F</td><td>Old Italic</td><td>古意大利文</td></tr><tr><td>10330</td><td>1034F</td><td>Gothic</td><td>哥特文</td></tr><tr><td>10380</td><td>1039F</td><td>Ugaritic</td><td>乌加里特楔形文字</td></tr><tr><td>103A0</td><td>103DF</td><td>Old Persian</td><td>古波斯文</td></tr><tr><td>10400</td><td>1044F</td><td>Deseret</td><td>德塞雷特大学音标</td></tr><tr><td>10450</td><td>1047F</td><td>Shavian</td><td>肃伯纳速记符号</td></tr><tr><td>10480</td><td>104AF</td><td>Osmanya</td><td>Osmanya字母表</td></tr><tr><td>10800</td><td>1083F</td><td>Cypriot Syllabary</td><td>塞浦路斯音节文字</td></tr><tr><td>10900</td><td>1091F</td><td>Phoenician</td><td>腓尼基文</td></tr><tr><td>10A00</td><td>10A5F</td><td>Kharoshthi</td><td>迦娄士悌文</td></tr><tr><td>12000</td><td>123FF</td><td>Cuneiform</td><td>楔形文字</td></tr><tr><td>12400</td><td>1247F</td><td>Cuneiform Numbers and Punctuation</td><td>楔形文字数字和标点</td></tr><tr><td>1D000</td><td>1D0FF</td><td>Byzantine Musical Symbols</td><td>东正教音乐符号</td></tr><tr><td>1D100</td><td>1D1FF</td><td>Musical Symbols</td><td>音乐符号</td></tr><tr><td>1D200</td><td>1D24F</td><td>Ancient Greek Musical Notation</td><td>古希腊音乐符号</td></tr><tr><td>1D300</td><td>1D35F</td><td>Tai Xuan Jing Symbols</td><td>太玄经符号</td></tr><tr><td>1D360</td><td>1D37F</td><td>Counting Rod Numerals</td><td>算筹</td></tr><tr><td>1D400</td><td>1D7FF</td><td>Mathematical Alphanumeric Symbols</td><td>数学用字母数字符号</td></tr><tr><td>20000</td><td>2A6DF</td><td>CJK Unified Ideographs Extension B</td><td>中日韩统一表意文字扩充 B</td></tr><tr><td>2F800</td><td>2FA1F</td><td>CJK Compatibility Ideographs Supplement</td><td>中日韩兼容表意文字补充</td></tr><tr><td>E0000</td><td>E007F</td><td>Tags</td><td>标签</td></tr><tr><td>E0100</td><td>E01EF</td><td>Variation Selectors Supplement</td><td>字型变换选取器补充</td></tr><tr><td>F0000</td><td>FFFFF</td><td>Supplementary Private Use Area-A</td><td>补充专用区-A</td></tr><tr><td>100000</td><td>10FFFF</td><td>Supplementary Private Use Area-B</td><td>补充专用区-B</td></tr></tbody></table><p>Block是Unicode字符的一个属性。属于同一个Block的字符有着相近的用途。Block表中的开始码位、结束码位只是用来划分出一块区域，在开始码位和结束码位之间可能还有很多未定义的码位。使用UniToy，大家可以按照Block浏览Unicode字符，既可以按列表显示：</p><p><img src="http://www.fmddlmyy.cn/images/block_list.png" width="874" height="435" border="0" alt=""></p><p>&#160;</p>也可以显示每个字符的详细信息：<p><img src="http://www.fmddlmyy.cn/images/block_detail.png" width="829" height="575" border="0" alt=""></p><h4>4.1.3 Script</h4><p>Unicode中每个字符都有一个Script属性，这个属性表明字符所属的文字系统。Unicode目前支持以下Script：</p><table border="1"><tbody><tr><td>Script名称（英文）</td><td>Script名称（中文）</td><td>Script包含的字符数</td></tr><tr><td>Arabic</td><td>阿拉伯文</td><td>966</td></tr><tr><td>Armenian</td><td>亚美尼亚文</td><td>90</td></tr><tr><td>Balinese</td><td>巴厘文</td><td>121</td></tr><tr><td>Bengali</td><td>孟加拉文</td><td>91</td></tr><tr><td>Bopomofo</td><td>汉语注音符号</td><td>64</td></tr><tr><td>Braille</td><td>盲文</td><td>256</td></tr><tr><td>Buginese</td><td>布吉文</td><td>30</td></tr><tr><td>Buhid</td><td>布迪文</td><td>20</td></tr><tr><td>Canadian Aboriginal</td><td>加拿大印第安方言</td><td>630</td></tr><tr><td>Cherokee</td><td>切罗基文</td><td>85</td></tr><tr><td>Common</td><td>Common</td><td>5020</td></tr><tr><td>Coptic</td><td>科普特文</td><td>128</td></tr><tr><td>Cuneiform</td><td>楔形文字</td><td>982</td></tr><tr><td>Cypriot</td><td>塞浦路斯音节文字</td><td>55</td></tr><tr><td>Cyrillic</td><td>西里尔文</td><td>277</td></tr><tr><td>Deseret</td><td>德塞雷特大学音标</td><td>80</td></tr><tr><td>Devanagari</td><td>天成文书（梵文）</td><td>107</td></tr><tr><td>Ethiopic</td><td>埃塞俄比亚文</td><td>461</td></tr><tr><td>Georgian</td><td>格鲁吉亚文</td><td>120</td></tr><tr><td>Gothic</td><td>哥特文</td><td>94</td></tr><tr><td>Glagolitic</td><td>格拉哥里字母表</td><td>27</td></tr><tr><td>Greek</td><td>希腊文</td><td>506</td></tr><tr><td>Gujarati</td><td>古吉拉特文</td><td>83</td></tr><tr><td>Gurmukhi</td><td>锡克教文</td><td>77</td></tr><tr><td>Han</td><td>汉文</td><td>71570</td></tr><tr><td>Hangul</td><td>韩文书写系统</td><td>11619</td></tr><tr><td>Hanunoo</td><td>哈努诺文</td><td>21</td></tr><tr><td>Hebrew</td><td>希伯来文</td><td>133</td></tr><tr><td>Hiragana</td><td>平假名</td><td>89</td></tr><tr><td>Inherited</td><td>Inherited</td><td>461</td></tr><tr><td>Kannada</td><td>卡纳达文</td><td>86</td></tr><tr><td>Katakana</td><td>片假名</td><td>164</td></tr><tr><td>Kharoshthi</td><td>迦娄士悌文</td><td>65</td></tr><tr><td>Khmer</td><td>高棉文</td><td>146</td></tr><tr><td>Lao</td><td>老挝文</td><td>65</td></tr><tr><td>Latin</td><td>拉丁文系</td><td>1070</td></tr><tr><td>Limbu</td><td>林布文(尼泊尔东部)</td><td>66</td></tr><tr><td>Linear B</td><td>线形文字B</td><td>211</td></tr><tr><td>Malayalam</td><td>德拉维族文(印度)</td><td>78</td></tr><tr><td>Mongolian</td><td>蒙古文</td><td>152</td></tr><tr><td>Myanmar</td><td>缅甸文</td><td>78</td></tr><tr><td>New Tai Lue</td><td>新傣文</td><td>80</td></tr><tr><td>Nko</td><td>N'Ko字母表</td><td>59</td></tr><tr><td>Ogham</td><td>欧甘文字</td><td>29</td></tr><tr><td>Old Italic</td><td>古意大利文</td><td>35</td></tr><tr><td>Old Persian</td><td>古波斯文</td><td>50</td></tr><tr><td>Oriya</td><td>奥里亚文</td><td>81</td></tr><tr><td>Osmanya</td><td>Osmanya字母表</td><td>40</td></tr><tr><td>Phags Pa</td><td>Phags Pa字母表(蒙古)</td><td>56</td></tr><tr><td>Phoenician</td><td>腓尼基文</td><td>27</td></tr><tr><td>Runic</td><td>古代北欧文</td><td>78</td></tr><tr><td>Shavian</td><td>肃伯纳速记符号</td><td>48</td></tr><tr><td>Sinhala</td><td>僧伽罗文</td><td>80</td></tr><tr><td>Syloti Nagri</td><td>Syloti Nagri字母表(印度)</td><td>44</td></tr><tr><td>Syriac</td><td>叙利亚文</td><td>77</td></tr><tr><td>Tagalog</td><td>塔加路文(菲律宾)</td><td>20</td></tr><tr><td>Tagbanwa</td><td>Tagbanwa文(菲律宾)</td><td>18</td></tr><tr><td>Tai Le</td><td>德宏傣文</td><td>35</td></tr><tr><td>Tamil</td><td>泰米尔文</td><td>71</td></tr><tr><td>Telugu</td><td>泰卢固文(印度)</td><td>80</td></tr><tr><td>Thaana</td><td>马尔代夫书写体</td><td>50</td></tr><tr><td>Thai</td><td>泰国文</td><td>86</td></tr><tr><td>Tibetan</td><td>藏文</td><td>195</td></tr><tr><td>Tifinagh</td><td>提非纳字母表</td><td>55</td></tr><tr><td>Ugaritic</td><td>乌加里特楔形文字</td><td>31</td></tr><tr><td>Yi</td><td>彝文</td><td>1220</td></tr></tbody></table><p>其中，有两个Script值有着特殊的含义：</p><ul><li>Common：Script属性为Common的字符可能在多个文字系统中使用，不是某个文字系统特有的。例如：空格、数字等。</li><li>Inherited：Script属性为Inherited的字符会继承前一个字符的Script属性。主要是一些组合用符号，例如：在&#8220;组合附加符号&#8221;区（0x300-0x36f），字符的Script属性都是Inherited。</li></ul><p>UCD中的Script.txt列出了每个字符的Script属性。使用UniToy可以按照Script属性查看字符。例如：</p><p><img src="http://www.fmddlmyy.cn/images/script.png" width="829" height="428" border="0" alt=""></p><p>左侧Script窗口中，第一层节点是按英文字母顺序排列的Script属性。第二层节点是包含该Script文字的行（row），点击后显示该行内属于这个Script的字符。这样，就可以集中查看属于同一文字系统的字符。</p><h4>4.1.4 Unicode中的汉字</h4><p>前面提过，在Unicode已定义的99089个字符中，有71226个字符是汉字。它们的分布如下：</p><table border="1"><tbody><tr><td>Block名称</td><td>开始码位</td><td>结束码位</td><td>数量</td></tr><tr><td>中日韩统一表意文字扩充A</td><td>3400</td><td>4db5</td><td>6582</td><td></td></tr><tr><td>中日韩统一表意文字</td><td>4e00</td><td>9fbb</td><td>20924</td><td></td></tr><tr><td>中日韩兼容表意文字</td><td>f900</td><td>fa2d</td><td>302</td><td></td></tr><tr><td>中日韩兼容表意文字</td><td>fa30</td><td>fa6a</td><td>59</td><td></td></tr><tr><td>中日韩兼容表意文字</td><td>fa70</td><td>fad9</td><td>106</td></tr><tr><td>中日韩统一表意文字扩充B</td><td>20000</td><td>2a6d6</td><td>42711</td><td></td></tr><tr><td>中日韩兼容表意文字补充</td><td>2f800</td><td>2fa1d</td><td>542</td></tr></tbody></table><p>UCD的Unihan.txt中的部首偏旁索引（kRSUnicode）可以检索全部71226个汉字。kRSUnicode的部首是按照康熙字典定义的，共214个部首。简体字按照简体部首对应的繁体部首检索。UniToy整理了康熙字典部首对应的简体部首，提供了按照部首检索汉字的功能：</p><p><img src="http://www.fmddlmyy.cn/images/kangxi.png" width="921" height="653" border="0" alt=""></p><h3>4.2 UTF编码</h3><p>在字符编码的四个层次中，第一层的范围和第二层的编码在4.1节已经详细讨论过了。本节讨论第三层的UTF编码和第四层的字节序，主要谈谈第三层的UTF编码，即怎样将Unicode定义的编码转换成程序数据。</p><h4>4.2.1 UTF-8</h4><p>UTF-8以字节为单位对Unicode进行编码。从Unicode到UTF-8的编码方式如下：</p><table border="1"><tbody><tr><td>Unicode编码(16进制)</td><td>UTF-8 字节流(二进制)</td></tr><tr><td>000000 - 00007F</td><td>0xxxxxxx</td></tr><tr><td>000080 - 0007FF</td><td>110xxxxx 10xxxxxx</td></tr><tr><td>000800 - 00FFFF</td><td>1110xxxx 10xxxxxx 10xxxxxx</td></tr><tr><td>010000 - 10FFFF</td><td>11110xxx 10xxxxxx 10xxxxxx 10xxxxxx</td></tr></tbody></table><p>UTF-8的特点是对不同范围的字符使用不同长度的编码。对于0x00-0x7F之间的字符，UTF-8编码与ASCII编码完全相同。UTF-8编码的最大长度是4个字节。从上表可以看出，4字节模板有21个x，即可以容纳21位二进制数字。Unicode的最大码位0x10FFFF也只有21位。</p><p>例1：&#8220;汉&#8221;字的Unicode编码是0x6C49。0x6C49在0x0800-0xFFFF之间，使用用3字节模板了：<font color="#0000FF">1110</font>xxxx&nbsp;<font color="#0000FF">10</font>xxxxxx&nbsp;<font color="#0000FF">10</font>xxxxxx。将0x6C49写成二进制是：0110 1100 0100 1001， 用这个比特流依次代替模板中的x，得到：<font color="#0000FF">1110</font>0110&nbsp;<font color="#0000FF">10</font>110001&nbsp;<font color="#0000FF">10</font>001001，即E6 B1 89。</p><p>例2：&#8220;<img src="http://www.fmddlmyy.cn/images/134192.png" width="14" height="15" border="0" alt="">&#8221;字的Unicode编码是0x20C30。0x20C30在0x010000-0x10FFFF之间，使用用4字节模板了：<font color="#0000FF">11110</font>xxx&nbsp;<font color="#0000FF">10</font>xxxxxx&nbsp;<font color="#0000FF">10</font>xxxxxx&nbsp;<font color="#0000FF">10</font>xxxxxx。将0x20C30写成21位二进制数字（不足21位就在前面补0）：0 0010 0000 1100 0011 0000，用这个比特流依次代替模板中的x，得到：<font color="#0000FF">11110</font>000&nbsp;<font color="#0000FF">10</font>100000&nbsp;<font color="#0000FF">10</font>110000&nbsp;<font color="#0000FF">10</font>110000，即F0 A0 B0 B0。</p><h4>4.2.2 UTF-16</h4><p>UniToy有个&#8220;输出编码&#8221;功能，可以输出当前选择的文本编码。因为UniToy内部采用UTF-16编码，所以输出的编码就是文本的UTF-16编码。例如：如果我们输出&#8220;汉&#8221;字的UTF-16编码，可以看到0x6C49，这与&#8220;汉&#8221;字的Unicode编码是一致的。如果我们输出&#8220;<img src="http://www.fmddlmyy.cn/images/134192.png" width="14" height="15" border="0" alt="">&#8221;字的UTF-16编码，可以看到0xD843, 0xDC30。&#8220;<img src="http://www.fmddlmyy.cn/images/134192.png" width="14" height="15" border="0" alt="">&#8221;字的Unicode编码是0x20C30，它的UTF-16编码是怎样得到的呢？</p><h5>4.2.2.1 编码规则</h5><p>UTF-16编码以16位无符号整数为单位。我们把Unicode编码记作U。编码规则如下：</p><ul><li>如果U&lt;0x10000，U的UTF-16编码就是U对应的16位无符号整数（为书写简便，下文将16位无符号整数记作WORD）。</li><li>如果U&#8805;0x10000，我们先计算U'=U-0x10000，然后将U'写成二进制形式：yyyy yyyy yyxx xxxx xxxx，U的UTF-16编码（二进制）就是：<font color="#0000FF">110110</font>yyyyyyyyyy<font color="#0000FF">110111</font>xxxxxxxxxx。</li></ul><p>为什么U'可以被写成20个二进制位？Unicode的最大码位是0x10ffff，减去0x10000后，U'的最大值是0xfffff，所以肯定可以用20个二进制位表示。例如：&#8220;<img src="http://www.fmddlmyy.cn/images/134192.png" width="14" height="15" border="0" alt="">&#8221;字的Unicode编码是0x20C30，减去0x10000后，得到0x10C30，写成二进制是：0001 0000 1100 0011 0000。用前10位依次替代模板中的y，用后10位依次替代模板中的x，就得到：<font color="#0000FF">110110</font>0001000011&nbsp;<font color="#0000FF">110111</font>0000110000，即0xD843 0xDC30。</p><h5>4.2.2.2 代理区（Surrogate）</h5><p>按照上述规则，Unicode编码0x10000-0x10FFFF的UTF-16编码有两个WORD，第一个WORD的高6位是110110，第二个WORD的高6位是110111。可见，第一个WORD的取值范围（二进制）是<font color="#0000FF">110110</font>00 00000000到<font color="#0000FF">110110</font>11 11111111，即0xD800-0xDBFF。第二个WORD的取值范围（二进制）是<font color="#0000FF">110111</font>00 00000000到<font color="#0000FF">110111</font>11 11111111，即0xDC00-0xDFFF。</p><p>为了将一个WORD的UTF-16编码与两个WORD的UTF-16编码区分开来，Unicode编码的设计者将0xD800-0xDFFF保留下来，并称为代理区（Surrogate）：</p><table border="1"><tbody><tr><td>D800</td><td>DB7F</td><td>High Surrogates</td><td>高位替代</td></tr><tr><td>DB80</td><td>DBFF</td><td>High Private Use Surrogates</td><td>高位专用替代</td></tr><tr><td>DC00</td><td>DFFF</td><td>Low Surrogates</td><td>低位替代</td></tr></tbody></table><p>高位替代就是指这个范围的码位是两个WORD的UTF-16编码的第一个WORD。低位替代就是指这个范围的码位是两个WORD的UTF-16编码的第二个WORD。那么，高位专用替代是什么意思？我们来解答这个问题，顺便看看怎么由UTF-16编码推导Unicode编码。</p><p>解：如果一个字符的UTF-16编码的第一个WORD在0xDB80到0xDBFF之间，那么它的Unicode编码在什么范围内？我们知道第二个WORD的取值范围是0xDC00-0xDFFF，所以这个字符的UTF-16编码范围应该是0xDB80 0xDC00到0xDBFF 0xDFFF。我们将这个范围写成二进制：</p><p><font color="#0000FF">110110</font>1110000000&nbsp;<font color="#0000FF">110111</font>00 00000000 -&nbsp;<font color="#0000FF">110110</font>1111111111&nbsp;<font color="#0000FF">110111</font>1111111111</p><p>按照编码的相反步骤，取出高低WORD的后10位，并拼在一起，得到</p><p>1110 0000 0000 0000 0000 - 1111 1111 1111 1111 1111</p><p>即0xe0000-0xfffff，按照编码的相反步骤再加上0x10000，得到0xf0000-0x10ffff。这就是UTF-16编码的第一个WORD在0xdb80到0xdbff之间的Unicode编码范围，即平面15和平面16。因为Unicode标准将平面15和平面16都作为专用区，所以0xDB80到0xDBFF之间的保留码位被称作高位专用替代。</p><h4>4.2.3 UTF-32</h4><p>UTF-32编码以32位无符号整数为单位。Unicode的UTF-32编码就是其对应的32位无符号整数。</p><h4>4.2.4 字节序</h4><p>根据字节序的不同，UTF-16可以被实现为UTF-16LE或UTF-16BE，UTF-32可以被实现为UTF-32LE或UTF-32BE。例如：</p><table border="1"><tbody><tr><td>字符</td><td>Unicode编码</td><td>UTF-16LE</td><td>UTF-16BE</td><td>UTF32-LE</td><td>UTF32-BE</td></tr><tr><td>汉</td><td>0x6C49</td><td>49 6C</td><td>6C 49</td><td>49 6C 00 00</td><td>00 00 6C 49</td></tr><tr><td><img src="http://www.fmddlmyy.cn/images/134192.png" width="14" height="15" border="0" alt=""></td><td>0x20C30</td><td>43 D8 30 DC</td><td>D8 43 DC 30</td><td>30 0C 02 00</td><td>00 02 0C 30</td></tr></tbody></table><p>&#160;</p>那么，怎么判断字节流的字节序呢？<p>&#160;</p><p>Unicode标准建议用BOM（Byte Order Mark）来区分字节序，即在传输字节流前，先传输被作为BOM的字符"零宽无中断空格"。这个字符的编码是FEFF，而反过来的FFFE（UTF-16）和FFFE0000（UTF-32）在Unicode中都是未定义的码位，不应该出现在实际传输中。下表是各种UTF编码的BOM：</p><table border="1"><tbody><tr><td>UTF编码</td><td>Byte Order Mark</td></tr><tr><td>UTF-8</td><td>EF BB BF</td></tr><tr><td>UTF-16LE</td><td>FF FE</td></tr><tr><td>UTF-16BE</td><td>FE FF</td></tr><tr><td>UTF-32LE</td><td>FF FE 00 00</td></tr><tr><td>UTF-32BE</td><td>00 00 FE FF</td></tr></tbody></table><h2>5 结束语</h2><p>程序员的工作就是将复杂的世界简单地表达出来，希望这篇文章也能做到这一点。本文的初稿完成于2007年2月14日。我会在我的个人主页<a href="http://www.fmddlmyy.cn/">http://www.fmddlmyy.cn</a>维护这篇文章的最新版本。</p></span>
<img src ="http://www.cppblog.com/lauer3912/aggbug/146284.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-05-12 21:58 <a href="http://www.cppblog.com/lauer3912/archive/2011/05/12/146284.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>浅谈文字编码和Unicode（上）(转载)</title><link>http://www.cppblog.com/lauer3912/archive/2011/05/12/146283.html</link><dc:creator>RTY</dc:creator><author>RTY</author><pubDate>Thu, 12 May 2011 13:56:00 GMT</pubDate><guid>http://www.cppblog.com/lauer3912/archive/2011/05/12/146283.html</guid><wfw:comment>http://www.cppblog.com/lauer3912/comments/146283.html</wfw:comment><comments>http://www.cppblog.com/lauer3912/archive/2011/05/12/146283.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lauer3912/comments/commentRss/146283.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lauer3912/services/trackbacks/146283.html</trackback:ping><description><![CDATA[<span  style="font-family: Simsun; "><p>我曾经写过一篇<a href="http://www.fmddlmyy.cn/text6.html">《谈谈Unicode编码，简要解释UCS、UTF、BMP、BOM等名词》</a>（以下简称《谈谈Unicode编码》），在网上流传较广，我也收到不少朋友的反馈。本文探讨《谈谈Unicode编码》中未介绍或介绍较少的代码页、Surrogates等问题，补充一些Unicode资料，顺带介绍一下我最近编写的一个Unicode工具：UniToy。本文虽然是前文的补充，但在写作上尽量做到独立成篇。</p><p>标题中的&#8220;浅谈&#8221;是对自己的要求，我希望文字能尽量浅显易懂。但本文还是假设读者知道字节、16进制，了解《谈谈Unicode编码》中介绍过的字节序和Unicode的基本概念。</p><h2>0 UniToy</h2>UniToy是我编写的一个小工具。通过UniToy，我们可以全方位、多角度地查看Unicode，了解Unicode和语言、代码页的关系，完成一些文字编码的相关工作。本文的一些内容是通过UniToy演示的。大家可以从我的网站（<a href="http://www.fmddlmyy.cn/" target="_top">www.fmddlmyy.cn</a>）下载UniToy的<a href="http://www.fmddlmyy.cn/unitoy.html" target="_top">演示版本</a>。<h2>1 文字的显示</h2><h3>1.1 发生了什么？</h3><p>我们首先以Windows为例来看看文字显示过程中发生了什么。用记事本打开一个文本文件，可以看到文件包含的文字：</p><p><img src="http://www.fmddlmyy.cn/images/uni_text.png" width="287" height="87" border="0" alt="字符和编码"></p><p>如果我们用UltraEdit或Hex Workshop查看这个文件的16进制数据，可以看到：</p><p><img src="http://www.fmddlmyy.cn/images/uni_data.png" width="638" height="178" border="0" alt="D7D6 B7FB BACD B1E0 C2EB"></p><p>我们看到：文件&#8220;例子GBK.txt&#8221;有10个字节，依次是&#8220;D7 D6 B7 FB BA CD B1 E0 C2 EB&#8221;，这就是记事本从文件中读到的内容。记事本是用来打开文本文件的，所以它会调用Windows的文本显示函数将读到的数据作为文本显示。Windows首先将文本数据转换到它内部使用的编码格式：Unicode，然后按照文本的Unicode去字体文件中查找字体图像，最后将图像显示到窗口上。 总结一下前面的分析，文字的显示应该是这样的：</p><ul><li>步骤1：文字首先以某种编码保存在文件中。</li><li>步骤2：Windows将文件中的文字编码映射到Unicode。</li><li>步骤3：Windows按照Unicode在字体文件中查找字体图像，画到窗口上。</li></ul>所谓编码就是用数字表示字符，例如用D7D6表示&#8220;字&#8221;。当然，<u>编码</u>还意味着<u>约定</u>，即大家都认可。从《谈谈Unicode编码》中，我们知道Unicode也是一种文字编码，它的特殊性在于它是由国际组织设计，可以容纳全世界所有语言文字。而我们平常使用的文字编码通常是针对一个区域的语言、文字设计，只支持特定的语言文字。例如：在上面的例子中，文件&#8220;例子GBK.txt&#8221;采用的就是GBK编码。<p>&#160;</p><p>如果上述3个步骤中任何一步发生了错误，文字就不能被正确显示，例如：</p><ul><li><p>错误1：如果弄错了编码，例如将Big5编码的文字当作GBK编码，就会出现乱码。</p><p><img src="http://www.fmddlmyy.cn/images/big5.png" width="287" height="87" border="0" alt=""></p></li><li><p>错误2：如果从特定编码到Unicode的映射发生错误，例如文本数据中出现该编码方案未定义的字符，Windows就会使用缺省字符，通常是?。</p><p><img src="http://www.fmddlmyy.cn/images/gb18030.png" width="287" height="87" border="0" alt=""></p></li><li>如果当前字体不支持要显示的字符，Windows就会显示字体文件中的缺省图像：空白或方格。</li></ul><p>在Unicode被广泛使用前，有多少种语言、文字，就可能有多少种文字编码方案。一种文字也可能有多种编码方案。那么我们怎么确定文本数据采用了什么编码？</p><h3>1.2 采用了哪种编码？</h3><p>按照惯例，文本文件中的数据都是文本编码，那么它怎么表明自己的编码格式？在记事本的&#8220;打开&#8221;对话框上：</p><p><img src="http://www.fmddlmyy.cn/images/notepad_open.png" width="354" height="140" border="0" alt=""></p><p>我们可以看到记事本支持4种编码格式：ANSI、Unicode、Unicode big endian、UTF-8。如果读者看过《谈谈Unicode编码》，对Unicode、Unicode big endian、UTF-8应该不会陌生，其实它们更准确的名称应该是UTF-16LE（Little Endian）、UTF-16BE（Big Endian）和UTF-8，它们是基于Unicode的不同编码方案。</p><p>在《谈谈Unicode编码》中介绍过，Windows通过在文本文件开头增加一些特殊字节（BOM）来区分上述3种编码，并将没有BOM的文本数据按照ANSI代码页处理。那么什么是代码页，什么是ANSI代码页？</p><h2>2 代码页和字符集</h2><h3>2.1 Windows的代码页</h3><h4>2.1.1 代码页</h4><p>代码页（Code Page）是个古老的专业术语，据说是IBM公司首先使用的。代码页和字符集的含义基本相同，代码页规定了适用于特定地区的字符集合，和这些字符的编码。可以将代码页理解为字符和字节数据的映射表。</p><p>Windows为自己支持的代码页都编了一个号码。例如代码页936就是简体中文 GBK，代码页950就是繁体中文 Big5。代码页的概念比较简单，就是一个字符编码方案。但要说清楚Windows的ANSI代码页，就要从Windows的区域（Locale）说起了。</p><h4>2.1.2 区域和ANSI代码页</h4><p>微软为了适应世界上不同地区用户的文化背景和生活习惯，在Windows中设计了区域（Locale）设置的功能。Local是指特定于某个国家或地区的一组设定，包括代码页，数字、货币、时间和日期的格式等。在Windows内部，其实有两个Locale设置：系统Locale和用户Locale。系统Locale决定代码页，用户Locale决定数字、货币、时间和日期的格式。我们可以在控制面板的&#8220;区域和语言选项&#8221;中设置系统Locale和用户Locale：</p><p><img src="http://www.fmddlmyy.cn/images/locale.PNG" width="914" height="443" border="0" alt=""></p><p>每个Locale都有一个对应的代码页。Locale和代码页的对应关系，大家可以参阅我的另一篇文章<a href="http://www.fmddlmyy.cn/text7.html">《谈谈Windows程序中的字符编码》</a>的附录1。系统Locale对应的代码页被作为Windows的默认代码页。在没有文本编码信息时，Windows按照默认代码页的编码方案解释文本数据。这个默认代码页通常被称作ANSI代码页（ACP）。</p><p>ANSI代码页还有一层意思，就是微软自己定义的代码页。在历史上，IBM的个人计算机和微软公司的操作系统曾经是PC的标准配置。微软公司将IBM公司定义的代码页称作OEM代码页，在IBM公司的代码页基础上作了些增补后，作为自己的代码页，并冠以ANSI的字样。我们在&#8220;区域和语言选项&#8221;高级页面的代码页转换表中看到的包含ANSI字样的代码页都是微软自己定义的代码页。例如：</p><ul><li>874 （ANSI/OEM - 泰文）</li><li>932 （ANSI/OEM - 日文 Shift-JIS）</li><li>936 （ANSI/OEM - 简体中文 GBK）</li><li>949 （ANSI/OEM - 韩文）</li><li>950 （ANSI/OEM - 繁体中文 Big5）</li><li>1250 （ANSI - 中欧）</li><li>1251 （ANSI - 西里尔文）</li><li>1252 （ANSI - 拉丁文 I）</li><li>1253 （ANSI - 希腊文）</li><li>1254 （ANSI - 土耳其文）</li><li>1255 （ANSI - 希伯来文）</li><li>1256 （ANSI - 阿拉伯文）</li><li>1257 （ANSI - 波罗的海文）</li><li>1258 （ANSI/OEM - 越南）</li></ul><p>在UniToy中，我们可以按照代码页编码顺序查看这些代码页的字符和编码：</p><p><img src="http://www.fmddlmyy.cn/images/unitoy_codepage.png" width="744" height="333" border="0" alt=""></p><p>我们不能直接设置ANSI代码页，只能通过选择系统Locale，间接改变当前的ANSI代码页。微软定义的Locale只使用自己定义的代码页。所以，我们虽然可以通过&#8220;区域和语言选项&#8221;中的代码页转换表安装很多代码页，但只能将微软的代码页作为系统默认代码页。</p><h4>2.1.3 代码页转换表</h4><p>在Windows 2000以后，Windows统一采用UTF-16作为内部字符编码。现在，安装一个代码页就是安装一张代码页转换表。通过代码页转换表，Windows既可以将代码页的编码转换到UTF-16，也可以将UTF-16转换到代码页的编码。代码页转换表的具体实现可以是一个以nls为后缀的数据文件，也可以是一个提供转换函数的动态链接库。有的代码页是不需要安装的。例如：Windows将UTF-7和UTF-8分别作为代码页65000和代码页65001。UTF-7、UTF-8和UTF-16都是基于Unicode的编码方案。它们之间可以通过简单的算法直接转换，不需要安装代码页转换表。</p><p>在安装过一个代码页后，Windows就知道怎样将该代码页的文本转换到Unicode文本，也知道怎样将Unicode文本转换成该代码页的文本。例如：UniToy有导入和导出功能。所谓导入功能就是将任一代码页的文本文件转换到Unicode文本；导出功能就是将Unicode文本转换到任一指定的代码页。这里所说的代码页就是指系统已安装的代码页：</p><p><img src="http://www.fmddlmyy.cn/images/import.png" width="351" height="234" border="0" alt=""></p><p>其实，如果全世界人民在计算机刚发明时就统一采用Unicode作为字符编码，那么代码页就没有存在的必要了。可惜在Unicode被发明前，世界各国人民都发明并使用了各种字符编码方案。所以，Windows必须通过代码页支持已经被广泛使用的字符编码。从这种意义看，代码页主要是为了兼容现有的数据、程序和习惯而存在的。</p><h4>2.1.4 SBCS、DBCS和MBCS</h4><p>SBCS、DBCS和MBCS分别是单字节字符集、双字节字符集和多字节字符集的缩写。SBCS、DBCS和MBCS的最大编码长度分别是1字节、两字节和大于两字节（例如4或5字节）。例如：代码页1252 （ANSI-拉丁文 I）是单字节字符集；代码页936 （ANSI/OEM-简体中文 GBK）是双字节字符集；代码页54936 （GB18030 简体中文）是多字节字符集。</p><p>单字节字符集中的字符都用一个字节表示。显然，SBCS最多只能容纳256个字符。</p><p>双字节字符集的字符用一个或两个字节表示。那么我们从文本数据中读到一个字节时，怎么判断它是单字节字符，还是双字节字符的首字符？答案是通过字节所处范围来判断。例如：在GBK编码中，单字节字符的范围是0x00-0x80，双字节字符首字节的范围是0x81到0xFE。我们顺序读取字节数据，如果读到的字节在0x81到0xFE内，那么这个字节就是双字节字符的首字节。GBK定义双字节字符的尾字节范围是0x40到0x7E和0x80到0xFE。</p><p>GB18030是多字节字符集，它的字符可以用一个、两个或四个字节表示。这时我们又如何判断一个字节是属于单字节字符，双字节字符，还是四字节字符？GB18030与GBK是兼容的，它利用了GBK双字节字符尾字节的未使用码位。GB18030的四字节字符的第一字节的范围也是0x81到0xFE，第二字节的范围是0x30-0x39。通过第二字节所处范围就可以区分双字节字符和四字节字符。GB18030定义四字节字符的第三字节范围是0x81到0xFE，第四字节范围是0x30-0x39。</p><h3>2.2 代码页实例</h3><h4>2.2.1 实例一：GB18030代码页</h4><p>1.1节的&#8220;错误2&#8221;中演示了一个全被显示成'?'的文件。这个文件的数据是：</p><p><img src="http://www.fmddlmyy.cn/images/gb18030_data.png" width="638" height="178" border="0" alt=""></p><p>其实，这是一个包含了6个四字节字符的GB18030编码的文件。记事本按照GBK显示这些数据，而GB18030的四字节字符编码在GBK中是未定义的。Windows根据首字节范围判断出12个双字节字符，然后因为找不到匹配的转换而将其映射到默认字符'?'。使用UniToy按照GB18030代码页导入这个文件，就可以看到：</p><p><img src="http://www.fmddlmyy.cn/images/gb18030_text.png" width="369" height="155" border="0" alt=""></p><p>这个GB18030编码的文件是用UniToy创建的，编辑Unicode文本，然后导出到GB18030编码格式。</p><h4>2.2.2 实例二：GBK和Big5的转换</h4><p>综合使用UniToy的导入、导出功能就可以在任意两个代码页之间转换文本。其实，由于各代码页支持的字符范围不同，我们一般不会直接在代码页间转换文本。例如将以下GBK编码的文本：</p><p><img src="http://www.fmddlmyy.cn/images/conv_gbk.png" width="384" height="175" border="0" alt=""></p><p>直接转换到Big5编码，就会看到：</p><p><img src="http://www.fmddlmyy.cn/images/conv_big5_0.png" width="384" height="175" border="0" alt=""></p><p>变成'?'的字符都是Big5编码不支持的简化字。在从Unicode转换到Big5编码时，由于Big5编码不支持这些字符，Windows就用默认字符'?'代替。在UniToy中，我们可以先将简体字转换到繁体字，然后再导出到Big5编码，就可以正常显示：</p><p><img src="http://www.fmddlmyy.cn/images/conv_big5.png" width="384" height="175" border="0" alt=""></p><p>同理，将Big5编码的文本转换到GBK编码的步骤应该是：</p><ul><li>将Big5编码的文本导入到Unicode文本；</li><li>将繁体的Unicode文本转换简体的Unicode文本；</li><li>将简体的Unicode文本导出到GBK文本。</li></ul><h3>2.3 互联网的字符集</h3><h4>2.3.1 字符集</h4><p>互联网上的信息缤纷多彩，但文本依然是最重要的信息载体。html文件通过标记表明自己使用的字符集。例如：</p><p>&lt;meta http-equiv="Content-Type" content="text/html; charset=utf-8"&gt;</p><p>或者：</p><p>&lt;meta http-equiv="charset" content="iso-8859-1"&gt;</p><p>那么我们可以使用哪些字符集（charset）呢？在IETF（互联网工程任务组）的网页上维护着一份可以在互联网上使用的字符集的清单：<a href="http://www.iana.org/assignments/character-sets">CHARACTER SETS</a>。如果有新的字符集被登记，IETF会更新这份文档。</p><p>简单浏览一下，2006年12月7日的版本列出了253个字符集。其中也包括微软的CP1250 ~ CP1258，在这里它们不会被称作什么ANSI代码页，而是被简单地称作windows-1250、windows-1251等。其实在Unicode被广泛使用前，除了中日韩等大字符集，世界上，特别是西方使用最广泛的字符集应该是ISO 8859系列字符集。</p><h4>2.3.2 ISO 8859系列字符集</h4><p>ISO 8859系列字符集是欧洲计算机制造商协会（ECMA）在上世纪80年代中期设计，并被国际标准化（ISO）组织采纳为国际标准。ISO 8859系列字符集目前有15个字符集，包括：</p><ul><li>ISO 8859-1	大部分的西欧语系，例如英文、法文、西班牙文和德文等（Latin-1）</li><li>ISO 8859-2	大部分的中欧和东欧语系，例如捷克文、波兰文和匈牙利文等（Latin-2）</li><li>ISO 8859-3	欧洲东南部和其它各种文字（Latin-3）</li><li>ISO 8859-4	斯堪的那维亚和波罗的海语系（Latin-4）</li><li>ISO 8859-5	拉丁文与斯拉夫文（俄文、保加利亚文等）</li><li>ISO 8859-6	拉丁文与阿拉伯文</li><li>ISO 8859-7	拉丁文与希腊文</li><li>ISO 8859-8	拉丁文与希伯来文</li><li>ISO 8859-9	为土耳其文修正的Latin-1（Latin-5）</li><li>ISO 8859-10	拉普人、北欧与爱斯基摩人的文字（Latin-6）</li><li>ISO 8859-11	拉丁文与泰文</li><li>ISO 8859-13	波罗的海周边语系，例如拉脱维亚文等（Latin-7）</li><li>ISO 8859-14	凯尔特文，例如盖尔文、威尔士文等（Latin-8）</li><li>ISO 8859-15	改进的Latin-1，增加遗漏的法文、芬兰文字符和欧元符号（Latin-9）</li><li>ISO 8859-16	罗马尼亚文（Latin-10）</li></ul><p>其中缺少的编号12据说是为了预留给天城体梵文字母（Deva-nagari）的。印地文和尼泊尔文都使用了这种在七世纪形成的字母表。由于印度定义了自己的编码ISCII（Indian Script Code for Information Interchange），所以这个编号就未被使用。ISO 8859系列字符集都是单字节字符集，即只使用0x00-0xFF对字符编码。</p><p>大家都知道ASCII吧，那么大家知道ANSI X3.4和ISO 646吗？在1968年发布的ANSI X3.4和1972年发布的ISO 646就是ASCII编码，只不过是不同组织发布的。绝大多数字符集都与ASCII编码保持兼容，ISO 8859系列字符集也不例外，它们的0x00-0x7f都与ASCII码保持一致，各字符集的不同之处在于如何利用0x80-0xff的码位。使用UniToy可以查看ISO 8859系列所有字符集的编码，例如：</p><p><img src="http://www.fmddlmyy.cn/images/iso8859.png" width="724" height="335" border="0" alt=""></p><p>通过这些演示，大家是不是觉得代码页和字符集都是很简单、朴实的东西呢？好，在进入Unicode的话题前，让我们先看一个很深奥的概念。</p></span>
<img src ="http://www.cppblog.com/lauer3912/aggbug/146283.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-05-12 21:56 <a href="http://www.cppblog.com/lauer3912/archive/2011/05/12/146283.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>字符集编码与 C/C++ 源文件字符编译乱弹(收集转载)</title><link>http://www.cppblog.com/lauer3912/archive/2011/05/12/146281.html</link><dc:creator>RTY</dc:creator><author>RTY</author><pubDate>Thu, 12 May 2011 13:40:00 GMT</pubDate><guid>http://www.cppblog.com/lauer3912/archive/2011/05/12/146281.html</guid><wfw:comment>http://www.cppblog.com/lauer3912/comments/146281.html</wfw:comment><comments>http://www.cppblog.com/lauer3912/archive/2011/05/12/146281.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.cppblog.com/lauer3912/comments/commentRss/146281.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lauer3912/services/trackbacks/146281.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 字符集编码与 C/C++ 源文件字符编译乱弹2010年2月24日Breaker原创发表评论阅读评论最近在看国际化编程 (i18n:&nbsp;internationalization) 的东西，也弄清楚了点字符集有关的一些问题，其实网上的一些牛人已经将字符集、Unicode 等相关的问题说的很清楚了，我在这里引用他们的总结并自己小结一下心得，并且实验一下在编译时，源代码自身的字...&nbsp;&nbsp;<a href='http://www.cppblog.com/lauer3912/archive/2011/05/12/146281.html'>阅读全文</a><img src ="http://www.cppblog.com/lauer3912/aggbug/146281.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-05-12 21:40 <a href="http://www.cppblog.com/lauer3912/archive/2011/05/12/146281.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>字符集编码与 C/C++ 源文件字符编译乱弹(收集转载)</title><link>http://www.cppblog.com/lauer3912/archive/2011/05/12/146280.html</link><dc:creator>RTY</dc:creator><author>RTY</author><pubDate>Thu, 12 May 2011 13:40:00 GMT</pubDate><guid>http://www.cppblog.com/lauer3912/archive/2011/05/12/146280.html</guid><wfw:comment>http://www.cppblog.com/lauer3912/comments/146280.html</wfw:comment><comments>http://www.cppblog.com/lauer3912/archive/2011/05/12/146280.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lauer3912/comments/commentRss/146280.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lauer3912/services/trackbacks/146280.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 字符集编码与 C/C++ 源文件字符编译乱弹2010年2月24日Breaker原创发表评论阅读评论最近在看国际化编程 (i18n:&nbsp;internationalization) 的东西，也弄清楚了点字符集有关的一些问题，其实网上的一些牛人已经将字符集、Unicode 等相关的问题说的很清楚了，我在这里引用他们的总结并自己小结一下心得，并且实验一下在编译时，源代码自身的字...&nbsp;&nbsp;<a href='http://www.cppblog.com/lauer3912/archive/2011/05/12/146280.html'>阅读全文</a><img src ="http://www.cppblog.com/lauer3912/aggbug/146280.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-05-12 21:40 <a href="http://www.cppblog.com/lauer3912/archive/2011/05/12/146280.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>