﻿<?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++博客-Good Good code,Day Day up-文章分类-C++</title><link>http://www.cppblog.com/dawnbreak/category/8913.html</link><description>PearLi's Blog</description><language>zh-cn</language><lastBuildDate>Sat, 15 Aug 2009 13:02:13 GMT</lastBuildDate><pubDate>Sat, 15 Aug 2009 13:02:13 GMT</pubDate><ttl>60</ttl><item><title>OnPaint()函数的作用原理</title><link>http://www.cppblog.com/dawnbreak/articles/93425.html</link><dc:creator>pear_li</dc:creator><author>pear_li</author><pubDate>Sat, 15 Aug 2009 06:56:00 GMT</pubDate><guid>http://www.cppblog.com/dawnbreak/articles/93425.html</guid><wfw:comment>http://www.cppblog.com/dawnbreak/comments/93425.html</wfw:comment><comments>http://www.cppblog.com/dawnbreak/articles/93425.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/dawnbreak/comments/commentRss/93425.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/dawnbreak/services/trackbacks/93425.html</trackback:ping><description><![CDATA[<font color=#000099 face=幼圆>WM_PAINT是窗口每次重绘都会产生的一个消息。</font>
<p><font style="FONT-FAMILY: 幼圆; FONT-SIZE: 16px" color=#000099>OnPaint是对这个消息的反应函数</font></p>
<p><font style="FONT-FAMILY: 幼圆; FONT-SIZE: 16px" color=#000099>&nbsp;<wbr></font></p>
<p><font style="FONT-FAMILY: 幼圆; FONT-SIZE: 16px" color=#000099>mfc 的 CWnd::OnPaint 没做什么，只是丢给系统处理。</font></p>
<p><font style="FONT-FAMILY: 幼圆; FONT-SIZE: 20px" color=#000099><strong>一 ：</strong></font></p>
<p><font style="FONT-FAMILY: 幼圆" color=#000099>&nbsp;<wbr>&nbsp;<wbr> 先执行OnEraseBkgnd，擦除背景（如果想自绘控件，这个函数直接return TRUE就可以了，这样就不会擦除背景，不会闪）</font></p>
<p><font style="FONT-FAMILY: 幼圆" color=#000099>&nbsp;<wbr></font></p>
<p><strong><font style="FONT-FAMILY: 幼圆; FONT-SIZE: 18px" color=#000099>OnEraseBkGnd与OnPaint的区别与联系</font></strong></p>
<p><font style="FONT-FAMILY: 幼圆; FONT-SIZE: 16px" color=#000099>在OnEraseBkGnd中,如果你不调用原来缺省的OnEraseBkGnd只是重画背景则不会有闪烁.而在<font style="BACKGROUND-COLOR: #ccff00">OnPaint里面,由于它隐含的调用了OnEraseBkGnd</font>,而你又没有处理OnEraseBkGnd 函数,这时就和窗口缺省的背景刷相关了.缺省的 OnEraseBkGnd操作使用窗口的缺省背景刷刷新背景(一般情况下是白刷),而随后你又自己重画背景造成屏幕闪动.</font></p>
<p><font style="FONT-FAMILY: 幼圆; FONT-SIZE: 16px" color=#000099><font style="BACKGROUND-COLOR: #ccff00">OnEraseBkGnd不是每次都会被调用的</font>.如果你<font style="BACKGROUND-COLOR: #ccff00">调用Invalidate</font>的时候参数为TRUE,那么在OnPaint里面隐含<font style="BACKGROUND-COLOR: #ccff00">调用BeginPaint</font>的时候就产<font style="BACKGROUND-COLOR: #ccff00">生WM_ERASEBKGND消息</font>,如果参数是FALSE,则不会重刷背景.</font></p>
<p><font style="FONT-FAMILY: 幼圆; FONT-SIZE: 16px" color=#000099><strong>ZYP解释：</strong><font color=#000000>void Invalidate( BOOL bErase = TRUE ); 该函数的作用是使整个窗口客户区无效。窗口的客户区无效意味着需要重绘，参数bErase为TRUE时，重绘区域内的背景将被重绘即擦除，否则，背景将保持不变。调用Invalidate等函数后窗口不会立即重绘，这是由于WM_PAINT消息的优先级很低，它需要等消息队列中的其它消息发送完后才能被处理。</font></font></p>
<p><font style="FONT-FAMILY: 幼圆; FONT-SIZE: 16px" color=#000099><font color=#000000><font style="BACKGROUND-COLOR: #ccff00" color=#000099>OnPaint里面会调用</font>BeginPaint函数自动设置显示设备内容的剪切区域而排除任何更新区域外的区域更新区域。如果更新区域被标记为可擦除的，BeginPaint发送一个WM_ERASEBKGND消息给窗口。WM_ERASEBKGND消息的响应函数既是<font style="BACKGROUND-COLOR: #ccff00" color=#000099>OnEraseBkGnd（）</font></font><br><br>所以解决方法有三个半:<br>1.用OnEraseBkGnd实现,不要调用原来的OnEraseBkGnd函数.<br>2.用OnPaint实现,同时重载OnEraseBkGnd,其中直接返回.<br>3.用OnPaint实现,创建窗口时设置背景刷为空<br>4.用OnPaint实现,但是要求刷新时用Invalidate(FALSE)这样<br>的函数.(不过这种情况下,窗口覆盖等造成的刷新还是要闪一<br>下,所以不是彻底的解决方法)<br>都挺简单的.</font></p>
<table>
    <tbody>
        <tr>
            <td>
            <p><font style="FONT-FAMILY: 幼圆; FONT-SIZE: 16px" color=#000099>在MFC中 任何一個window元件的繪圖 都是放在這兩個member function中<br>在設定上 OnEraseBkgnd()是用來畫底圖的 而OnPaint()是用來畫主要物件的<br>舉例說明 一個按鈕是灰色的 上面還有文字<br>則OnEraseBkgnd()所做的事就是把按鈕畫成灰色<br>而OnPaint()所做的事 就是畫上文字</font></p>
            <p><br><font style="FONT-FAMILY: 幼圆; FONT-SIZE: 16px" color=#000099>既然這兩個member function都是用來畫出元件的<br>那為何還要分OnPaint() 與 OnEraseBkgnd() 呢<br>其實OnPaint() 與 OnEraseBkgnd() 特性是有差的<br>1. <wbr><wbr><wbr><wbr><wbr>OnEraseBkgnd()的要求是快速在裡面的繪圖程式最好是不要太耗時間<br><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr>因為<font style="BACKGROUND-COLOR: #ccff00">每當window元件有任何小變動都會馬上呼叫OnEraseBkgnd()</font><br>2. <wbr><wbr><wbr><wbr><wbr><font style="BACKGROUND-COLOR: #ccff33" color=#000099>OnPaint() 是只有在程式有空閒的時候才會被呼叫<br></font>3. <wbr><wbr><wbr><wbr><wbr>OnEraseBkgnd() 是在 OnPaint() 之前呼叫的<br>所以 OnPaint()被呼叫一次之前 可能會呼叫OnEraseBkgnd()好幾次<br><br>如果我們是一個在做圖形化使用者介面的人<br>常會需要把一張美美的圖片設為我們dialog的底圖<br>把繪圖的程式碼放在OnPaint() 之中 可能會常碰到一些問題<br>比方說拖曳一個視窗在我們做的dialog上面一直移動<br>則dialog會變成灰色 直到動作停止才恢復<br>這是因為每次需要重繪的時候 程式都會馬上呼叫OnEraseBkgnd()<br>OnEraseBkgnd()就把dialog畫成灰色<br>而只有動作停止之後 程式才會呼叫OnPaint() 這時才會把我們要畫的底圖貼上去<br><br>這個問題的解法 比較差點的方法是把OnEraseBkgnd() 改寫成不做事的function<br>如下所示</font></p>
            <div>
            <table border=1 cellSpacing=0 cellPadding=0 width="100%" height=32>
                <tbody>
                    <tr>
                        <td>
                        <p><font style="FONT-FAMILY: 幼圆; FONT-SIZE: 16px" color=#000099>BOOL CMyDlg::OnEraseBkgnd(CDC* pDC)<br>{<br><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr>return TRUE;<br>}</font></p>
                        </td>
                    </tr>
                </tbody>
            </table>
            </div>
            <p><font style="FONT-FAMILY: 幼圆; FONT-SIZE: 16px" color=#000099>以上本來是會呼叫CDialog::OnEraseBkgnd() 但是如果我們不呼叫的話<br>程式便不會畫上灰色的底色了</font></p>
            <p><font style="FONT-FAMILY: 幼圆; FONT-SIZE: 16px" color=#000099><font color=#000000>Q:基于对话框的程序中如何重载OnEraseBkGnd()函数</font></font></p>
            <p><font style="FONT-FAMILY: 幼圆; FONT-SIZE: 16px" color=#000099>A:<font color=#000000>这是一个消息WM_ERASEBKWND &nbsp;<wbr><br>&nbsp;<wbr> 在CLASS &nbsp;<wbr> WIZARD中 &nbsp;<wbr><br>&nbsp;<wbr> 选择CLASSINFO页面 &nbsp;<wbr><br>&nbsp;<wbr> 在MESSAGEFILTER中的选项设在WINDOW就可以看到这个消息了.</font><br><br>比較好的做法是直接將繪圖的程式從OnPaint()移到OnEraseBkgnd()來做<br>如下所示</font></p>
            <div>
            <table border=1 cellSpacing=0 cellPadding=0 width="100%" height=280>
                <tbody>
                    <tr>
                        <td>
                        <p><br><font style="FONT-FAMILY: 幼圆; FONT-SIZE: 16px" color=#000099>// m_bmpBKGND 為一CBitmap物件 且事先早已載入我們的底圖<br>// 底圖的大小與我們的視窗client大小一致<br><br><br>BOOL CMyDlg::OnEraseBkgnd(CDC* pDC)<br>{<br><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr>CRect rc;<br><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr>GetUpdateRect(&amp;rc);<br><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr>CDC srcDC;<br><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr>srcDC.CreateCompatibleDC(pDC);<br><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr>srcDC.SelectObject(m_bmpBKGND);<br><br><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr>pDC-&gt;BitBlt(rc.left,rc.top,rc.GetWidth(),<br><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr>rc.GetHeight(),&amp;srcDC,rc.left,rc.top,SRCCOPY);<br><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr>return TRUE;<br>}</font></p>
                        </td>
                    </tr>
                </tbody>
            </table>
            </div>
            <div><br><font style="FONT-FAMILY: 幼圆; FONT-SIZE: 16px" color=#000099>特別要注意的是 取得重畫大小是使用GetUpdateRect() 而不是GetClientRect()<br>如果使用GetClientRect() 會把不該重畫的地方重畫</font></div>
            <div><font style="FONT-SIZE: 16px"><wbr></font></div>
            <div><font style="FONT-FAMILY: 幼圆; FONT-SIZE: 16px" color=#000099>来自:http://hi.baidu.com/%BF%AA%D0%C4_%D0%D6%B5%DC/blog/item/2f6d3b10b6c622fac2ce79a5<wbr>.html</font></div>
            </td>
        </tr>
    </tbody>
</table>
<pre><font style="FONT-FAMILY: 幼圆" color=#000099>&nbsp;<wbr> </font> </pre>
<p><strong><font style="FONT-FAMILY: 幼圆; FONT-SIZE: 20px" color=#000099>二 ：</font></strong></p>
<p><font style="FONT-FAMILY: 幼圆" color=#000099>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 系统的Onpaint中调用了OnDraw，但如果我们自己继承了一个OnPaint函数又没有显式调用OnDraw，则OnDraw就不会被调用,OnInitialUpdate在OnDraw之前，是窗口被创建以后调用的第一个函数。</font></p>
<p><font style="FONT-FAMILY: 幼圆" color=#000099>&nbsp;<wbr></font></p>
<p><strong><font style="FONT-FAMILY: 幼圆" color=#000099 size=4>MFC中OnDraw与OnPaint的区别</font></strong></p>
<p><font style="FONT-FAMILY: 幼圆" color=#000099>在OnPaint中调用OnDraw，一般来说，用户自己的绘图代码应放在OnDraw中。</font></p>
<p><font style="FONT-FAMILY: 幼圆" color=#000099 size=3>OnPaint()是CWnd的类成员，负责响应WM_PAINT消息。OnDraw()是CVIEW的成员函数，没有响应消息的功能.当视图变得无效时（包括大小的改变，移动，被遮盖等等），Windows发送WM_PAINT消息。该视图的OnPaint 处理函数通过创建CPaintDC类的DC对象来响应该消息并调用视图的OnDraw成员函数.<font style="BACKGROUND-COLOR: #ccff00">OnPaint最后也要调用OnDraw</font>,因此一般在OnDraw函数中进行绘制。</font></p>
<p><font size=3><br><font style="FONT-FAMILY: 幼圆" color=#000099>The WM_PAINT message is sent when the UpdateWindow or RedrawWindow member function is called.</font></font></p>
<p><font style="FONT-FAMILY: 幼圆" color=#000099 size=3>在OnPaint中，将调用BeginPaint，用来获得客户区的显示设备环境，并以此调用GDI函数执行绘图操作。在绘图操作完成后，将调用EndPaint以释放显示设备环境。而OnDraw在BeginPaint与EndPaint间被调用。（<font color=#000000>一个应用程序除了响应WM_PAINT消息外，不应该调用BeginPaint。每次调用BeginPaint都应该有相应的EndPaint函数。</font>）</font></p>
<p><font size=3><font style="FONT-FAMILY: 幼圆" color=#000099>1) 在mfc结构里OnPaint是CWnd的成员函数. OnDraw是CView的成员函数.<br>2) OnPaint()调用OnDraw()，OnPrint也会调用OnDraw()，所以OnDraw()是显示和打印的共同操作。<br><br>OnPaint是WM_PAINT消息引发的重绘消息处理函数，在OnPaint中会调用OnDraw来进行绘图。OnPaint中首先构造一个CPaintDC类得实例，然后一这个实例为参数来调用虚函数OnPrepareDC来进行一些绘制前的一些处理，比设置映射模式，最后调用OnDraw。而</font><a name=baidusnap0></a><font color=#000099><font style="FONT-FAMILY: 楷体_GB2312,楷体"><font style="FONT-FAMILY: 幼圆"><strong style="BACKGROUND-COLOR: rgb(255,255,102); COLOR: black">OnDraw和</strong>OnPrepareDC不是消息处理函数。所以在不是因为重绘消息所引发的OnPaint导致OnDraw被调用时，比如在OnLButtonDown等消息处理函数中绘图时，要先自己调用OnPrepareDC。<br>至于CPaintDC和CClientDC根本是两回事情 CPaintDC是一个设备环境类，在OnPaint中作为参数传递给OnPrepareDC来作设备环境的设置。真正和CClientDC具有可比性的是CWindowDC，他们一个是描述客户区域，一个是描述整个屏幕。<br>如果是对CVIEW或从CVIEW类派生的窗口绘图时应该用OnDraw。</font></font></font></font></p>
<p><font size=3><font style="FONT-FAMILY: 幼圆" color=#000099>OnDraw()</font><a name=baidusnap1></a><font color=#000099><font style="FONT-FAMILY: 楷体_GB2312,楷体"><font style="FONT-FAMILY: 幼圆"><strong style="BACKGROUND-COLOR: rgb(160,255,255); COLOR: black">和OnPaint</strong>()有什么区别呢？<br>首先：我们先要明确CView类派生自CWnd类。而OnPaint()是CWnd的类成员，同时负责响应WM_PAINT消息。OnDraw()是CVIEW的成员函数，并且没有响应消息的功能。这就是为什么你用VC成的程序代码时，在视图类只有OnDraw没有</font></font></font><a name=baidusnap2></a><font color=#000099><font style="FONT-FAMILY: 楷体_GB2312,楷体"><font style="FONT-FAMILY: 幼圆"><strong style="BACKGROUND-COLOR: rgb(153,255,153); COLOR: black">OnPaint的</strong>原因。而在基于对话框的程序中，只有OnPaint。<br>其次：我们在第《每天跟我学MFC》3的开始部分已经说到了。要想在屏幕上绘图或显示图形，首先需要建立设备环境DC。其实DC是一个数据结构，它包含输出设备（不单指你17寸的纯屏显示器，还包括打印机之类的输出设备）的绘图属性的描述。MFC提供了CPaintDC类和CWindwoDC类来实时的响应，而CPaintDC支持重画。当视图变得无效时（包括大小的改变，移动，被遮盖等等），Windows 将 WM_PAINT 消息发送给它。该视图的OnPaint 处理函数通过创建 CPaintDC 类的DC对象来响应该消息并调用视图的 OnDraw 成员函数。通常我们不必编写重写的 OnPaint 处理成员函数。<br>///CView默认的标准的重画函数<br>void CView::OnPaint() //见VIEWCORE.CPP<br>{</font></font></font></font></p>
<p><font color=#000099><font style="FONT-FAMILY: 楷体_GB2312,楷体"><font style="FONT-FAMILY: 幼圆"><font size=3>CPaintDC dc(this);<br>OnPrepareDC(&amp;dc)；<br>OnDraw(&amp;dc); <wbr></font><wbr>//调用了OnDraw<br>}<br>///CView默认的标准的OnPrint函数<br>void CView::OnPrint(CDC* pDC, CPrintInfo*)<br>{<br>ASSERT_VALID(pDC);<br>OnDraw(pDC); <wbr><wbr>// Call Draw<br>}</font></font></font></p>
<p><font style="FONT-FAMILY: 幼圆" color=#000099 size=3>既然OnPaint最后也要调用OnDraw,因此我们一般会在OnDraw函数中进行绘制。下面是一个典型的程序。<br>///视图中的绘图代码首先检索指向文档的指针，然后通过DC进行绘图调用。<br>void CMyView::OnDraw( CDC* pDC )<br>{</font></p>
<p><font style="FONT-FAMILY: 幼圆" color=#000099 size=3>CMyDoc* pDoc = GetDocument();<br>CString s = pDoc-&gt;GetData();<br>GetClientRect( &amp;rect ); // Returns a CString CRect rect;<br>pDC-&gt;SetTextAlign( TA_BASELINE | TA_CENTER );<br>pDC-&gt;TextOut( rect.right / 2, rect.bottom / 2, s, s.GetLength() );<br>}<br>最后：现在大家明白这哥俩之间的关系了吧。因此我们一般用OnPaint维护窗口的客户区（例如我们的窗口客户区加一个背景图片），用OnDraw维护视图的客户区（例如我们通过鼠标在视图中画图）。当然你也可以不按照上面规律来，只要达到目的并且没有问题，怎么干都成。补充：我们还可以利用Invalidate(),ValidateRgn(),ValidateRect()函数强制的重画窗口，具体的请参考MSDN吧。</font></p>
<p><font style="FONT-FAMILY: 幼圆" color=#000099 size=3>OnDraw中可以绘制用户区域。OnPaint中只是当窗口无效时重绘不会保留CClientDC绘制的内容。</font></p>
<p><font style="FONT-FAMILY: 幼圆" color=#000099 size=3>这两个函数有区别也有联系：</font></p>
<p><font style="FONT-FAMILY: 幼圆" color=#000099 size=3>1、区别：OnDraw是一个纯虚函数，定义为virtual void OnDraw( CDC* pDC ) = 0;　而OnPaint是一个消息响应函数，它响应了WM＿PANIT消息，也是是窗口重绘消息。</font></p>
<p><font style="FONT-FAMILY: 幼圆" color=#000099 size=3>2、联系：我们一般在视类中作图的时候，往往不直接响应WM＿PANIT消息，而是重载OnDraw纯虚函数，这是因为在CVIEW类中的WM＿PANIT消息响应函数中调用了OnDraw函数，如果在CMYVIEW类中响应了WM＿PAINT消息，不显式地调用OnDraw函数的话，是不会在窗口重绘的时候调用OnDraw函数的。</font></p>
<p><font style="FONT-FAMILY: 幼圆" color=#000099 size=3>应用程序中几乎所有的绘图都在视图的 OnDraw 成员函数中发生，必须在视图类中重写该成员函数。（鼠标绘图是个特例，这在通过视图解释用户输入中讨论。）</font></p>
<p><font size=3><br><font color=#000099><font style="FONT-FAMILY: 楷体_GB2312,楷体"><font style="FONT-FAMILY: 幼圆">OnDraw 重写：<br>通过调用您提供的文档成员函数获取数据。<br>通过调用框架传递给 OnDraw 的设备上下文对象的成员函数来显示数据。<br>当文档的数据以某种方式更改后，必须重绘视图以反映该更改。默认的 OnUpdate 实现使视图的整个工作区无效。当视图变得无效时，Windows 将 WM_PAINT 消息发送给它。该视图的 OnPaint 处理函数通过创建 CPaintDC 类的设备上下文对象来响应该消息并调用视图的 OnDraw 成员函数。<br><br>当没有添加WM_PAINT消息处理时,窗口重绘时,由OnDraw来进行消息响应...当添加WM_PAINT消息处理时,窗口重绘时,WM_PAINT消息被投递,由OnPaint来进行消息响应.这时就不能隐式调用OnDraw了.必须显式调用( <wbr></font></font></font></font><wbr><font style="FONT-FAMILY: 幼圆" color=#000099>CDC *pDC=GetDC(); OnDraw(pDC); <wbr><wbr>)..<br>隐式调用:当由OnPaint来进行消息响应时,系统自动调用CView::OnDraw(&amp;pDC).</font></p>
<p><br><font style="FONT-FAMILY: 幼圆" color=#000099 size=3>想象一下，窗口显示的内容和打印的内容是差不多的，所以，一般情况下，统一由OnDraw来画。窗口前景需要刷新时，系统会会调用到OnPaint，而OnPaint一般情况下是对DC作一些初始化操作后，调用OnDraw()。</font></p>
<p><font size=3><br><font color=#000099><font style="FONT-FAMILY: 楷体_GB2312,楷体"><font style="FONT-FAMILY: 幼圆">OnEraseBkGnd()，是窗口背景需要刷新时由系统调用的。明显的一个例子是设置窗口的背景颜色（你可以把这放在OnPaint中去做，但是会使产生闪烁的现象）。 <wbr></font></font></font></font><wbr><br><font style="FONT-FAMILY: 幼圆" color=#000099>至于怎么界定背景和前景，那要具体问题具体分析了，一般情况下，你还是很容易区别的吧。</font></p>
<p><font style="FONT-FAMILY: 幼圆" color=#000099 size=3>的确，OnPaint()用来响应WM_PAINT消息，视类的OnPaint()内部根据是打印还是屏幕绘制分别以不同的参数调用OnDraw()虚函数。所以在OnDraw()里你可以区别对待打印和屏幕绘制。<br>其实，MFC在进行打印前后还做了很多工作，调用了很多虚函数，比如OnPreparePrint()等。</font></p>
<p><br><strong><font style="FONT-FAMILY: 幼圆; FONT-SIZE: 20px" color=#000099>另外OnInitialUpdate</font></strong></p>
<p><font style="FONT-FAMILY: 幼圆; FONT-SIZE: 16px" color=#000099>视图窗口完全建立后第一个被框架调用的函数。框架在第一次调用OnDraw前会调用OnInitialUpdate，因此OnInitialUpdate是设置滚动视图的逻辑尺寸和映射模式的最合适的地方。</font></p>
<img src ="http://www.cppblog.com/dawnbreak/aggbug/93425.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/dawnbreak/" target="_blank">pear_li</a> 2009-08-15 14:56 <a href="http://www.cppblog.com/dawnbreak/articles/93425.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>c++操作符重载 </title><link>http://www.cppblog.com/dawnbreak/articles/92979.html</link><dc:creator>pear_li</dc:creator><author>pear_li</author><pubDate>Tue, 11 Aug 2009 16:00:00 GMT</pubDate><guid>http://www.cppblog.com/dawnbreak/articles/92979.html</guid><wfw:comment>http://www.cppblog.com/dawnbreak/comments/92979.html</wfw:comment><comments>http://www.cppblog.com/dawnbreak/articles/92979.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/dawnbreak/comments/commentRss/92979.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/dawnbreak/services/trackbacks/92979.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 今天我学习的是C++操作符重载。C++的操作符重载给C++带来很大的方便和灵活性。可以重载里面大部分操作符。这样在调用的时候就会相对的自然和简单。废话少说，直接奉上源代码(有部分相关的资料的注释都已经写在源代码上了):Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighte...&nbsp;&nbsp;<a href='http://www.cppblog.com/dawnbreak/articles/92979.html'>阅读全文</a><img src ="http://www.cppblog.com/dawnbreak/aggbug/92979.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/dawnbreak/" target="_blank">pear_li</a> 2009-08-12 00:00 <a href="http://www.cppblog.com/dawnbreak/articles/92979.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Makefile教程</title><link>http://www.cppblog.com/dawnbreak/articles/86350.html</link><dc:creator>pear_li</dc:creator><author>pear_li</author><pubDate>Mon, 01 Jun 2009 01:35:00 GMT</pubDate><guid>http://www.cppblog.com/dawnbreak/articles/86350.html</guid><wfw:comment>http://www.cppblog.com/dawnbreak/comments/86350.html</wfw:comment><comments>http://www.cppblog.com/dawnbreak/articles/86350.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/dawnbreak/comments/commentRss/86350.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/dawnbreak/services/trackbacks/86350.html</trackback:ping><description><![CDATA[<p>
<div class=twikiToc>
<ul>
    <li><a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#sa5Qtjlz.tcPw"><u><font color=#606420>Makefile学习教程: 跟我一起写 Makefile</font></u></a>
    <ul>
        <li><a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#sa5OOFabXjfoA"><u><font color=#606420>0 Makefile概述</font></u></a>
        <ul>
            <li><a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#sawMNIbg0DwoM"><u><font color=#606420>0.1 关于程序的编译和链接</font></u></a> </li>
        </ul>
        <li><a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#sasS2ZeUsjr3E"><u><font color=#606420>1 Makefile 介绍</font></u></a>
        <ul>
            <li><a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#saFo4MNg5pK8Y"><u><font color=#606420>1.1 Makefile的规则</font></u></a>
            <li><a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#safvghBu.bIEs"><u><font color=#606420>1.2 一个示例</font></u></a>
            <li><a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#saUhuTfg0njOg"><u><font color=#606420>1.3 make是如何工作的</font></u></a>
            <li><a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#sakYRADlySYSU"><u><font color=#606420>1.4 makefile中使用变量</font></u></a>
            <li><a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#saI0kS8BRp2aM"><u><font color=#606420>1.5 让make自动推导</font></u></a>
            <li><a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#saZz8bKL2yj32"><u><font color=#606420>1.6 另类风格的makefile</font></u></a>
            <li><a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#sajVLYsI/Te76"><u><font color=#606420>1.7 清空目标文件的规则</font></u></a> </li>
        </ul>
        <li><a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#saUwmWQH9kI.I"><u><font color=#606420>2 Makefile 总述</font></u></a>
        <ul>
            <li><a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#saE5AkeJJa502"><u><font color=#606420>2.1 Makefile里有什么？</font></u></a>
            <li><a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#saMBWVx.XqyII"><u><font color=#606420>2.2Makefile的文件名</font></u></a>
            <li><a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#safRh/qN.aud2"><u><font color=#606420>2.3 引用其它的Makefile</font></u></a>
            <li><a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#saJlQ/WyAfG6."><font color=#606420><u>2.4 环境变量 MAKEFILES </u></font></a>
            <li><a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#sakmFYqQxnGBk"><u><font color=#606420>2.5 make的工作方式</font></u></a> </li>
        </ul>
        <li><a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#saiq29h0Ym1Bc"><u><font color=#606420>3 Makefile书写规则</font></u></a>
        <ul>
            <li><a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#samig.o8A6/iY"><u><font color=#606420>3.1 规则举例</font></u></a>
            <li><a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#saRuj5N56iBD2"><u><font color=#606420>3.2 规则的语法</font></u></a>
            <li><a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#saCyyQ9dQni8o"><u><font color=#606420>3.3 在规则中使用通配符</font></u></a>
            <li><a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#sa.l9r9vXz/c2"><u><font color=#606420>3.4 文件搜寻</font></u></a>
            <li><a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#sax9yWM/IDfvE"><u><font color=#606420>3.5 伪目标</font></u></a>
            <li><a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#savYQiB4DeEVQ"><u><font color=#606420>3.6 多目标</font></u></a>
            <li><a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#sa0U2FeigPDZs"><u><font color=#606420>3.7 静态模式</font></u></a>
            <li><a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#sar/BOa9ebBdQ"><u><font color=#606420>3.8 自动生成依赖性</font></u></a> </li>
        </ul>
        <li><a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#saciXNKlLtQME"><u><font color=#606420>4 Makefile 书写命令</font></u></a>
        <ul>
            <li><a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#saCADAE0R8KnA"><u><font color=#606420>4.1 显示命令</font></u></a>
            <li><a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#saNGoM/Hp.BSg"><u><font color=#606420>4.2 命令执行</font></u></a>
            <li><a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#saejriK4eEx6A"><u><font color=#606420>4.3 命令出错</font></u></a>
            <li><a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#saRJsMKKuxk32"><u><font color=#606420>4.4 嵌套执行make</font></u></a>
            <li><a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#saVJ9lkWtLG16"><u><font color=#606420>4.5 定义命令包</font></u></a> </li>
        </ul>
        </li>
    </ul>
    </li>
</ul>
</div>
<h3><a name=sa5OOFabXjfoA></a>0 Makefile概述 </h3>
<hr>
什么是makefile？或许很多Winodws的程序员都不知道这个东西，因为那些Windows的IDE都为你做了这个工作，但我觉得要作一个好的和professional的程序员，makefile还是要懂。这就好像现在有这么多的HTML的编辑器，但如果你想成为一个专业人士，你还是要了解HTML的标识的含义。特别在Unix下的软件编译，你就不能不自己写makefile了，会不会写makefile，从一个侧面说明了一个人是否具备完成大型工程的能力。
<p>因为，makefile关系到了整个工程的编译规则。一个工程中的源文件不计数，其按类型、功能、模块分别放在若干个目录中，makefile定义了一系列的规则来指定，哪些文件需要先编译，哪些文件需要后编译，哪些文件需要重新编译，甚至于进行更复杂的功能操作，因为makefile就像一个Shell脚本一样，其中也可以执行操作系统的命令。
<p>makefile带来的好处就是——&#8220;自动化编译&#8221;，一旦写好，只需要一个make命令，整个工程完全自动编译，极大的提高了软件开发的效率。make是一个命令工具，是一个解释makefile中指令的命令工具，一般来说，大多数的IDE都有这个命令，比如：Delphi的make，Visual C++的nmake，Linux下GNU的make。可见，makefile都成为了一种在工程方面的编译方法。
<p>现在讲述如何写makefile的文章比较少，这是我想写这篇文章的原因。当然，不同产商的make各不相同，也有不同的语法，但其本质都是在&#8220;文件依赖性&#8221;上做文章，这里，我仅对GNU的make进行讲述，我的环境是RedHat Linux 8.0，make的版本是3.80。必竟，这个make是应用最为广泛的，也是用得最多的。而且其还是最遵循于IEEE 1003.2-1992 标准的（POSIX.2）。
<p>在这篇文档中，将以C/C++的源码作为我们基础，所以必然涉及一些关于C/C++的编译的知识，相关于这方面的内容，还请各位查看相关的编译器的文档。这里所默认的编译器是UNIX下的GCC和CC。
<p>
<h4><a name=sawMNIbg0DwoM></a>0.1 关于程序的编译和链接 </h4>
在此，我想多说关于程序编译的一些规范和方法，一般来说，无论是C、C++、还是pas，首先要把源文件编译成中间代码文件，在Windows下也就是 .obj 文件，UNIX下是 .o 文件，即 Object File，这个动作叫做编译（compile）。然后再把大量的Object File合成执行文件，这个动作叫作链接（link）。
<p>编译时，编译器需要的是语法的正确，函数与变量的声明的正确。对于后者，通常是你需要告诉编译器头文件的所在位置（头文件中应该只是声明，而定义应该放在C/C++文件中），只要所有的语法正确，编译器就可以编译出中间目标文件。一般来说，每个源文件都应该对应于一个中间目标文件（O文件或是OBJ文件）。
<p>链接时，主要是链接函数和全局变量，所以，我们可以使用这些中间目标文件（O文件或是OBJ文件）来链接我们的应用程序。链接器并不管函数所在的源文件，只管函数的中间目标文件（Object File），在大多数时候，由于源文件太多，编译生成的中间目标文件太多，而在链接时需要明显地指出中间目标文件名，这对于编译很不方便，所以，我们要给中间目标文件打个包，在Windows下这种包叫&#8220;库文件&#8221;（Library File)，也就是 .lib 文件，在UNIX下，是Archive File，也就是 .a 文件。
<p>总结一下，源文件首先会生成中间目标文件，再由中间目标文件生成执行文件。在编译时，编译器只检测程序语法，和函数、变量是否被声明。如果函数未被声明，编译器会给出一个警告，但可以生成Object File。而在链接程序时，链接器会在所有的Object File中找寻函数的实现，如果找不到，那到就会报链接错误码（Linker Error），在VC下，这种错误一般是：Link 2001错误，意思说是说，链接器未能找到函数的实现。你需要指定函数的Object File.
<p>好，言归正传，GNU的make有许多的内容，闲言少叙，还是让我们开始吧。
<p>
<h3><a name=sasS2ZeUsjr3E></a>1 Makefile 介绍 </h3>
<hr>
make命令执行时，需要一个 Makefile 文件，以告诉make命令需要怎么样的去编译和链接程序。
<p>首先，我们用一个示例来说明Makefile的书写规则。以便给大家一个感兴认识。这个示例来源于GNU的make使用手册，在这个示例中，我们的工程有8个C文件，和3个头文件，我们要写一个Makefile来告诉make命令如何编译和链接这几个文件。我们的规则是：
<ol>
    <li>如果这个工程没有编译过，那么我们的所有C文件都要编译并被链接。
    <li>如果这个工程的某几个C文件被修改，那么我们只编译被修改的C文件，并链接目标程序。
    <li>如果这个工程的头文件被改变了，那么我们需要编译引用了这几个头文件的C文件，并链接目标程序。 </li>
</ol>
<p>只要我们的Makefile写得够好，所有的这一切，我们只用一个make命令就可以完成，make命令会自动智能地根据当前的文件修改的情况来确定哪些文件需要重编译，从而自己编译所需要的文件和链接目标程序。
<h4><a name=saFo4MNg5pK8Y></a>1.1 Makefile的规则 </h4>
在讲述这个Makefile之前，还是让我们先来粗略地看一看Makefile的规则。
<pre style="PADDING-BOTTOM: 0px">target ... : prerequisites ...
command
...
...
</pre>
target也就是一个目标文件，可以是Object File，也可以是执行文件。还可以是一个标签（Label），对于标签这种特性，在后续的&#8220;伪目标&#8221;章节中会有叙述。
<p>prerequisites就是，要生成那个target所需要的文件或是目标。
<p>command也就是make需要执行的命令。（任意的Shell命令）
<p>这是一个文件的依赖关系，也就是说，target这一个或多个的目标文件依赖于prerequisites中的文件，其生成规则定义在command中。说白一点就是说，prerequisites中如果有一个以上的文件比target文件要新的话，command所定义的命令就会被执行。这就是Makefile的规则。也就是Makefile中最核心的内容。
<p>说到底，Makefile的东西就是这样一点，好像我的这篇文档也该结束了。呵呵。还不尽然，这是Makefile的主线和核心，但要写好一个Makefile还不够，我会以后面一点一点地结合我的工作经验给你慢慢到来。内容还多着呢。：）
<h4><a name=safvghBu.bIEs></a>1.2 一个示例 </h4>
正如前面所说的，如果一个工程有3个头文件，和8个C文件，我们为了完成前面所述的那三个规则，我们的Makefile应该是下面的这个样子的。
<pre style="PADDING-BOTTOM: 0px">    edit : main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
cc -o edit main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
main.o : main.c defs.h
cc -c main.c
kbd.o : kbd.c defs.h command.h
cc -c kbd.c
command.o : command.c defs.h command.h
cc -c command.c
display.o : display.c defs.h buffer.h
cc -c display.c
insert.o : insert.c defs.h buffer.h
cc -c insert.c
search.o : search.c defs.h buffer.h
cc -c search.c
files.o : files.c defs.h buffer.h command.h
cc -c files.c
utils.o : utils.c defs.h
cc -c utils.c
clean :
rm edit main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
</pre>
反斜杠（\）是换行符的意思。这样比较便于Makefile的易读。我们可以把这个内容保存在文件为&#8220;Makefile&#8221;或&#8220;makefile&#8221;的文件中，然后在该目录下直接输入命令&#8220;make&#8221;就可以生成执行文件edit。如果要删除执行文件和所有的中间目标文件，那么，只要简单地执行一下&#8220;make clean&#8221;就可以了。
<p>在这个makefile中，目标文件（target）包含：执行文件edit和中间目标文件（*.o），依赖文件（prerequisites）就是冒号后面的那些 .c 文件和 .h文件。每一个 .o 文件都有一组依赖文件，而这些 .o 文件又是执行文件 edit 的依赖文件。依赖关系的实质上就是说明了目标文件是由哪些文件生成的，换言之，目标文件是哪些文件更新的。
<p>在定义好依赖关系后，后续的那一行定义了如何生成目标文件的操作系统命令，一定要以一个Tab键作为开头。记住，make并不管命令是怎么工作的，他只管执行所定义的命令。make会比较targets文件和prerequisites文件的修改日期，如果prerequisites文件的日期要比targets文件的日期要新，或者target不存在的话，那么，make就会执行后续定义的命令。
<p>这里要说明一点的是，clean不是一个文件，它只不过是一个动作名字，有点像C语言中的lable一样，其冒号后什么也没有，那么，make就不会自动去找文件的依赖性，也就不会自动执行其后所定义的命令。要执行其后的命令，就要在make命令后明显得指出这个lable的名字。这样的方法非常有用，我们可以在一个makefile中定义不用的编译或是和编译无关的命令，比如程序的打包，程序的备份，等等。
<p>
<h4><a name=saUhuTfg0njOg></a>1.3 make是如何工作的 </h4>
在默认的方式下，也就是我们只输入make命令。那么，
<p>
<ol>
    <li>make会在当前目录下找名字叫&#8220;Makefile&#8221;或&#8220;makefile&#8221;的文件。
    <li>如果找到，它会找文件中的第一个目标文件（target），在上面的例子中，他会找到&#8220;edit&#8221;这个文件，并把这个文件作为最终的目标文件。
    <li>如果edit文件不存在，或是edit所依赖的后面的 .o 文件的文件修改时间要比edit这个文件新，那么，他就会执行后面所定义的命令来生成edit这个文件。
    <li>如果edit所依赖的.o文件也存在，那么make会在当前文件中找目标为.o文件的依赖性，如果找到则再根据那一个规则生成.o文件。（这有点像一个堆栈的过程）
    <li>当然，你的C文件和H文件是存在的啦，于是make会生成 .o 文件，然后再用 .o 文件生命make的终极任务，也就是执行文件edit了。 </li>
</ol>
<p>这就是整个make的依赖性，make会一层又一层地去找文件的依赖关系，直到最终编译出第一个目标文件。在找寻的过程中，如果出现错误，比如最后被依赖的文件找不到，那么make就会直接退出，并报错，而对于所定义的命令的错误，或是编译不成功，make根本不理。make只管文件的依赖性，即，如果在我找了依赖关系之后，冒号后面的文件还是不在，那么对不起，我就不工作啦。
<p>通过上述分析，我们知道，像clean这种，没有被第一个目标文件直接或间接关联，那么它后面所定义的命令将不会被自动执行，不过，我们可以显示要make执行。即命令——&#8220;make clean&#8221;，以此来清除所有的目标文件，以便重编译。
<p>于是在我们编程中，如果这个工程已被编译过了，当我们修改了其中一个源文件，比如file.c，那么根据我们的依赖性，我们的目标file.o会被重编译（也就是在这个依性关系后面所定义的命令），于是file.o的文件也是最新的啦，于是file.o的文件修改时间要比edit要新，所以edit也会被重新链接了（详见edit目标文件后定义的命令）。
<p>而如果我们改变了&#8220;command.h&#8221;，那么，kdb.o、command.o和files.o都会被重编译，并且，edit会被重链接。
<h4><a name=sakYRADlySYSU></a>1.4 makefile中使用变量 </h4>
在上面的例子中，先让我们看看edit的规则：
<pre style="PADDING-BOTTOM: 0px">      edit : main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
cc -o edit main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
</pre>
我们可以看到[.o]文件的字符串被重复了两次，如果我们的工程需要加入一个新的[.o]文件，那么我们需要在两个地方加（应该是三个地方，还有一个地方在clean中）。当然，我们的makefile并不复杂，所以在两个地方加也不累，但如果makefile变得复杂，那么我们就有可能会忘掉一个需要加入的地方，而导致编译失败。所以，为了makefile的易维护，在makefile中我们可以使用变量。makefile的变量也就是一个字符串，理解成C语言中的宏可能会更好。
<p>比如，我们声明一个变量，叫objects, OBJECTS, objs, OBJS, obj, 或是 OBJ，反正不管什么啦，只要能够表示obj文件就行了。我们在makefile一开始就这样定义： </p>
<pre style="PADDING-BOTTOM: 0px">     objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
</pre>
于是，我们就可以很方便地在我们的makefile中以&#8220;$(objects)&#8221;的方式来使用这个变量了，于是我们的改良版makefile就变成下面这个样子：
<pre style="PADDING-BOTTOM: 0px">    objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
edit : $(objects)
cc -o edit $(objects)
main.o : main.c defs.h
cc -c main.c
kbd.o : kbd.c defs.h command.h
cc -c kbd.c
command.o : command.c defs.h command.h
cc -c command.c
display.o : display.c defs.h buffer.h
cc -c display.c
insert.o : insert.c defs.h buffer.h
cc -c insert.c
search.o : search.c defs.h buffer.h
cc -c search.c
files.o : files.c defs.h buffer.h command.h
cc -c files.c
utils.o : utils.c defs.h
cc -c utils.c
clean :
rm edit $(objects)
</pre>
<p>于是如果有新的 .o 文件加入，我们只需简单地修改一下 objects 变量就可以了。
<p>关于变量更多的话题，我会在后续给你一一道来。
<h4><a name=saI0kS8BRp2aM></a>1.5 让make自动推导 </h4>
GNU的make很强大，它可以自动推导文件以及文件依赖关系后面的命令，于是我们就没必要去在每一个[.o]文件后都写上类似的命令，因为，我们的make会自动识别，并自己推导命令。
<p>只要make看到一个[.o]文件，它就会自动的把[.c]文件加在依赖关系中，如果make找到一个whatever.o，那么whatever.c，就会是whatever.o的依赖文件。并且 cc -c whatever.c 也会被推导出来，于是，我们的makefile再也不用写得这么复杂。我们的是新的makefile又出炉了。 </p>
<pre style="PADDING-BOTTOM: 0px">    objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
edit : $(objects)
cc -o edit $(objects)
main.o : defs.h
kbd.o : defs.h command.h
command.o : defs.h command.h
display.o : defs.h buffer.h
insert.o : defs.h buffer.h
search.o : defs.h buffer.h
files.o : defs.h buffer.h command.h
utils.o : defs.h
.PHONY : clean
clean :
rm edit $(objects)
</pre>
这种方法，也就是make的&#8220;隐晦规则&#8221;。上面文件内容中，&#8220;.PHONY&#8221;表示，clean是个伪目标文件。
<p>关于更为详细的&#8220;隐晦规则&#8221;和&#8220;伪目标文件&#8221;，我会在后续给你一一道来。
<h4><a name=saZz8bKL2yj32></a>1.6 另类风格的makefile </h4>
即然我们的make可以自动推导命令，那么我看到那堆[.o]和[.h]的依赖就有点不爽，那么多的重复的[.h]，能不能把其收拢起来，好吧，没有问题，这个对于make来说很容易，谁叫它提供了自动推导命令和文件的功能呢？来看看最新风格的makefile吧。
<pre style="PADDING-BOTTOM: 0px">    objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
edit : $(objects)
cc -o edit $(objects)
$(objects) : defs.h
kbd.o command.o files.o : command.h
display.o insert.o search.o files.o : buffer.h
.PHONY : clean
clean :
rm edit $(objects)
</pre>
这种风格，让我们的makefile变得很简单，但我们的文件依赖关系就显得有点凌乱了。鱼和熊掌不可兼得。还看你的喜好了。我是不喜欢这种风格的，一是文件的依赖关系看不清楚，二是如果文件一多，要加入几个新的.o文件，那就理不清楚了。
<h4><a name=sajVLYsI/Te76></a>1.7 清空目标文件的规则 </h4>
每个Makefile中都应该写一个清空目标文件（.o和执行文件）的规则，这不仅便于重编译，也很利于保持文件的清洁。这是一个&#8220;修养&#8221;（呵呵，还记得我的《编程修养》吗）。一般的风格都是：
<pre style="PADDING-BOTTOM: 0px">        clean:
rm edit $(objects)
</pre>
更为稳健的做法是：
<pre style="PADDING-BOTTOM: 0px">        .PHONY : clean
clean :
-rm edit $(objects)
</pre>
前面说过，.PHONY意思表示clean是一个&#8220;伪目标&#8221;，。而在rm命令前面加了一个小减号的意思就是，也许某些文件出现问题，但不要管，继续做后面的事。当然，clean的规则不要放在文件的开头，不然，这就会变成make的默认目标，相信谁也不愿意这样。不成文的规矩是——&#8220;clean从来都是放在文件的最后&#8221;。
<p>
<p>上面就是一个makefile的概貌，也是makefile的基础，下面还有很多makefile的相关细节，准备好了吗？准备好了就来。
<hr>
<h3><a name=saUwmWQH9kI.I></a>2 Makefile 总述 </h3>
<h4><a name=saE5AkeJJa502></a>2.1 Makefile里有什么？ </h4>
Makefile里主要包含了五个东西：显式规则、隐晦规则、变量定义、文件指示和注释。
<p>
<ol>
    <li>显式规则。显式规则说明了，如何生成一个或多的的目标文件。这是由Makefile的书写者明显指出，要生成的文件，文件的依赖文件，生成的命令。
    <li>隐晦规则。由于我们的make有自动推导的功能，所以隐晦的规则可以让我们比较粗糙地简略地书写Makefile，这是由make所支持的。
    <li>变量的定义。在Makefile中我们要定义一系列的变量，变量一般都是字符串，这个有点你C语言中的宏，当Makefile被执行时，其中的变量都会被扩展到相应的引用位置上。
    <li>文件指示。其包括了三个部分，一个是在一个Makefile中引用另一个Makefile，就像C语言中的include一样；另一个是指根据某些情况指定Makefile中的有效部分，就像C语言中的预编译#if一样；还有就是定义一个多行的命令。有关这一部分的内容，我会在后续的部分中讲述。
    <li>注释。Makefile中只有行注释，和UNIX的Shell脚本一样，其注释是用&#8220;#&#8221;字符，这个就像C/C++中的&#8220;//&#8221;一样。如果你要在你的Makefile中使用&#8220;#&#8221;字符，可以用反斜框进行转义，如：&#8220;\#&#8221;。 </li>
</ol>
<p>最后，还值得一提的是，在Makefile中的命令，必须要以[Tab]键开始。
<h4><a name=saMBWVx.XqyII></a>2.2Makefile的文件名 </h4>
默认的情况下，make命令会在当前目录下按顺序找寻文件名为&#8220;GNUmakefile&#8221;、&#8220;makefile&#8221;、&#8220;Makefile&#8221;的文件，找到了解释这个文件。在这三个文件名中，最好使用&#8220;Makefile&#8221;这个文件名，因为，这个文件名第一个字符为大写，这样有一种显目的感觉。最好不要用&#8220;GNUmakefile&#8221;，这个文件是GNU的make识别的。有另外一些make只对全小写的&#8220;makefile&#8221;文件名敏感，但是基本上来说，大多数的make都支持&#8220;makefile&#8221;和&#8220;Makefile&#8221;这两种默认文件名。
<p>当然，你可以使用别的文件名来书写Makefile，比如：&#8220;Make.Linux&#8221;，&#8220;Make.Solaris&#8221;，&#8220;Make.AIX&#8221;等，如果要指定特定的Makefile，你可以使用make的&#8220;-f&#8221;和&#8220;--file&#8221;参数，如：make -f Make.Linux或make --file Make.AIX。
<h4><a name=safRh/qN.aud2></a>2.3 引用其它的Makefile </h4>
在Makefile使用include关键字可以把别的Makefile包含进来，这很像C语言的#include，被包含的文件会原模原样的放在当前文件的包含位置。include的语法是：
<div class=BeautifierPlugin>
<div class=fragment>
<pre style="PADDING-BOTTOM: 0px">
<pre style="PADDING-BOTTOM: 0px">include &lt;filename&gt;</pre>
</pre>
</div>
</div>
filename可以是当前操作系统Shell的文件模式（可以保含路径和通配符）
<p>在include前面可以有一些空字符，但是绝不能是[Tab]键开始。include和<filename>可以用一个或多个空格隔开。举个例子，你有这样几个Makefile：a.mk、b.mk、c.mk，还有一个文件叫foo.make，以及一个变量$(bar)，其包含了e.mk和f.mk，那么，下面的语句： </p>
<pre style="PADDING-BOTTOM: 0px">    include foo.make *.mk $(bar)
</pre>
等价于：
<pre style="PADDING-BOTTOM: 0px">    include foo.make a.mk b.mk c.mk e.mk f.mk
</pre>
make命令开始时，会把找寻include所指出的其它Makefile，并把其内容安置在当前的位置。就好像C/C++的#include指令一样。如果文件都没有指定绝对路径或是相对路径的话，make会在当前目录下首先寻找，如果当前目录下没有找到，那么，make还会在下面的几个目录下找：
<p>
<ol>
    <li>如果make执行时，有&#8220;-I&#8221;或&#8220;--include-dir&#8221;参数，那么make就会在这个参数所指定的目录下去寻找。
    <li>如果目录
    <prefix>/include（一般是：/usr/local/bin或/usr/include）存在的话，make也会去找。 </li>
</ol>
<p>如果有文件没有找到的话，make会生成一条警告信息，但不会马上出现致命错误。它会继续载入其它的文件，一旦完成makefile的读取，make会再重试这些没有找到，或是不能读取的文件，如果还是不行，make才会出现一条致命信息。如果你想让make不理那些无法读取的文件，而继续执行，你可以在include前加一个减号&#8220;-&#8221;。如：
<div class=BeautifierPlugin>
<div class=fragment>
<pre style="PADDING-BOTTOM: 0px">
<pre style="PADDING-BOTTOM: 0px">-include &lt;filename&gt;</pre>
</pre>
</div>
</div>
其表示，无论include过程中出现什么错误，都不要报错继续执行。和其它版本make兼容的相关命令是sinclude，其作用和这一个是一样的。
<h4><a name=saJlQ/WyAfG6.></a>2.4 环境变量 MAKEFILES </h4>
如果你的当前环境中定义了环境变量MAKEFILES，那么，make会把这个变量中的值做一个类似于include的动作。这个变量中的值是其它的Makefile，用空格分隔。只是，它和include不同的是，从这个环境变中引入的Makefile的&#8220;目标&#8221;不会起作用，如果环境变量中定义的文件发现错误，make也会不理。
<p>但是在这里我还是建议不要使用这个环境变量，因为只要这个变量一被定义，那么当你使用make时，所有的Makefile都会受到它的影响，这绝不是你想看到的。在这里提这个事，只是为了告诉大家，也许有时候你的Makefile出现了怪事，那么你可以看看当前环境中有没有定义这个变量。
<h4><a name=sakmFYqQxnGBk></a>2.5 make的工作方式 </h4>
GNU的make工作时的执行步骤入下：（想来其它的make也是类似）
<p>
<ol>
    <li>读入所有的Makefile。
    <li>读入被include的其它Makefile。
    <li>初始化文件中的变量。
    <li>推导隐晦规则，并分析所有规则。
    <li>为所有的目标文件创建依赖关系链。
    <li>根据依赖关系，决定哪些目标要重新生成。
    <li>执行生成命令。 </li>
</ol>
<p>1-5步为第一个阶段，6-7为第二个阶段。第一个阶段中，如果定义的变量被使用了，那么，make会把其展开在使用的位置。但make并不会完全马上展开，make使用的是拖延战术，如果变量出现在依赖关系的规则中，那么仅当这条依赖被决定要使用了，变量才会在其内部展开。
<p>当然，这个工作方式你不一定要清楚，但是知道这个方式你也会对make更为熟悉。有了这个基础，后续部分也就容易看懂了。
<h3><a name=saiq29h0Ym1Bc></a>3 Makefile书写规则 </h3>
<hr>
规则包含两个部分，一个是依赖关系，一个是生成目标的方法。
<p>在Makefile中，规则的顺序是很重要的，因为，Makefile中只应该有一个最终目标，其它的目标都是被这个目标所连带出来的，所以一定要让make知道你的最终目标是什么。一般来说，定义在Makefile中的目标可能会有很多，但是第一条规则中的目标将被确立为最终的目标。如果第一条规则中的目标有很多个，那么，第一个目标会成为最终的目标。make所完成的也就是这个目标。
<p>好了，还是让我们来看一看如何书写规则。
<h4><a name=samig.o8A6/iY></a>3.1 规则举例 </h4>
<pre style="PADDING-BOTTOM: 0px"> foo.o : foo.c defs.h       # foo模块
cc -c -g foo.c
</pre>
看到这个例子，各位应该不是很陌生了，前面也已说过，foo.o是我们的目标，foo.c和defs.h是目标所依赖的源文件，而只有一个命令&#8220;cc -c -g foo.c&#8221;（以Tab键开头）。这个规则告诉我们两件事：
<p>
<ol>
    <li>文件的依赖关系，foo.o依赖于foo.c和defs.h的文件，如果foo.c和defs.h的文件日期要比foo.o文件日期要新，或是foo.o不存在，那么依赖关系发生。
    <li>如果生成（或更新）foo.o文件。也就是那个cc命令，其说明了，如何生成foo.o这个文件。（当然foo.c文件include了defs.h文件） </li>
</ol>
<p>
<h4><a name=saRuj5N56iBD2></a>3.2 规则的语法 </h4>
<pre style="PADDING-BOTTOM: 0px">      targets : prerequisites
command
...
</pre>
或是这样：
<pre style="PADDING-BOTTOM: 0px">      targets : prerequisites ; command
command
...
</pre>
targets是文件名，以空格分开，可以使用通配符。一般来说，我们的目标基本上是一个文件，但也有可能是多个文件。
<p>command是命令行，如果其不与&#8220;target:prerequisites&#8221;在一行，那么，必须以[Tab键]开头，如果和prerequisites在一行，那么可以用分号做为分隔。（见上）
<p>prerequisites也就是目标所依赖的文件（或依赖目标）。如果其中的某个文件要比目标文件要新，那么，目标就被认为是&#8220;过时的&#8221;，被认为是需要重生成的。这个在前面已经讲过了。
<p>如果命令太长，你可以使用反斜框（&#8216;\&#8217;）作为换行符。make对一行上有多少个字符没有限制。规则告诉make两件事，文件的依赖关系和如何成成目标文件。
<p>一般来说，make会以UNIX的标准Shell，也就是/bin/sh来执行命令。
<h4><a name=saCyyQ9dQni8o></a>3.3 在规则中使用通配符 </h4>
<p>如果我们想定义一系列比较类似的文件，我们很自然地就想起使用通配符。make支持三各通配符：&#8220;*&#8221;，&#8220;?&#8221;和&#8220;[...]&#8221;。这是和Unix的B-Shell是相同的。
<p>波浪号（&#8220;~&#8221;）字符在文件名中也有比较特殊的用途。如果是&#8220;~/test&#8221;，这就表示当前用户的$HOME目录下的test目录。而&#8220;~hchen/test&#8221;则表示用户hchen的宿主目录下的test目录。（这些都是Unix下的小知识了，make也支持）而在Windows或是MS-DOS下，用户没有宿主目录，那么波浪号所指的目录则根据环境变量&#8220;HOME&#8221;而定。
<p>通配符代替了你一系列的文件，如&#8220;*.c&#8221;表示所以后缀为c的文件。一个需要我们注意的是，如果我们的文件名中有通配符，如：&#8220;*&#8221;，那么可以用转义字符&#8220;\&#8221;，如&#8220;\*&#8221;来表示真实的&#8220;*&#8221;字符，而不是任意长度的字符串。
<p>好吧，还是先来看几个例子吧： </p>
<pre style="PADDING-BOTTOM: 0px">    clean:
rm -f *.o
</pre>
上面这个例子我不不多说了，这是操作系统Shell所支持的通配符。这是在命令中的通配符。
<pre style="PADDING-BOTTOM: 0px">    print: *.c
lpr -p $?
touch print
</pre>
上面这个例子说明了通配符也可以在我们的规则中，目标print依赖于所有的[.c]文件。其中的&#8220;$?&#8221;是一个自动化变量，我会在后面给你讲述。
<pre style="PADDING-BOTTOM: 0px">    objects = *.o
</pre>
上面这个例子，表示了，通符同样可以用在变量中。并不是说[*.o]会展开，不！objects的值就是&#8220;*.o&#8221;。Makefile中的变量其实就是C/C++中的宏。如果你要让通配符在变量中展开，也就是让objects的值是所有[.o]的文件名的集合，那么，你可以这样：
<pre style="PADDING-BOTTOM: 0px">    objects := $(wildcard *.o)
</pre>
这种用法由关键字&#8220;wildcard&#8221;指出，关于Makefile的关键字，我们将在后面讨论。
<h4><a name=sa.l9r9vXz/c2></a>3.4 文件搜寻 </h4>
<p>在一些大的工程中，有大量的源文件，我们通常的做法是把这许多的源文件分类，并存放在不同的目录中。所以，当make需要去找寻文件的依赖关系时，你可以在文件前加上路径，但最好的方法是把一个路径告诉make，让make在自动去找。
<p>Makefile文件中的特殊变量&#8220;VPATH&#8221;就是完成这个功能的，如果没有指明这个变量，make只会在当前的目录中去找寻依赖文件和目标文件。如果定义了这个变量，那么，make就会在当当前目录找不到的情况下，到所指定的目录中去找寻文件了。 </p>
<pre style="PADDING-BOTTOM: 0px">    VPATH = src:../headers
</pre>
上面的的定义指定两个目录，&#8220;src&#8221;和&#8220;../headers&#8221;，make会按照这个顺序进行搜索。目录由&#8220;冒号&#8221;分隔。（当然，当前目录永远是最高优先搜索的地方）
<p>另一个设置文件搜索路径的方法是使用make的&#8220;vpath&#8221;关键字（注意，它是全小写的），这不是变量，这是一个make的关键字，这和上面提到的那个VPATH变量很类似，但是它更为灵活。它可以指定不同的文件在不同的搜索目录中。这是一个很灵活的功能。它的使用方法有三种：
<ol>
    <li>vpath &lt; pattern&gt; &lt; directories&gt; <br>为符合模式&lt; pattern&gt;的文件指定搜索目录&lt; directories&gt;。
    <li>vpath &lt; pattern&gt;<br>清除符合模式&lt; pattern&gt;的文件的搜索目录。
    <li>vpath <br>清除所有已被设置好了的文件搜索目录。 </li>
</ol>
<p>vapth使用方法中的&lt; pattern&gt;需要包含&#8220;%&#8221;字符。&#8220;%&#8221;的意思是匹配零或若干字符，例如，&#8220;%.h&#8221;表示所有以&#8220;.h&#8221;结尾的文件。&lt; pattern&gt;指定了要搜索的文件集，而&lt; directories&gt;则指定了
<pattern>的文件集的搜索的目录。例如： </p>
<pre style="PADDING-BOTTOM: 0px">    vpath %.h ../headers
</pre>
该语句表示，要求make在&#8220;../headers&#8221;目录下搜索所有以&#8220;.h&#8221;结尾的文件。（如果某文件在当前目录没有找到的话）
<p>我们可以连续地使用vpath语句，以指定不同搜索策略。如果连续的vpath语句中出现了相同的&lt; pattern&gt;，或是被重复了的&lt; pattern&gt;，那么，make会按照vpath语句的先后顺序来执行搜索。如： </p>
<pre style="PADDING-BOTTOM: 0px">    vpath %.c foo
vpath %   blish
vpath %.c bar
</pre>
其表示&#8220;.c&#8221;结尾的文件，先在&#8220;foo&#8221;目录，然后是&#8220;blish&#8221;，最后是&#8220;bar&#8221;目录。
<pre style="PADDING-BOTTOM: 0px">    vpath %.c foo:bar
vpath %   blish
</pre>
而上面的语句则表示&#8220;.c&#8221;结尾的文件，先在&#8220;foo&#8221;目录，然后是&#8220;bar&#8221;目录，最后才是&#8220;blish&#8221;目录。
<h4><a name=sax9yWM/IDfvE></a>3.5 伪目标 </h4>
<p>最早先的一个例子中，我们提到过一个&#8220;clean&#8221;的目标，这是一个&#8220;伪目标&#8221;， </p>
<pre style="PADDING-BOTTOM: 0px">    clean:
rm *.o temp
</pre>
正像我们前面例子中的&#8220;clean&#8221;一样，即然我们生成了许多文件编译文件，我们也应该提供一个清除它们的&#8220;目标&#8221;以备完整地重编译而用。 （以&#8220;make clean&#8221;来使用该目标）
<p>因为，我们并不生成&#8220;clean&#8221;这个文件。&#8220;伪目标&#8221;并不是一个文件，只是一个标签，由于&#8220;伪目标&#8221;不是文件，所以make无法生成它的依赖关系和决定它是否要执行。我们只有通过显示地指明这个&#8220;目标&#8221;才能让其生效。当然，&#8220;伪目标&#8221;的取名不能和文件名重名，不然其就失去了&#8220;伪目标&#8221;的意义了。
<p>当然，为了避免和文件重名的这种情况，我们可以使用一个特殊的标记&#8220;.PHONY&#8221;来显示地指明一个目标是&#8220;伪目标&#8221;，向make说明，不管是否有这个文件，这个目标就是&#8220;伪目标&#8221;。 </p>
<pre style="PADDING-BOTTOM: 0px">    .PHONY : clean
</pre>
只要有这个声明，不管是否有&#8220;clean&#8221;文件，要运行&#8220;clean&#8221;这个目标，只有&#8220;make clean&#8221;这样。于是整个过程可以这样写：
<pre style="PADDING-BOTTOM: 0px">     .PHONY: clean
clean:
rm *.o temp
</pre>
伪目标一般没有依赖的文件。但是，我们也可以为伪目标指定所依赖的文件。伪目标同样可以作为&#8220;默认目标&#8221;，只要将其放在第一个。一个示例就是，如果你的Makefile需要一口气生成若干个可执行文件，但你只想简单地敲一个make完事，并且，所有的目标文件都写在一个Makefile中，那么你可以使用&#8220;伪目标&#8221;这个特性：
<pre style="PADDING-BOTTOM: 0px">    all : prog1 prog2 prog3
.PHONY : all
prog1 : prog1.o utils.o
cc -o prog1 prog1.o utils.o
prog2 : prog2.o
cc -o prog2 prog2.o
prog3 : prog3.o sort.o utils.o
cc -o prog3 prog3.o sort.o utils.o
</pre>
我们知道，Makefile中的第一个目标会被作为其默认目标。我们声明了一个&#8220;all&#8221;的伪目标，其依赖于其它三个目标。由于伪目标的特性是，总是被执行的，所以其依赖的那三个目标就总是不如&#8220;all&#8221;这个目标新。所以，其它三个目标的规则总是会被决议。也就达到了我们一口气生成多个目标的目的。&#8220;.PHONY : all&#8221;声明了&#8220;all&#8221;这个目标为&#8220;伪目标&#8221;。
<p>随便提一句，从上面的例子我们可以看出，目标也可以成为依赖。所以，伪目标同样也可成为依赖。看下面的例子： </p>
<pre style="PADDING-BOTTOM: 0px">    .PHONY: cleanall cleanobj cleandiff
cleanall : cleanobj cleandiff
rm program
cleanobj :
rm *.o
cleandiff :
rm *.diff
</pre>
&#8220;make clean&#8221;将清除所有要被清除的文件。&#8220;cleanobj&#8221;和&#8220;cleandiff&#8221;这两个伪目标有点像&#8220;子程序&#8221;的意思。我们可以输入&#8220;make cleanall&#8221;和&#8220;make cleanobj&#8221;和&#8220;make cleandiff&#8221;命令来达到清除不同种类文件的目的
<p>
<h4><a name=savYQiB4DeEVQ></a>3.6 多目标 </h4>
<p>Makefile的规则中的目标可以不止一个，其支持多目标，有可能我们的多个目标同时依赖于一个文件，并且其生成的命令大体类似。于是我们就能把其合并起来。当然，多个目标的生成规则的执行命令是同一个，这可能会可我们带来麻烦，不过好在我们的可以使用一个自动化变量&#8220;$@&#8221;（关于自动化变量，将在后面讲述），这个变量表示着目前规则中所有的目标的集合，这样说可能很抽象，还是看一个例子吧。 </p>
<pre style="PADDING-BOTTOM: 0px">    bigoutput littleoutput : text.g
generate text.g -$(subst output,,$@) &gt; $@
<pre style="PADDING-BOTTOM: 0px">    上述规则等价于：
</pre>
bigoutput : text.g
generate text.g -big &gt; bigoutput
littleoutput : text.g
generate text.g -little &gt; littleoutput
</pre>
其中，-$(subst output,,$@)中的&#8220;$&#8221;表示执行一个Makefile的函数，函数名为subst，后面的为参数。关于函数，将在后面讲述。这里的这个函数是截取字符串的意思，&#8220;$@&#8221;表示目标的集合，就像一个数组，&#8220;$@&#8221;依次取出目标，并执于命令。
<p>
<p>
<h4><a name=sa0U2FeigPDZs></a>3.7 静态模式 </h4>
<p>静态模式可以更加容易地定义多目标的规则，可以让我们的规则变得更加的有弹性和灵活。我们还是先来看一下语法：
<div class=BeautifierPlugin>
<div class=fragment>
<pre style="PADDING-BOTTOM: 0px">
<pre style="PADDING-BOTTOM: 0px">&lt;targets ...&gt;: &lt;target-pattern&gt;: &lt;prereq-patterns ...&gt;
　　　&lt;commands&gt;
...</pre>
</pre>
</div>
</div>
<p>targets定义了一系列的目标文件，可以有通配符。是目标的一个集合。
<p>target-parrtern是指明了targets的模式，也就是的目标集模式。
<p>prereq-parrterns是目标的依赖模式，它对target-parrtern形成的模式再进行一次依赖目标的定义。
<p>这样描述这三个东西，可能还是没有说清楚，还是举个例子来说明一下吧。如果我们的&lt;target-parrtern&gt;定义成&#8220;%.o&#8221;，意思是我们的<target>集合中都是以&#8220;.o&#8221;结尾的，而如果我们的&lt;prereq-parrterns&gt;定义成&#8220;%.c&#8221;，意思是对&lt;target-parrtern&gt;所形成的目标集进行二次定义，其计算方法是，取&lt;target-parrtern&gt;模式中的&#8220;%&#8221;（也就是去掉了[.o]这个结尾），并为其加上[.c]这个结尾，形成的新集合。
<p>所以，我们的&#8220;目标模式&#8221;或是&#8220;依赖模式&#8221;中都应该有&#8220;%&#8221;这个字符，如果你的文件名中有&#8220;%&#8221;那么你可以使用反斜杠&#8220;\&#8221;进行转义，来标明真实的&#8220;%&#8221;字符。
<p>看一个例子： </p>
<pre style="PADDING-BOTTOM: 0px">    objects = foo.o bar.o
all: $(objects)
$(objects): %.o: %.c
$(CC) -c $(CFLAGS) $&lt; -o $@
</pre>
上面的例子中，指明了我们的目标从$object中获取，&#8220;%.o&#8221;表明要所有以&#8220;.o&#8221;结尾的目标，也就是&#8220;foo.o bar.o&#8221;，也就是变量$object集合的模式，而依赖模式&#8220;%.c&#8221;则取模式&#8220;%.o&#8221;的&#8220;%&#8221;，也就是&#8220;foo bar&#8221;，并为其加下&#8220;.c&#8221;的后缀，于是，我们的依赖目标就是&#8220;foo.c bar.c&#8221;。而命令中的&#8220;$&lt;&#8221;和&#8220;$@&#8221;则是自动化变量，&#8220;$&lt;&#8221;表示所有的依赖目标集（也就是&#8220;foo.c bar.c&#8221;），&#8220;$@&#8221;表示目标集（也褪恰癴oo.o bar.o&#8221;）。于是，上面的规则展开后等价于下面的规则：
<pre style="PADDING-BOTTOM: 0px">    foo.o : foo.c
$(CC) -c $(CFLAGS) foo.c -o foo.o
bar.o : bar.c
$(CC) -c $(CFLAGS) bar.c -o bar.o
</pre>
试想，如果我们的&#8220;%.o&#8221;有几百个，那种我们只要用这种很简单的&#8220;静态模式规则&#8221;就可以写完一堆规则，实在是太有效率了。&#8220;静态模式规则&#8221;的用法很灵活，如果用得好，那会一个很强大的功能。再看一个例子：
<pre style="PADDING-BOTTOM: 0px">    files = foo.elc bar.o lose.o
$(filter %.o,$(files)): %.o: %.c
$(CC) -c $(CFLAGS) $&lt; -o $@
$(filter %.elc,$(files)): %.elc: %.el
emacs -f batch-byte-compile $&lt;
</pre>
<p>$(filter %.o,$(files))表示调用Makefile的filter函数，过滤&#8220;$filter&#8221;集，只要其中模式为&#8220;%.o&#8221;的内容。其的它内容，我就不用多说了吧。这个例字展示了Makefile中更大的弹性。
<h4><a name=sar/BOa9ebBdQ></a>3.8 自动生成依赖性 </h4>
<p>在Makefile中，我们的依赖关系可能会需要包含一系列的头文件，比如，如果我们的main.c中有一句&#8220;#include "defs.h"&#8221;，那么我们的依赖关系应该是： </p>
<pre style="PADDING-BOTTOM: 0px">    main.o : main.c defs.h
</pre>
但是，如果是一个比较大型的工程，你必需清楚哪些C文件包含了哪些头文件，并且，你在加入或删除头文件时，也需要小心地修改Makefile，这是一个很没有维护性的工作。为了避免这种繁重而又容易出错的事情，我们可以使用C/C++编译的一个功能。大多数的C/C++编译器都支持一个&#8220;-M&#8221;的选项，即自动找寻源文件中包含的头文件，并生成一个依赖关系。例如，如果我们执行下面的命令：
<pre style="PADDING-BOTTOM: 0px">    cc -M main.c
</pre>
其输出是：
<pre style="PADDING-BOTTOM: 0px">    main.o : main.c defs.h
</pre>
于是由编译器自动生成的依赖关系，这样一来，你就不必再手动书写若干文件的依赖关系，而由编译器自动生成了。需要提醒一句的是，如果你使用GNU的C/C++编译器，你得用&#8220;-MM&#8221;参数，不然，&#8220;-M&#8221;参数会把一些标准库的头文件也包含进来。
<p>gcc -M main.c的输出是： </p>
<pre style="PADDING-BOTTOM: 0px">    main.o: main.c defs.h /usr/include/stdio.h /usr/include/features.h \
/usr/include/sys/cdefs.h /usr/include/gnu/stubs.h \
/usr/lib/gcc-lib/i486-suse-linux/2.95.3/include/stddef.h \
/usr/include/bits/types.h /usr/include/bits/pthreadtypes.h \
/usr/include/bits/sched.h /usr/include/libio.h \
/usr/include/_G_config.h /usr/include/wchar.h \
/usr/include/bits/wchar.h /usr/include/gconv.h \
/usr/lib/gcc-lib/i486-suse-linux/2.95.3/include/stdarg.h \
/usr/include/bits/stdio_lim.h
</pre>
gcc -MM main.c的输出则是：
<pre style="PADDING-BOTTOM: 0px">    main.o: main.c defs.h
</pre>
那么，编译器的这个功能如何与我们的Makefile联系在一起呢。因为这样一来，我们的Makefile也要根据这些源文件重新生成，让Makefile自已依赖于源文件？这个功能并不现实，不过我们可以有其它手段来迂回地实现这一功能。GNU组织建议把编译器为每一个源文件的自动生成的依赖关系放到一个文件中，为每一个&#8220;name.c&#8221;的文件都生成一个&#8220;name.d&#8221;的Makefile文件，[.d]文件中就存放对应[.c]文件的依赖关系。
<p>于是，我们可以写出[.c]文件和[.d]文件的依赖关系，并让make自动更新或自成[.d]文件，并把其包含在我们的主Makefile中，这样，我们就可以自动化地生成每个文件的依赖关系了。
<p>这里，我们给出了一个模式规则来产生[.d]文件： </p>
<pre style="PADDING-BOTTOM: 0px">    %.d: %.c
@set -e; rm -f $@; \
$(CC) -M $(CPPFLAGS) $&lt; &gt; $@.$$$$; \
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' &lt; $@.$$$$ &gt; $@; \
rm -f $@.$$$$
</pre>
<p>这个规则的意思是，所有的[.d]文件依赖于[.c]文件，&#8220;rm -f $@&#8221;的意思是删除所有的目标，也就是[.d]文件，第二行的意思是，为每个依赖文件&#8220;$&lt;&#8221;，也就是[.c]文件生成依赖文件，&#8220;$@&#8221;表示模式&#8220;%.d&#8221;文件，如果有一个C文件是name.c，那么&#8220;%&#8221;就是&#8220;name&#8221;，&#8220;$$$$&#8221;意为一个随机编号，第二行生成的文件有可能是&#8220;name.d.12345&#8221;，第三行使用sed命令做了一个替换，关于sed命令的用法请参看相关的使用文档。第四行就是删除临时文件。
<p>总而言之，这个模式要做的事就是在编译器生成的依赖关系中加入[.d]文件的依赖，即把依赖关系： </p>
<pre style="PADDING-BOTTOM: 0px">    main.o : main.c defs.h
</pre>
转成：
<pre style="PADDING-BOTTOM: 0px">    main.o main.d : main.c defs.h
</pre>
于是，我们的[.d]文件也会自动更新了，并会自动生成了，当然，你还可以在这个[.d]文件中加入的不只是依赖关系，包括生成的命令也可一并加入，让每个[.d]文件都包含一个完赖的规则。一旦我们完成这个工作，接下来，我们就要把这些自动生成的规则放进我们的主Makefile中。我们可以使用Makefile的&#8220;include&#8221;命令，来引入别的Makefile文件（前面讲过），例如：
<pre style="PADDING-BOTTOM: 0px">    sources = foo.c bar.c
include $(sources:.c=.d)
</pre>
上述语句中的&#8220;$(sources:.c=.d)&#8221;中的&#8220;.c=.d&#8221;的意思是做一个替换，把变量$(sources)所有[.c]的字串都替换成[.d]，关于这个&#8220;替换&#8221;的内容，在后面我会有更为详细的讲述。当然，你得注意次序，因为include是按次来载入文件，最先载入的[<no>.d]文件中的目标会成为默认目标
<h3><a name=saciXNKlLtQME></a>4 Makefile 书写命令 </h3>
<hr>
<p>每条规则中的命令和操作系统Shell的命令行是一致的。make会一按顺序一条一条的执行命令，每条命令的开头必须以[Tab]键开头，除非，命令是紧跟在依赖规则后面的分号后的。在命令行之间中的空格或是空行会被忽略，但是如果该空格或空行是以Tab键开头的，那么make会认为其是一个空命令。
<p>我们在UNIX下可能会使用不同的Shell，但是make的命令默认是被&#8220;/bin/sh&#8221;——UNIX的标准Shell解释执行的。除非你特别指定一个其它的Shell。Makefile中，&#8220;#&#8221;是注释符，很像C/C++中的&#8220;//&#8221;，其后的本行字符都被注释。
<p>
<h4><a name=saCADAE0R8KnA></a>4.1 显示命令 </h4>
<p>通常，make会把其要执行的命令行在命令执行前输出到屏幕上。当我们用&#8220;@&#8221;字符在命令行前，那么，这个命令将不被make显示出来，最具代表性的例子是，我们用这个功能来像屏幕显示一些信息。如： </p>
<pre style="PADDING-BOTTOM: 0px">    @echo 正在编译XXX模块......
</pre>
当make执行时，会输出&#8220;正在编译XXX模块......&#8221;字串，但不会输出命令，如果没有&#8220;@&#8221;，那么，make将输出：
<pre style="PADDING-BOTTOM: 0px">    echo 正在编译XXX模块......
正在编译XXX模块......
</pre>
如果make执行时，带入make参数&#8220;-n&#8221;或&#8220;--just-print&#8221;，那么其只是显示命令，但不会执行命令，这个功能很有利于我们调试我们的Makefile，看看我们书写的命令是执行起来是什么样子的或是什么顺序的。
<p>而make参数&#8220;-s&#8221;或&#8220;--slient&#8221;则是全面禁止命令的显示。
<p>
<h4><a name=saNGoM/Hp.BSg></a>4.2 命令执行 </h4>
<p>当依赖目标新于目标时，也就是当规则的目标需要被更新时，make会一条一条的执行其后的命令。需要注意的是，如果你要让上一条命令的结果应用在下一条命令时，你应该使用分号分隔这两条命令。比如你的第一条命令是cd命令，你希望第二条命令得在cd之后的基础上运行，那么你就不能把这两条命令写在两行上，而应该把这两条命令写在一行上，用分号分隔。如： </p>
<pre style="PADDING-BOTTOM: 0px">    示例一：
exec:
cd /home/hchen
pwd
示例二：
exec:
cd /home/hchen; pwd
</pre>
<p>当我们执行&#8220;make exec&#8221;时，第一个例子中的cd没有作用，pwd会打印出当前的Makefile目录，而第二个例子中，cd就起作用了，pwd会打印出&#8220;/home/hchen&#8221;。
<p>make一般是使用环境变量SHELL中所定义的系统Shell来执行命令，默认情况下使用UNIX的标准Shell——/bin/sh来执行命令。但在MS-DOS下有点特殊，因为MS-DOS下没有SHELL环境变量，当然你也可以指定。如果你指定了UNIX风格的目录形式，首先，make会在SHELL所指定的路径中找寻命令解释器，如果找不到，其会在当前盘符中的当前目录中寻找，如果再找不到，其会在PATH环境变量中所定义的所有路径中寻找。MS-DOS中，如果你定义的命令解释器没有找到，其会给你的命令解释器加上诸如&#8220;.exe&#8221;、&#8220;.com&#8221;、&#8220;.bat&#8221;、&#8220;.sh&#8221;等后缀。
<p>
<h4><a name=saejriK4eEx6A></a>4.3 命令出错 </h4>
<p>每当命令运行完后，make会检测每个命令的返回码，如果命令返回成功，那么make会执行下一条命令，当规则中所有的命令成功返回后，这个规则就算是成功完成了。如果一个规则中的某个命令出错了（命令退出码非零），那么make就会终止执行当前规则，这将有可能终止所有规则的执行。
<p>有些时候，命令的出错并不表示就是错误的。例如mkdir命令，我们一定需要建立一个目录，如果目录不存在，那么mkdir就成功执行，万事大吉，如果目录存在，那么就出错了。我们之所以使用mkdir的意思就是一定要有这样的一个目录，于是我们就不希望mkdir出错而终止规则的运行。
<p>为了做到这一点，忽略命令的出错，我们可以在Makefile的命令行前加一个减号&#8220;-&#8221;（在Tab键之后），标记为不管命令出不出错都认为是成功的。如： </p>
<pre style="PADDING-BOTTOM: 0px">   clean:
-rm -f *.o
</pre>
还有一个全局的办法是，给make加上&#8220;-i&#8221;或是&#8220;--ignore-errors&#8221;参数，那么，Makefile中所有命令都会忽略错误。而如果一个规则是以&#8220;.IGNORE&#8221;作为目标的，那么这个规则中的所有命令将会忽略错误。这些是不同级别的防止命令出错的方法，你可以根据你的不同喜欢设置。
<p>还有一个要提一下的make的参数的是&#8220;-k&#8221;或是&#8220;--keep-going&#8221;，这个参数的意思是，如果某规则中的命令出错了，那么就终目该规则的执行，但继续执行其它规则。
<h4><a name=saRJsMKKuxk32></a>4.4 嵌套执行make </h4>
<p>在一些大的工程中，我们会把我们不同模块或是不同功能的源文件放在不同的目录中，我们可以在每个目录中都书写一个该目录的Makefile，这有利于让我们的Makefile变得更加地简洁，而不至于把所有的东西全部写在一个Makefile中，这样会很难维护我们的Makefile，这个技术对于我们模块编译和分段编译有着非常大的好处。
<p>例如，我们有一个子目录叫subdir，这个目录下有个Makefile文件，来指明了这个目录下文件的编译规则。那么我们总控的Makefile可以这样书写： </p>
<pre style="PADDING-BOTTOM: 0px">    subsystem:
cd subdir &amp;&amp; $(MAKE)
其等价于：
subsystem:
$(MAKE) -C subdir
</pre>
定义$(MAKE)宏变量的意思是，也许我们的make需要一些参数，所以定义成一个变量比较利于维护。这两个例子的意思都是先进入&#8220;subdir&#8221;目录，然后执行make命令。
<p>我们把这个Makefile叫做&#8220;总控Makefile&#8221;，总控Makefile的变量可以传递到下级的Makefile中（如果你显示的声明），但是不会覆盖下层的Makefile中所定义的变量，除非指定了&#8220;-e&#8221;参数。
<p>如果你要传递变量到下级Makefile中，那么你可以使用这样的声明：
<div class=BeautifierPlugin>
<div class=fragment>
<pre style="PADDING-BOTTOM: 0px">
<pre style="PADDING-BOTTOM: 0px">export &lt;variable ...&gt;</pre>
</pre>
</div>
</div>
如果你不想让某些变量传递到下级Makefile中，那么你可以这样声明：
<div class=BeautifierPlugin>
<div class=fragment>
<pre style="PADDING-BOTTOM: 0px">
<pre style="PADDING-BOTTOM: 0px">unexport &lt;variable ...&gt;</pre>
</pre>
</div>
</div>
如：
<pre style="PADDING-BOTTOM: 0px">
示例一：
export variable = value
其等价于：
variable = value
export variable
其等价于：
export variable := value
其等价于：
variable := value
export variable
示例二：
export variable += value
其等价于：
variable += value
export variable
</pre>
如果你要传递所有的变量，那么，只要一个export就行了。后面什么也不用跟，表示传递所有的变量。
<p>需要注意的是，有两个变量，一个是SHELL，一个是MAKEFLAGS，这两个变量不管你是否export，其总是要传递到下层Makefile中，特别是MAKEFILES变量，其中包含了make的参数信息，如果我们执行&#8220;总控Makefile&#8221;时有make参数或是在上层Makefile中定义了这个变量，那么MAKEFILES变量将会是这些参数，并会传递到下层Makefile中，这是一个系统级的环境变量。
<p>但是make命令中的有几个参数并不往下传递，它们是&#8220;-C&#8221;,&#8220;-f&#8221;,&#8220;-h&#8221;&#8220;-o&#8221;和&#8220;-W&#8221;（有关Makefile参数的细节将在后面说明），如果你不想往下层传递参数，那么，你可以这样来： </p>
<pre style="PADDING-BOTTOM: 0px">
subsystem:
cd subdir &amp;&amp; $(MAKE) MAKEFLAGS=
</pre>
如果你定义了环境变量MAKEFLAGS，那么你得确信其中的选项是大家都会用到的，如果其中有&#8220;-t&#8221;,&#8220;-n&#8221;,和&#8220;-q&#8221;参数，那么将会有让你意想不到的结果，或许会让你异常地恐慌。
<p>还有一个在&#8220;嵌套执行&#8221;中比较有用的参数，&#8220;-w&#8221;或是&#8220;--print-directory&#8221;会在make的过程中输出一些信息，让你看到目前的工作目录。比如，如果我们的下级make目录是&#8220;/home/hchen/gnu/make&#8221;，如果我们使用&#8220;make -w&#8221;来执行，那么当进入该目录时，我们会看到： </p>
<pre style="PADDING-BOTTOM: 0px">
make: Entering directory `/home/hchen/gnu/make'.
</pre>
而在完成下层make后离开目录时，我们会看到：
<pre style="PADDING-BOTTOM: 0px">
make: Leaving directory `/home/hchen/gnu/make'
</pre>
当你使用&#8220;-C&#8221;参数来指定make下层Makefile时，&#8220;-w&#8221;会被自动打开的。如果参数中有&#8220;-s&#8221;（&#8220;--slient&#8221;）或是&#8220;--no-print-directory&#8221;，那么，&#8220;-w&#8221;总是失效的。
<h4><a name=saVJ9lkWtLG16></a>4.5 定义命令包 </h4>
<p>如果Makefile中出现一些相同命令序列，那么我们可以为这些相同的命令序列定义一个变量。定义这种命令序列的语法以&#8220;define&#8221;开始，以&#8220;endef&#8221;结束，如： </p>
<pre style="PADDING-BOTTOM: 0px">    define run-yacc
yacc $(firstword $^)
mv y.tab.c $@
endef
</pre>
这里，&#8220;run-yacc&#8221;是这个命令包的名字，其不要和Makefile中的变量重名。在&#8220;define&#8221;和&#8220;endef&#8221;中的两行就是命令序列。这个命令包中的第一个命令是运行Yacc程序，因为Yacc程序总是生成&#8220;y.tab.c&#8221;的文件，所以第二行的命令就是把这个文件改改名字。还是把这个命令包放到一个示例中来看看吧。
<pre style="PADDING-BOTTOM: 0px">    foo.c : foo.y
$(run-yacc)
</pre>
我们可以看见，要使用这个命令包，我们就好像使用变量一样。在这个命令包的使用中，命令包&#8220;run-yacc&#8221;中的&#8220;$^&#8221;就是&#8220;foo.y&#8221;，&#8220;$@&#8221;就是&#8220;foo.c&#8221;（有关这种以&#8220;$&#8221;开头的特殊变量，我们会在后面介绍），make在执行命令包时，命令包中的每个命令会被依次独立执行。 
<img src ="http://www.cppblog.com/dawnbreak/aggbug/86350.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/dawnbreak/" target="_blank">pear_li</a> 2009-06-01 09:35 <a href="http://www.cppblog.com/dawnbreak/articles/86350.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++格式化输出输入 （stream）</title><link>http://www.cppblog.com/dawnbreak/articles/85932.html</link><dc:creator>pear_li</dc:creator><author>pear_li</author><pubDate>Wed, 27 May 2009 09:53:00 GMT</pubDate><guid>http://www.cppblog.com/dawnbreak/articles/85932.html</guid><wfw:comment>http://www.cppblog.com/dawnbreak/comments/85932.html</wfw:comment><comments>http://www.cppblog.com/dawnbreak/articles/85932.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/dawnbreak/comments/commentRss/85932.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/dawnbreak/services/trackbacks/85932.html</trackback:ping><description><![CDATA[<h4><font color=#ff0000>本文摘自C++primer第四版附录A3</font></h4>
<h4>A.3.3. 控制输出格式</h4>
<p>许多操纵符使我们能够改变输出的外观。有两大类的输出控制：控制数值的表示，以及控制填充符的数量和布局。</p>
<h5>控制布尔值和格式</h5>
<p>改变对象格式化状态的操纵符的一个例子是 <tt><font face=NSimsun>boolalpha</font></tt> 操纵符。默认情况下，将 <tt><font face=NSimsun>bool</font></tt> 值显示为 1 或 0，<tt><font face=NSimsun>true</font></tt> 值显示为 1，而 <tt><font face=NSimsun>false</font></tt> 值显示为 0。可以通过流的 <tt><font face=NSimsun>boolalpha</font></tt> 操纵符覆盖这个格式化：</p>
<pre>cout &lt;&lt; "default bool values: "<br>          &lt;&lt; true &lt;&lt; " " &lt;&lt; false<br>          &lt;&lt; "\nalpha bool values: "<br>          &lt;&lt; boolalpha<br>          &lt;&lt; true &lt;&lt; " " &lt;&lt; false<br>          &lt;&lt; endl;</pre>
<p>执行时，这段程序产生下面的输出：</p>
<pre><span>default bool values: 1 0</span><br>     <span>alpha bool values: true false</span></pre>
<p>一旦将 <tt><font face=NSimsun>boolalpha</font></tt> &#8220;写&#8221;至 <tt><font face=NSimsun>cout</font></tt>，从这个点起就改变了 <tt><font face=NSimsun>cout</font></tt> 将怎样显示 <tt><font face=NSimsun>bool</font></tt> 值，后续显示 <tt><font face=NSimsun>bool</font></tt> 值的操作将用 <tt><font face=NSimsun>true</font></tt> 或 <tt><font face=NSimsun>false</font></tt> 进行显示。</p>
<a></a><a></a><a></a><a></a><a></a><a></a><a></a><a></a><a></a><a></a><a></a><a></a><a></a><a></a><a></a>
<p>要取消 <tt><font face=NSimsun>cout</font></tt> 的格式状态改变，必须应用 <tt><font face=NSimsun>noboolalpha</font></tt>：</p>
<pre>bool bool_val;<br>cout &lt;&lt; boolalpha    // <span>sets internal state of</span> <span>cout</span><br>     &lt;&lt; bool_val<br>     &lt;&lt; noboolalpha; // <span>resets internal state to default formatting</span></pre>
<p>现在只改变 <tt><font face=NSimsun>bool</font></tt> 值的格式化来显示 <tt><font face=NSimsun>bool_val</font></tt>，并且立即将流重置为原来的状态。</p>
<h5>指定整型值的基数</h5>
<p>默认情况下，用十进制读写整型值。通过使用操纵符 <tt><font face=NSimsun>hex</font></tt>、<tt><font face=NSimsun>oct</font></tt> 和 <tt><font face=NSimsun>dec</font></tt>，程序员可以将表示进制改为八进制、十六进制或恢复十进制（浮点值的表示不受影响）：</p>
<pre>const int ival = 15, jval = 1024; // <span>const,</span> <span>so values never change</span><br>     cout &lt;&lt; "default: ival = " &lt;&lt; ival<br>          &lt;&lt; " jval = " &lt;&lt; jval &lt;&lt; endl;<br>     cout &lt;&lt; "printed in octal: ival = " &lt;&lt; oct &lt;&lt; ival<br>          &lt;&lt; " jval = " &lt;&lt; jval &lt;&lt; endl;<br>     cout &lt;&lt; "printed in hexadecimal: ival = " &lt;&lt; hex &lt;&lt; ival<br>          &lt;&lt; " jval = " &lt;&lt; jval &lt;&lt; endl;<br>     cout &lt;&lt; "printed in decimal: ival = " &lt;&lt; dec &lt;&lt; ival<br>          &lt;&lt; " jval = " &lt;&lt; jval &lt;&lt; endl;</pre>
<p>编译和执行的时候，程序产生下面的输出：</p>
<pre><span>default: ival = 15 jval = 1024</span><br>     <span>printed in octal: ival = 17 jval = 2000</span><br>     <span>printed in hexadecimal: ival = f jval = 400</span><br>     <span>printed in decimal: ival = 15 jval = 1024</span></pre>
<p>注意，像 <tt><font face=NSimsun>boolalpha</font></tt> 一样，这些操纵符改变格式状态。它们影响紧接在后面的输出，以及所有后续的整型输出，直到通过调用另一操纵符重围格式为止。</p>
<a></a>
<h5>指出输出的基数</h5>
<p>默认情况下，显示数值的时候，不存在关于所用基数的可见记号。例如，20 是 20,还是 16 的八进制表示？按十进制模式显示数值的时候，会按我们期待的格式打印数值。如果需要打印八进制或十六进制值，可能应该也使用 <tt><font face=NSimsun>showbase</font></tt> 操纵符。<tt><font face=NSimsun>showbase</font></tt> 操纵符导致输出流使用的约定，与指定整型常量基数所用的相同：</p>
<ul>
    <li>
    <p>以 0x 为前导表示十六进制。</p>
    <li>
    <p>以 0 为前导表示八进制。</p>
    <li>
    <p>没有任何前导表示十进制。</p>
    </li>
</ul>
<p>修改程序使用 <tt><font face=NSimsun>showbase</font></tt> 如下：</p>
<pre>const int ival = 15, jval = 1024; // <span>const</span> <span>so values never change</span><br>     cout &lt;&lt; showbase; // <span>show base when printing integral values</span><br>     cout &lt;&lt; "default: ival = " &lt;&lt; ival<br>          &lt;&lt; " jval = " &lt;&lt; jval &lt;&lt; endl;<br>     cout &lt;&lt; "printed in octal: ival = " &lt;&lt; oct &lt;&lt; ival<br>          &lt;&lt; " jval = " &lt;&lt; jval &lt;&lt; endl;<br>     cout &lt;&lt; "printed in hexadecimal: ival = " &lt;&lt; hex &lt;&lt; ival<br>          &lt;&lt; " jval = " &lt;&lt; jval &lt;&lt; endl;<br>     cout &lt;&lt; "printed in decimal: ival = " &lt;&lt; dec &lt;&lt; ival<br>          &lt;&lt; " jval = " &lt;&lt; jval &lt;&lt; endl;<br>     cout &lt;&lt; noshowbase; // <span>reset state of the stream</span></pre>
<p>修改后的输出使得基础值到底是什么很清楚：</p>
<pre><span>default: ival = 15 jval = 1024</span><br>     <span>printed in octal: ival = 017 jval = 02000</span><br>     <span>printed in hexadecimal: ival = 0xf jval = 0x400</span><br>     <span>printed in decimal: ival = 15 jval = 1024</span></pre>
<p><tt><font face=NSimsun>noshowbase</font></tt> 操纵符重置 <tt><font face=NSimsun>cout</font></tt>，以便它不再显示整型值的表示基数。</p>
<p>默认情况下，十六进制值用带小写 <tt><font face=NSimsun>x</font></tt> 的小写形式打印。可以应用 <tt><font face=NSimsun>uppercase</font></tt> 操纵符显示 <tt><font face=NSimsun>X</font></tt> 并将十六进制数字 <tt><font face=NSimsun>a</font></tt> - <tt><font face=NSimsun>f</font></tt> 显示为大写字母。</p>
<pre>cout &lt;&lt; uppercase &lt;&lt; showbase &lt;&lt; hex<br>          &lt;&lt; "printed in hexadecimal: ival = " &lt;&lt; ival<br>          &lt;&lt; " jval = " &lt;&lt; jval &lt;&lt; endl<br>          &lt;&lt; nouppercase &lt;&lt; endl;</pre>
<p>前面的程序产生下面的输出：</p>
<pre><span>printed in hexadecimal: ival = 0XF jval = 0X400</span></pre>
<p>要恢复小写，就应用 nouppercase 操纵符。</p>
<h5>控制浮点值的格式</h5>
<p>对于浮点值的格式化，可以控制下面三个方面：</p>
<ul>
    <li>
    <p>精度：显示多少位数字。</p>
    <li>
    <p>记数法：用小数还是科学记法法显示。</p>
    <li>
    <p>对是整数的浮点值的小数点的处理。</p>
    </li>
</ul>
<p>默认情况下，使用六位数字的精度显示浮点值。如果值没有小数部分，则省略小数点。使用小数形式还是科学记数法显示数值取决于被显示的浮点数的值，标准库选择增强数值可读性的格式，非常大和非常小的值使用科学记数法显示，其他值使用小数形式。</p>
<h5>指定显示精度</h5>
<p>默认情况下，精度控制显示的数字总位数。显示的时候，将浮点值四舍五入到当前精度。因此，如果当前精度是 4，则 <tt><font face=NSimsun>3.14159</font></tt> 成为 <tt><font face=NSimsun>3.142</font></tt>；如果精度是 3，打印为 <tt><font face=NSimsun>3.14</font></tt>。</p>
<p>通过名为 <tt><font face=NSimsun>precision</font></tt> 的成员函数，或者通过使用 <tt><font face=NSimsun>setprecision</font></tt> 操纵符，可以改变精度。<tt><font face=NSimsun>precision</font></tt> 成员是重载的（<a href="http://blog.163.com/mk:@MSITStore:G:%20%E6%AD%A3%E4%BA%8B%20C%20Program%20C++%20C++%20Primer%E7%AC%AC%E5%9B%9B%E7%89%88%E4%B8%AD%E8%8B%B1%E6%96%87%E5%AF%B9%E7%85%A7.chm::/0201721481/ch07lev1sec8.html#ch07lev1sec8"><font color=#bf7fa1>第 7.8 节</font></a>）：一个版本接受一个 <tt><font face=NSimsun>int</font></tt> 值并将精度设置为那个新值，它返回<span>先前</span>的精度值；另一个版本不接受实参并返回当前精度值。<tt><font face=NSimsun>setprecision</font></tt> 操纵符接受一个实参，用来设置精度。</p>
<p>下面的程序说明控制显示浮点值所用精度的不同方法：</p>
<pre>// <span>cout.precision</span> <span>reports current precision value</span><br>     cout &lt;&lt; "Precision: " &lt;&lt; cout.precision()<br>          &lt;&lt; ", Value: "   &lt;&lt; sqrt(2.0) &lt;&lt; endl;<br>     // <span>cout.precision(12)</span> <span>asks that 12 digits of precision to be printed</span><br>     cout.precision(12);<br>     cout &lt;&lt; "Precision: " &lt;&lt; cout.precision()<br>          &lt;&lt; ", Value: "   &lt;&lt; sqrt(2.0) &lt;&lt; endl;<br>     // <span>alternative way to set precision using</span> <span>setprecision</span> <span>manipulator</span><br>     cout &lt;&lt; setprecision(3);<br>     cout &lt;&lt; "Precision: " &lt;&lt; cout.precision()<br>          &lt;&lt; ", Value: "   &lt;&lt; sqrt(2.0) &lt;&lt; endl;</pre>
<p>编译并执行后，程序产生下面的输出：</p>
<pre><span>Precision: 6, Value: 1.41421</span><br>     <span>Precision: 12, Value: 1.41421356237</span><br>     <span>Precision: 3, Value: 1.41</span></pre>
<p>这个程序调用标准库中的 <tt><font face=NSimsun>sqrt</font></tt> 函数，可以在头文件 <tt><font face=NSimsun>cmath</font></tt> 中找到它。<tt><font face=NSimsun>sqrt</font></tt> 函数量重载的，可以用 <tt><font face=NSimsun>float</font></tt>、<tt><font face=NSimsun>double</font></tt> 或 <tt><font face=NSimsun>long double</font></tt> 实参调用，它返回实参的平方根。</p>
<a></a>
<div>
<p>
<table cellSpacing=0 cellPadding=1 width="90%" border=0>
    <tbody>
        <tr>
            <td vAlign=top width=60><br></td>
            <td vAlign=top>
            <p>操纵符和其他接受实参的操纵符定义在头文件 <tt><font face=NSimsun>iomanip</font></tt> 中。</p>
            </td>
        </tr>
    </tbody>
</table>
</p>
</div>
<h5>控制记数法</h5>
<p>默认情况下，用于显示浮点值的记数法取决于数的大小：如果数很大或很小，将按科学记数法显示，否则，使用固定位数的小数。标准库选择使得数容易阅读的记数法。</p>
<a></a>
<div>
<p>
<table cellSpacing=0 cellPadding=1 width="90%" border=0>
    <tbody>
        <tr>
            <td vAlign=top width=60><br></td>
            <td vAlign=top>
            <p>将浮点数显示为普通数（相对于显示货币、百分比，那时我们希望控制值的外观）的时候，通常最好让标准库来选择使用的记数法。要强制科学记数法或固定位数小数的一种情况是在显示表的时候，表中的小数点应该对齐。</p>
            </td>
        </tr>
    </tbody>
</table>
</p>
</div>
<p>如果希望强制科学记数法或固定位数小数表示，可以通过使用适当的操纵符做到这一点：<tt><font face=NSimsun>scientific</font></tt> 操纵符将流变为使用科学记数法。像在十六进制值上显示 <tt><font face=NSimsun>x</font></tt> 一样，也可以通过 <tt><font face=NSimsun>uppercase</font></tt> 操纵符控制科学记数法中的 <tt><font face=NSimsun>e</font></tt>。<tt><font face=NSimsun>fixed</font></tt> 操纵符将流为使用固定位数小数表示。</p>
<p>这些操纵符改变流精度的默认含义。执行 <tt><font face=NSimsun>scientific</font></tt> 或 <tt><font face=NSimsun>fixed</font></tt> 之后，精度值控制小数点之后的数位。默认情况下，精度指定数字的总位数——小数点之前和之后。使用 <tt><font face=NSimsun>fixed</font></tt> 或 <tt><font face=NSimsun>scientific</font></tt> 命名我们能够按列对齐来显示数，这一策略保证小数点总是在相对于被显示的小数部分固定的位置。</p>
<h5>恢复浮点值的默认记数法</h5>
<p>与其他操纵符不同，不存在将流恢复为根据被显示值选择记数法的默认状态的操纵符，相反，我们必须调用 <tt><font face=NSimsun>unsetf</font></tt> 成员来取消 <tt><font face=NSimsun>scientific</font></tt> 或 <tt><font face=NSimsun>fixed</font></tt> 所做的改变。要将流恢复为浮点值的默认处理，将名为 <tt><font face=NSimsun>floatfield</font></tt> 的标准库定义值传给 <tt><font face=NSimsun>unsetf</font></tt> 函数：</p>
<pre>// <span>reset to default handling for notation</span><br>     cout.unsetf(ostream::floatfield);</pre>
<p>除了取消它们的效果之外，使用这些操纵符像使用任意其他操纵符一样：</p>
<pre>cout &lt;&lt; sqrt(2.0) &lt;&lt; '\n' &lt;&lt; endl;<br>     cout &lt;&lt; "scientific: " &lt;&lt; scientific &lt;&lt; sqrt(2.0) &lt;&lt; '\n'<br>          &lt;&lt; "fixed decimal: " &lt;&lt; fixed &lt;&lt; sqrt(2.0) &lt;&lt; "\n\n";<br>     cout &lt;&lt; uppercase<br>          &lt;&lt; "scientific: " &lt;&lt; scientific &lt;&lt; sqrt(2.0) &lt;&lt; '\n'<br>          &lt;&lt; "fixed decimal: " &lt;&lt; fixed &lt;&lt; sqrt(2.0) &lt;&lt; endl<br>          &lt;&lt; nouppercase;<br>     // <span>reset to default handling for notation</span><br>     cout.unsetf(ostream::floatfield);<br>     cout &lt;&lt; '\n' &lt;&lt; sqrt(2.0) &lt;&lt; endl;</pre>
<p>产生如下输出：</p>
<pre><span>1.41421</span><br><br>     <span>scientific: 1.414214e+00</span><br>     <span>fixed decimal: 1.414214</span><br><br>     <span>scientific: 1.414214E+00</span><br>     <span>fixed decimal: 1.414214</span><br><br>     <span>1.41421</span></pre>
<h5>显示小数点</h5>
<p>默认情况下，当浮点值的小数部分为 0 的时候，不显示小数点。<tt><font face=NSimsun>showpoint</font></tt> 操纵符强制显示小数点：</p>
<pre>cout &lt;&lt; 10.0 &lt;&lt; endl;        // <span>prints</span> <span>10</span><br>     cout &lt;&lt; showpoint &lt;&lt; 10.0    // <span>prints</span> <span>10.0000</span><br>          &lt;&lt; noshowpoint &lt;&lt; endl; // <span>revert to default handling of decimal point</span></pre>
<p><tt><font face=NSimsun>noshowpoint</font></tt> 操纵符恢复默认行为。下一个输出表达式将具有默认行为，即，如果浮点值小数部分为 0,就取消小数点。</p>
<h5>填充输出</h5>
<p>按栏显示数据的时候，经常很希望很好地控制数据的格式化。标准库提供下面几个操纵帮助我们实现需要的控制：</p>
<ul>
    <li>
    <p><tt><font face=NSimsun>setw</font></tt>，指定下一个数值或字符串的最小间隔。</p>
    <li>
    <p><tt><font face=NSimsun>left</font></tt>，左对齐输出。</p>
    <p><tt><font face=NSimsun>right</font></tt>，右对齐输出。输出默认为右对齐。</p>
    <li>
    <p><tt><font face=NSimsun>internal</font></tt>，控制负值的符号位置。<tt><font face=NSimsun>internal</font></tt> 左对齐符号且右对齐值，用空格填充介于其间的空间。</p>
    <li>
    <p><tt><font face=NSimsun>setfill</font></tt>，使我们能够指定填充输出时使用的另一个字符。默认情况下，值是空格。</p>
    </li>
</ul>
<a></a>
<div>
<p>
<table cellSpacing=0 cellPadding=1 width="90%" border=0>
    <tbody>
        <tr>
            <td vAlign=top width=60><br></td>
            <td vAlign=top>
            <p>像 <tt><font face=NSimsun>endl</font></tt> 一样，<tt><font face=NSimsun>setw</font></tt> 不改变输出流的内部状态，它只决定下一个输出的长度。</p>
            </td>
        </tr>
    </tbody>
</table>
</p>
</div>
<p>下面程序段说明了这些操纵符：</p>
<pre>int i = -16;<br>     double d = 3.14159;<br>     // <span>pad first column to use minimum of 12 positions in the output</span><br>     cout &lt;&lt; "i: " &lt;&lt; setw(12) &lt;&lt; i &lt;&lt; "next col" &lt;&lt; '\n'<br>          &lt;&lt; "d: " &lt;&lt; setw(12) &lt;&lt; d &lt;&lt; "next col" &lt;&lt; '\n';<br>     // <span>pad first column and left-justify all columns</span><br>     cout &lt;&lt; left<br>          &lt;&lt; "i: " &lt;&lt; setw(12) &lt;&lt; i &lt;&lt; "next col" &lt;&lt; '\n'<br>          &lt;&lt; "d: " &lt;&lt; setw(12) &lt;&lt; d &lt;&lt; "next col" &lt;&lt; '\n'<br>          &lt;&lt; right; // <span>restore normal justification</span><br>     // <span>pad first column and right-justify all columns</span><br>     cout &lt;&lt; right<br>          &lt;&lt; "i: " &lt;&lt; setw(12) &lt;&lt; i &lt;&lt; "next col" &lt;&lt; '\n'<br>          &lt;&lt; "d: " &lt;&lt; setw(12) &lt;&lt; d &lt;&lt; "next col" &lt;&lt; '\n';<br>     // <span>pad first column but put the padding internal to the field</span><br>     cout &lt;&lt; internal<br>          &lt;&lt; "i: " &lt;&lt; setw(12) &lt;&lt; i &lt;&lt; "next col" &lt;&lt; '\n'<br>          &lt;&lt; "d: " &lt;&lt; setw(12) &lt;&lt; d &lt;&lt; "next col" &lt;&lt; '\n';<br>     // <span>pad first column, using # as the pad character</span><br>     cout &lt;&lt; setfill('#')<br>          &lt;&lt; "i: " &lt;&lt; setw(12) &lt;&lt; i &lt;&lt; "next col" &lt;&lt; '\n'<br>          &lt;&lt; "d: " &lt;&lt; setw(12) &lt;&lt; d &lt;&lt; "next col" &lt;&lt; '\n'<br>          &lt;&lt; setfill(' '); // <span>restore normal pad character</span></pre>
<p>执行时，该程序段产生如下输出：</p>
<pre><span>i:          -16next col</span><br>     <span>d:      3.14159next col</span><br>     <span>i: -16         next col</span><br>     <span>d: 3.14159     next col</span><br>     <span>i:          -16next col</span><br>     <span>d:      3.14159next col</span><br>     <span>i: -         16next col</span><br>     <span>d:      3.14159next col</span><br>     <span>i: -#########16next col</span><br>     <span>d: #####3.14159next col</span></pre>
<h4>A.3.4. 控制输入格式化</h4>
<p>默认情况下，输入操作符忽略空白（空格、制表符、换行符、进纸和回车）。对下面的循环：</p>
<pre>while (cin &gt;&gt; ch)<br>         cout &lt;&lt; ch;</pre>
<p>给定输入序列</p>
<pre><span>a b   c</span><br>     <span>d</span></pre>
<p>循环执行四次从字符 <tt><font face=NSimsun>a</font></tt> 读到 <tt><font face=NSimsun>d</font></tt>，跳过介于其间的空格、可能的制表符和换行符。该程序段的输出是：</p>
<pre><span>abcd</span></pre>
<p><tt><font face=NSimsun>noskipws</font></tt> 操纵符导致输入操作符读（而不是跳过）空白。要返回默认行为，应用 <tt><font face=NSimsun>skipws</font></tt> 操纵符：</p>
<pre>cin &gt;&gt; noskipws;      // <span>set</span> <span>cin</span> <span>so that it reads whitespace</span><br>     while (cin &gt;&gt; ch)<br>             cout &lt;&lt; ch;<br>     cin &gt;&gt; skipws; // <span>reset</span> <span>cin</span> <span>to default state so that it discards whitespace</span></pre>
<p>给定与前面相同的输入，该循环进行 7 次迭代，读输入中的空白以及字符。该循环产生如下输出：</p>
<pre><span>a b    c</span><br>     <span>d</span></pre>
<h4>A.3.5. 未格式化的输入／输出操作</h4>
<p>迄今为止，示例程序中只使用过格式化的 IO 操作。输入和输出操作符（<tt><font face=NSimsun>&lt;&lt;</font></tt> 和 <tt><font face=NSimsun>&gt;&gt;</font></tt>）根据被处理数据的类型格式化所读写的数据。输入操作符忽略空白，输出操作符应用填充、精度等。</p>
<p>标准库还提供了丰富的支持未格式化 IO 的低级操作，这些操作使我们能够将流作为未解释的字节序列处理，而不是作为数据类型（如 <tt><font face=NSimsun>char</font></tt>、<tt><font face=NSimsun>int</font></tt>、<tt><font face=NSimsun>string</font></tt> 等）的序列处理。</p>
<img src ="http://www.cppblog.com/dawnbreak/aggbug/85932.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/dawnbreak/" target="_blank">pear_li</a> 2009-05-27 17:53 <a href="http://www.cppblog.com/dawnbreak/articles/85932.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>基于事件解析的SAX模型分析</title><link>http://www.cppblog.com/dawnbreak/articles/85822.html</link><dc:creator>pear_li</dc:creator><author>pear_li</author><pubDate>Tue, 26 May 2009 09:26:00 GMT</pubDate><guid>http://www.cppblog.com/dawnbreak/articles/85822.html</guid><wfw:comment>http://www.cppblog.com/dawnbreak/comments/85822.html</wfw:comment><comments>http://www.cppblog.com/dawnbreak/articles/85822.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/dawnbreak/comments/commentRss/85822.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/dawnbreak/services/trackbacks/85822.html</trackback:ping><description><![CDATA[SAX2解析器读XML文档，然后产生基于特殊符号的事件。SAX2解析器实际上并不为该文档在内存中创建一棵树结构，它序列的处理一个文档的内容并产生相关的事件。<br><br>　　比如，当你进行基于事件的编程的时候，你可以创建函数来响应用户定义的事件（比如OnClick事件）。在利用SAX进行编程的时候，需要注意的是，是解析器而不是用户产生事件。<br><br>　　比如考虑下面一个简单的文档。<br><br>　　＜?xml version="1.0"?＞<br>　　＜parts＞<br>　　＜part＞TurboWidget＜/part＞<br>　　＜/parts＞<br><br>　　当SAX2在处理这个文档的时候，它产生如下的一系列的事件：<br><br>　　StartDocument( )<br>　　StartElement( "parts" )<br>　　StartElement( "part" )<br>　　Characters( "TurboWidget" )<br>　　EndElement( "part" )<br>　　EndElement( "parts" )<br>　　EndDocument( )<br><br>　　可以把SAX2看成是一个有拉特点（PUSH）的解析器，SAX2产生事件，然后你可以自己去处理些事件。实际上，当SAX2在解析一个文档的时候，SAXXMLReader读该文档并产生一系列的事件，你可以选择一些事件进行处理。<br><br>　　创建一个应用SAX的应用程序框架<br><br>　　SAX2产生的事件包括如下的种类：<br><br>　　&#168; 和XML文档内容相关的事件(ISAXContentHandler)<br><br>　　&#168; 和DTD相关的事件(ISAXDTDHandler)<br><br>　　&#168; 出现错误时发生的事件(ISAXErrorHandler)<br><br>　　为了处理这些事件，你需要实现一个相关的处理类，该处理类需要包含一些方法来处理相关的事件。你必须对你想要处理的事件实现相关的处理。如果你不想处理某一个事件的话，只需要简单的忽略它就可以。在实际应用中，我们首先要继承这些接口，用C++我们可以创建一个类，在这个类的方法中，我们可以告诉应用程序在接收到一个事件的时候如何进行处理。下面是建立一个基于SAX的应用的基本步骤：<br><br>　　1． 创建头文件当使用SAX2的时候，我们需要用到动态连接库MSXML.DLL,为了使用MSXML中包含的SAX2接口，你必须在程序的头文件（一般在stdafx.h中）中包含下列的代码：<br><br>　　#import <msxml3.dll>raw_interfaces_only <br><br><br>　　using namespace MSXML2;<br><br>　　2． 建立具体的操作（handler）类，SAX2主要定义了三个基本的操作类，它们分别是ISAXContentHandler，ISAXDTDHandler和ISAXErrorHandler。<br><br>　　ISAXContentHandler是用来处理SAX2解析器对文档内容进行解析时所产生的消息的，ISAXXMLReader通过方法putContentHandler来注册这个实例。而ISAXDTDHandler是用来处理和DTD相关的基本的消息的，ISAXXMLReader通过方法putDTDHandler来注册这个实例。ISAXErrorHandler提供了对在解析过程中遇到错误时产生的错误事件的处理，ISAXXMLReader通过方法putErrorHandler来注册这个实例<br><br>　　因为这三个类都是用来对事件进行处理的，并且需要在接口ISAXXMLReader中进行注册。但是它们的基本使用方法类似，所以我们这里只详细描述对接口ISAXContentHandler 的操作。<br><br>　　ISAXContentHandler接口接收关于文档的内容变化的事件，这是实现SAX应用所需要的最重要的接口，如果应用在遇到基本的解析事件的时候需要被通知的话，ISAXXMLReader通过方法putContentHandler来注册这个实例，然后ISAXXMLReader就使用这个实例来报告基于文档的事件，比如元素的开始，元素的结束和相关的字符串数据等等。ISAXContentHandler 包括了很多的方法：比如startDocument，endDocument，startElement，endElement等等。实际上它包含了好接个startXXX和endXXX对来建立不同的信息集合的抽象。比如startDocument方法在文档信息开始的时候被调用，而在startDocument以后被调用的方法就被认为是文档信息项（item）的子项。在文档信息内容结束的时候endDocument就被调用，表示文档信息的结束。 实际上是SAX2在解析文档的时候，当处于文档某一位置的时候，会激发相应的方法，比如当一个文档开始的时候，就会激发startDocument方法，在实际实现的时候，我们可以在我们继承ISAXContentHandler类的实现类中，重载该方法，实现我们自己想要的处理。我们可以把这些方法看成是ISAXContentHandler接口提供给我们的。需要注意的是事件被处理的顺序和信息在文档中的位置是一致的。<br><br>　　同时需要注意的是，如果我们需要在我们的应用中对这些消息进行处理的话，我们就要继承处理这些消息的类，比如我们只需要对文档内容进行处理，而忽略对DTD和解析过程中错误(Error)的处理，那么我们只需要创建一个新的类，该类继承ISAXContentHandler接口，因为ISAXContentHandler中定义了很多的事件处理方法，而事实上我们只需要对我们所关心事件的处理方法进行重载，对我们不关心的事件可以简单的忽略它。<br><br>　　比如我们只关心startElement和endElement事件，而且我们假设我们建立的类的名称为CXMLContentDeal，我们的类就可以如下面所示：<br><br>　　class CXMLContentDeal : public ISAXContentHandler <br>　　　{<br>　　　　public:<br>　　　　　CXMLContentDeal();<br>　　　　　virtual CXMLContentDeal ();<br>　　　　　 virtual HRESULT STDMETHODCALLTYPE startElement( <br>　　　　　　　/* [in] */ wchar_t __RPC_FAR *pwchNamespaceUri,<br>　　　　　　　/* [in] */ int cchNamespaceUri,<br>　　　　　　　/* [in] */ wchar_t __RPC_FAR *pwchLocalName,<br>　　　　　　　/* [in] */ int cchLocalName,<br>　　　　　　　/* [in] */ wchar_t __RPC_FAR *pwchRawName,<br>　　　　　　　/* [in] */ int cchRawName,<br>　　　　　　　/* [in] */ ISAXAttributes __RPC_FAR *pAttributes);<br>　　　　　　virtual HRESULT STDMETHODCALLTYPE endElement( <br>　　　　　　　/* [in] */ wchar_t __RPC_FAR *pwchNamespaceUri,<br>　　　　　　　/* [in] */ int cchNamespaceUri,<br>　　　　　　　/* [in] */ wchar_t __RPC_FAR *pwchLocalName,<br>　　　　　　　/* [in] */ int cchLocalName,<br>　　　　　　　/* [in] */ wchar_t __RPC_FAR *pwchRawName,<br>　　　　　　　/* [in] */ int cchRawName);<br>　　　　}<br><br>　　然后我们可以重载方法startElement和endElement来进行和应用相关的特殊的处理。<br><br>　　3． 通过接口ISAXXMLReader创建一个解析器。XMLReader是SAX应用实现的主要的接口，XMLReader的作用是这样的。 首先，XML的开发人员使用这个接口来注册他们对其他SAX接口的实现（比如ContentHandler,DTDHandler,ErrorHandler等等），另外，XMLREADER通过setFeature和setProperty两个方法来配置SAX解析器的行为，最后，XMLReader封装了解析的功能。示例代码如下：<br><br>　　　ISAXXMLReader* pRdr = NULL;<br>　　　HRESULT hr = CoCreateInstance(<br>　　　　　　__uuidof(SAXXMLReader), <br>　　　　　　NULL, <br>　　　　　　CLSCTX_ALL, <br>　　　　　　__uuidof(ISAXXMLReader), <br>　　　　　　(void **)&amp;pRdr);<br><br>　　4． 创建相应的事件（handler）处理类，这里不妨假设我们只处理和文档内容相关的事件。示例代码如下：<br><br>　　CXMLContentDeal * pMc = new CXMLContentDeal();<br><br>　　注意这里CXMLContentDeal是继承接口ISAXContentHandler的类。<br><br>　　5．在解析器中注册事件处理类，示例代码如下：<br><br>　　　hr = pRdr-&gt;putContentHandler(pMc);<br><br>　　6．开始进行文档的解析，示例代码如下<br><br>　　　hr = pRdr-&gt;parseURL(URL); file://这里的URL是指一个具体XML文档的位置<br><br>　　7．释放解析器对象<br><br>　　　pRdr-&gt;Release();<br><br>　　以上就是基于SAX的应用程序的框架结构，我们可以看到，实际的事件处理是在我们的继承类CXMLContentDeal中实现的，在我们这个示例代码中，每当文档中一个新的元素开始的时候，都会激活方法startElement，每当一个元素结束的时候，都会激活方法endElement。我们可以在startElement和endElement中写入和应用相关的特定的代码。 
<img src ="http://www.cppblog.com/dawnbreak/aggbug/85822.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/dawnbreak/" target="_blank">pear_li</a> 2009-05-26 17:26 <a href="http://www.cppblog.com/dawnbreak/articles/85822.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>STL中的排序算法一览[By ACM郭老师] </title><link>http://www.cppblog.com/dawnbreak/articles/85685.html</link><dc:creator>pear_li</dc:creator><author>pear_li</author><pubDate>Mon, 25 May 2009 03:51:00 GMT</pubDate><guid>http://www.cppblog.com/dawnbreak/articles/85685.html</guid><wfw:comment>http://www.cppblog.com/dawnbreak/comments/85685.html</wfw:comment><comments>http://www.cppblog.com/dawnbreak/articles/85685.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/dawnbreak/comments/commentRss/85685.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/dawnbreak/services/trackbacks/85685.html</trackback:ping><description><![CDATA[这篇文章我很喜欢，是郭老师的新作！希望大家喜欢！<br>详细的从算法的效率方面来说明了排序算法！<br><a name=entrymore></a><br>STL中有多种排序算法，各有各的适用范围，下面听我一一道来：<br><br><span style="COLOR: #ff0000">I、完全排序</span><br>sort() <br>首先要隆重推出的当然是最最常用的sort了，sort有两种形式，第一种形式有两个迭代器参数，构成一个前开后闭的区间，按照元素的 less 关系排序；第二种形式多加一个指定排序准则的谓词。sort基本是最通用的排序函数，它使用快速排序算法，并且在递归过程中，当元素数目小于一个阈值（一般是16，我的试验是24）时，转成直接插入排序。伟大的数学家Knuth已经证明，在平均意义上，快速排序是最快的了；当然，最坏复杂性比较差。sort要求随机迭代器，因此对于很多编译器来说，对于前向迭代器（如list）使用sort是一个编译错误。(不过，在vc2005里面，这个错误信息实在很糟糕)<br><br>sort的基本使用方式如下：<br><br>
<div class=dp-highlighter>
<div class=bar>
<div class=tools><a onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;" href="http://www.doserver.net/#"><font color=#808080><u>view plain</u></font></a><a onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" href="http://www.doserver.net/#"><font color=#808080><u>copy to clipboard</u></font></a><a onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;" href="http://www.doserver.net/#"><font color=#808080><u>print</u></font></a><a onclick="dp.sh.Toolbar.Command('About',this);return false;" href="http://www.doserver.net/#"><font color=#808080><u>?</u></font></a></div>
</div>
<ol class=dp-cpp>
    <li class=alt><span><span>C++: &nbsp;&nbsp;</span></span>
    <li class=""><span>&nbsp;&nbsp;</span>
    <li class=alt><span></span><span class=preprocessor><strong><font color=#cd00cd>#include&nbsp;&lt;vector&gt; </font></strong></span><span>&nbsp;&nbsp;</span></span>
    <li class=""><span></span><span class=preprocessor><strong><font color=#cd00cd>#include&nbsp;&lt;algorithm&gt; </font></strong></span><span>&nbsp;&nbsp;</span></span>
    <li class=alt><span></span><span class=preprocessor><strong><font color=#cd00cd>#include&nbsp;&lt;functional&gt; </font></strong></span><span>&nbsp;&nbsp;</span></span>
    <li class=""><span></span><span class=preprocessor><strong><font color=#cd00cd>#include&nbsp;&lt;cstdlib&gt; </font></strong></span><span>&nbsp;&nbsp;</span></span>
    <li class=alt><span>&nbsp; &nbsp;&nbsp;</span>
    <li class=""><span></span><span class=keyword><strong><font color=#5697d9>using</font></strong></span><span>&nbsp;</span><span class=keyword><strong><font color=#5697d9>namespace</font></strong></span><span>&nbsp;std; &nbsp;&nbsp;</span></span>
    <li class=alt><span>&nbsp; &nbsp;&nbsp;</span>
    <li class=""><span></span><span class=keyword><strong><font color=#5697d9>void</font></strong></span><span>&nbsp;func1() &nbsp;&nbsp;</span></span>
    <li class=alt><span>{ &nbsp;&nbsp;</span>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;vector&lt;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&gt;&nbsp;ar; &nbsp;&nbsp;</span></span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=comment><font color=#ee0000>//向数组里面插入一些随机数 </font></span><span>&nbsp;&nbsp;</span></span>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;generate_n(back_inserter(ar),&nbsp;100,&nbsp;rand); &nbsp;&nbsp;</span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=comment><font color=#ee0000>//按从小到大排序 </font></span><span>&nbsp;&nbsp;</span></span>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;sort(ar.begin(),&nbsp;ar.end()); &nbsp;&nbsp;</span>
    <li class=alt><span>}&nbsp; &nbsp;&nbsp;</span> </li>
</ol>
</div>
<textarea class=c style="DISPLAY: none" name=code rows=15 cols=100>C++:
#include &lt;vector&gt;
#include &lt;algorithm&gt;
#include &lt;functional&gt;
#include &lt;cstdlib&gt;
using namespace std;
void func1()
{
&nbsp; &nbsp;vector&lt;int&gt; ar;
&nbsp; &nbsp;//向数组里面插入一些随机数
&nbsp; &nbsp;generate_n(back_inserter(ar), 100, rand);
&nbsp; &nbsp;//按从小到大排序
&nbsp; &nbsp;sort(ar.begin(), ar.end());
}
</textarea><br>经常有人问如何从大到小逆排序，这个其实有很多中方式实现，如下面的例子：<br>
<div class=dp-highlighter>
<div class=bar>
<div class=tools><a onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;" href="http://www.doserver.net/#"><font color=#808080><u>view plain</u></font></a><a onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" href="http://www.doserver.net/#"><font color=#808080><u>copy to clipboard</u></font></a><a onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;" href="http://www.doserver.net/#"><font color=#808080><u>print</u></font></a><a onclick="dp.sh.Toolbar.Command('About',this);return false;" href="http://www.doserver.net/#"><font color=#808080><u>?</u></font></a></div>
</div>
<ol class=dp-cpp>
    <li class=alt><span><span>C++: &nbsp;&nbsp;</span></span>
    <li class=""><span>&nbsp;&nbsp;</span>
    <li class=alt><span></span><span class=keyword><strong><font color=#5697d9>void</font></strong></span><span>&nbsp;func2() &nbsp;&nbsp;</span></span>
    <li class=""><span>{ &nbsp;&nbsp;</span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;vector&lt;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&gt;&nbsp;ar; &nbsp;&nbsp;</span></span>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=comment><font color=#ee0000>//向数组里面插入一些随机数 </font></span><span>&nbsp;&nbsp;</span></span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;generate_n(back_inserter(ar),&nbsp;100,&nbsp;rand); &nbsp;&nbsp;</span>
    <li class=""><span>&nbsp; &nbsp;&nbsp;</span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=comment><font color=#ee0000>//方法1：使用函数作为谓词 </font></span><span>&nbsp;&nbsp;</span></span>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;sort(ar.begin(),&nbsp;ar.end(),&nbsp;GreateThan); &nbsp;&nbsp;</span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=comment><font color=#ee0000>//方法2：使用仿函数作为谓词 </font></span><span>&nbsp;&nbsp;</span></span>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=comment><font color=#ee0000>//注意下面两种方法都需要有个括号，实际上是要产生一个临时对象 </font></span><span>&nbsp;&nbsp;</span></span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;sort(ar.begin(),&nbsp;ar.end(),&nbsp;CompareInt()); &nbsp;&nbsp;</span>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=comment><font color=#ee0000>//方法3：使用预定义的Adapter,&nbsp;定义在&nbsp;&lt;functional&gt;&nbsp;中 </font></span><span>&nbsp;&nbsp;</span></span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;sort(ar.begin(),&nbsp;ar.end(),&nbsp;greater&lt;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&gt;()); &nbsp;&nbsp;</span></span>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=comment><font color=#ee0000>//方法4：正常排序，然后翻转过来 </font></span><span>&nbsp;&nbsp;</span></span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;sort(ar.begin(),&nbsp;ar.end()); &nbsp;&nbsp;</span>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;reverse(ar.begin(),&nbsp;ar.end()); &nbsp;&nbsp;</span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=comment><font color=#ee0000>//方法5：使用逆迭代器 </font></span><span>&nbsp;&nbsp;</span></span>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;sort(ar.rbegin(),&nbsp;ar.rend()); &nbsp;&nbsp;</span>
    <li class=alt><span>}&nbsp; &nbsp;&nbsp;</span>
    <li class=""><span>&nbsp;&nbsp;</span> </li>
</ol>
</div>
<textarea class=c style="DISPLAY: none" name=code rows=15 cols=100>C++:
void func2()
{
&nbsp; &nbsp;vector&lt;int&gt; ar;
&nbsp; &nbsp;//向数组里面插入一些随机数
&nbsp; &nbsp;generate_n(back_inserter(ar), 100, rand);
&nbsp; &nbsp;//方法1：使用函数作为谓词
&nbsp; &nbsp;sort(ar.begin(), ar.end(), GreateThan);
&nbsp; &nbsp;//方法2：使用仿函数作为谓词
&nbsp; &nbsp;//注意下面两种方法都需要有个括号，实际上是要产生一个临时对象
&nbsp; &nbsp;sort(ar.begin(), ar.end(), CompareInt());
&nbsp; &nbsp;//方法3：使用预定义的Adapter, 定义在 &lt;functional&gt; 中
&nbsp; &nbsp;sort(ar.begin(), ar.end(), greater&lt;int&gt;());
&nbsp; &nbsp;//方法4：正常排序，然后翻转过来
&nbsp; &nbsp;sort(ar.begin(), ar.end());
&nbsp; &nbsp;reverse(ar.begin(), ar.end());
&nbsp; &nbsp;//方法5：使用逆迭代器
&nbsp; &nbsp;sort(ar.rbegin(), ar.rend());
}
</textarea><br>最后一种方法是我比较欣赏的，可以不能直接对原生数组使用，也就是说，如果ar的定义是int ar[MAXN]，上面其他的排序算法都可以简单的改成sort(ar, ar+MAXN, ...），但最后一个不行，要用另外一种比较丑陋的方式：<br><br>
<div class=dp-highlighter>
<div class=bar>
<div class=tools><a onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;" href="http://www.doserver.net/#"><font color=#808080><u>view plain</u></font></a><a onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" href="http://www.doserver.net/#"><font color=#808080><u>copy to clipboard</u></font></a><a onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;" href="http://www.doserver.net/#"><font color=#808080><u>print</u></font></a><a onclick="dp.sh.Toolbar.Command('About',this);return false;" href="http://www.doserver.net/#"><font color=#808080><u>?</u></font></a></div>
</div>
<ol class=dp-cpp>
    <li class=alt><span><span>C++: &nbsp;&nbsp;</span></span>
    <li class=""><span>&nbsp;&nbsp;</span>
    <li class=alt><span></span><span class=preprocessor><strong><font color=#cd00cd>#include&nbsp;&lt;iterator&gt; </font></strong></span><span>&nbsp;&nbsp;</span></span>
    <li class=""><span></span><span class=keyword><strong><font color=#5697d9>void</font></strong></span><span>&nbsp;func3(){ &nbsp;&nbsp;</span></span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;ax[5]={1,3,4,5,2}; &nbsp;&nbsp;</span></span>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;sort(reverse_iterator&lt;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>*&gt;(ax+5),&nbsp;reverse_iterator&lt;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>*&gt;(ax+0)); &nbsp;&nbsp;</span></span>
    <li class=alt><span>}&nbsp; &nbsp;&nbsp;</span> </li>
</ol>
</div>
<textarea class=c style="DISPLAY: none" name=code rows=15 cols=100>C++:
#include &lt;iterator&gt;
void func3(){
&nbsp; &nbsp;int ax[5]={1,3,4,5,2};
&nbsp; &nbsp;sort(reverse_iterator&lt;int*&gt;(ax+5), reverse_iterator&lt;int*&gt;(ax+0));
}
</textarea><br>stable_sort <br>sort优点一大堆，一个缺点就是它不是一种稳定的排序。什么是排序的稳定性，就是如果出现两个元素相等时，要求排序之后他们之间保持原来的次序（比如我们先按学号排序，然后按成绩排序，这时就希望成绩相同的还是按照学号的次序排）。很可惜，快速排序算法就不是稳定的，要追求这个，只好用stable_sort了。<br><br>在各种排序算法中，合并排序是稳定的，但一般的合并排序需要额外的O(N)的存储空间，而这个条件不是一定能够满足的（可能是比较奢侈的）。所以在stable_sort内部，首先判断是否有足够的额外空间（如vecotr中的cap-size()部分），有的话就使用普通合并函数，总的时间复杂性和快速排序一个数量级，都是O(N*logN)。如果没有额外空间，使用了一个merge_without_buffer的关键函数进行就地合并（如何实现是比较有技巧的，完全可以专门谈一谈），这个合并过程不需要额外的存储空间，但时间复杂度变成O(N*logN)，这种情况下，总的stable_sort时间复杂度是O(N*logN*logN)。<br><br>总之，stable_sort稍微慢一点儿，但能够保证稳定，使用方法和sort一样。但很多时候可以不用这种方式和这个函数，比如上面的例子，完全可以在排序比较准则中写入成绩和学号两个条件就OK了<br>
<div class=dp-highlighter>
<div class=bar>
<div class=tools><a onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;" href="http://www.doserver.net/#"><font color=#808080><u>view plain</u></font></a><a onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" href="http://www.doserver.net/#"><font color=#808080><u>copy to clipboard</u></font></a><a onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;" href="http://www.doserver.net/#"><font color=#808080><u>print</u></font></a><a onclick="dp.sh.Toolbar.Command('About',this);return false;" href="http://www.doserver.net/#"><font color=#808080><u>?</u></font></a></div>
</div>
<ol class=dp-cpp>
    <li class=alt><span><span>C++: &nbsp;&nbsp;</span></span>
    <li class=""><span>&nbsp;&nbsp;</span>
    <li class=alt><span></span><span class=keyword><strong><font color=#5697d9>class</font></strong></span><span>&nbsp;CStudent &nbsp;&nbsp;</span></span>
    <li class=""><span>{ &nbsp;&nbsp;</span>
    <li class=alt><span></span><span class=keyword><strong><font color=#5697d9>public</font></strong></span><span>: &nbsp;&nbsp;</span></span>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;CStudent(); &nbsp;&nbsp;</span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=comment><font color=#ee0000>//注意这个比较函数中的const </font></span><span>&nbsp;&nbsp;</span></span>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>bool</font></strong></span><span>&nbsp;operator&lt;(</span><span class=keyword><strong><font color=#5697d9>const</font></strong></span><span>&nbsp;CStudent&amp;&nbsp;rhs)&nbsp;</span><span class=keyword><strong><font color=#5697d9>const</font></strong></span><span>&nbsp;&nbsp;</span></span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;</span>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=keyword><strong><font color=#5697d9>if</font></strong></span><span>&nbsp;(m_score&nbsp;!=&nbsp;rhs.m_score) &nbsp;&nbsp;</span></span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=keyword><strong><font color=#5697d9>return</font></strong></span><span>&nbsp;(m_score&nbsp;&lt;rhs.m_score); &nbsp;&nbsp;</span></span>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=keyword><strong><font color=#5697d9>return</font></strong></span><span>&nbsp;m_name&nbsp;&lt;rhs.m_name; &nbsp;&nbsp;</span></span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span>
    <li class=""><span></span><span class=keyword><strong><font color=#5697d9>protected</font></strong></span><span>: &nbsp;&nbsp;</span></span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;std::string&nbsp;m_name; &nbsp;&nbsp;</span>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;m_score; &nbsp;&nbsp;</span></span>
    <li class=alt><span>}; &nbsp;&nbsp;</span>
    <li class=""><span>&nbsp; &nbsp;&nbsp;</span>
    <li class=alt><span></span><span class=keyword><strong><font color=#5697d9>void</font></strong></span><span>&nbsp;func4() &nbsp;&nbsp;</span></span>
    <li class=""><span>{ &nbsp;&nbsp;</span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;vector&lt;CStudent&gt;&nbsp;arStu; &nbsp;&nbsp;</span>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;sort(arStu.begin(),&nbsp;arStu.end()); &nbsp;&nbsp;</span>
    <li class=alt><span>}&nbsp; &nbsp;&nbsp;</span> </li>
</ol>
</div>
<textarea class=c style="DISPLAY: none" name=code rows=15 cols=100>C++:
class CStudent
{
public:
&nbsp; &nbsp;CStudent();
&nbsp; &nbsp;//注意这个比较函数中的const
&nbsp; &nbsp;bool operator&lt;(const CStudent&amp; rhs) const
&nbsp; &nbsp;{
&nbsp; &nbsp; &nbsp; &nbsp;if (m_score != rhs.m_score)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;return (m_score &lt;rhs.m_score);
&nbsp; &nbsp; &nbsp; &nbsp;return m_name &lt;rhs.m_name;
&nbsp; &nbsp;}
protected:
&nbsp; &nbsp;std::string m_name;
&nbsp; &nbsp;int m_score;
};
void func4()
{
&nbsp; &nbsp;vector&lt;CStudent&gt; arStu;
&nbsp; &nbsp;sort(arStu.begin(), arStu.end());
}
</textarea><br>sort_heap <br>堆排序也是一种快速的排序算法，复杂度也是O(N*logN)。STL中有一些和堆相关的函数，能够构造堆，如果在构造好的堆上每次取出来根节点放在尾部，所有元素循环一遍，最后的结果也就有序了。这就是sort_heap了。它的使用要求区间前面已经构造成堆，如：<br>
<div class=dp-highlighter>
<div class=bar>
<div class=tools><a onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;" href="http://www.doserver.net/#"><font color=#808080><u>view plain</u></font></a><a onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" href="http://www.doserver.net/#"><font color=#808080><u>copy to clipboard</u></font></a><a onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;" href="http://www.doserver.net/#"><font color=#808080><u>print</u></font></a><a onclick="dp.sh.Toolbar.Command('About',this);return false;" href="http://www.doserver.net/#"><font color=#808080><u>?</u></font></a></div>
</div>
<ol class=dp-cpp>
    <li class=alt><span><span>C++: &nbsp;&nbsp;</span></span>
    <li class=""><span>&nbsp;&nbsp;</span>
    <li class=alt><span></span><span class=keyword><strong><font color=#5697d9>void</font></strong></span><span>&nbsp;func5() &nbsp;&nbsp;</span></span>
    <li class=""><span>{ &nbsp;&nbsp;</span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;vector&lt;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&gt;&nbsp;ar; &nbsp;&nbsp;</span></span>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;generate_n(back_inserter(ar),&nbsp;100,&nbsp;rand); &nbsp;&nbsp;</span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;make_heap(ar.begin(),&nbsp;ar.end()); &nbsp;&nbsp;</span>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;sort_heap(ar.begin(),&nbsp;ar.end()); &nbsp;&nbsp;</span>
    <li class=alt><span>}&nbsp; &nbsp;&nbsp;</span> </li>
</ol>
</div>
<textarea class=c style="DISPLAY: none" name=code rows=15 cols=100>C++:
void func5()
{
&nbsp; &nbsp;vector&lt;int&gt; ar;
&nbsp; &nbsp;generate_n(back_inserter(ar), 100, rand);
&nbsp; &nbsp;make_heap(ar.begin(), ar.end());
&nbsp; &nbsp;sort_heap(ar.begin(), ar.end());
}
</textarea><br>list.sort <br>对于list容器，是不能直接使用sort的（包括stable_sort），从技术的角度来说是由于sort要求随机迭代器；从算法的角度来说，list这种链表结构就不适合用快速排序。因此，list容器内部实现了专门的sort算法，这个算法采用的是合并排序，应该是稳定的（不确定）。<br><br>其他 <br>优先队列（priority_queue）每次弹出的都是max值。实际上就是heap的一个容器方式的包装。 <br>关联式容器自身就必须是有序的（针对key），对其迭代时，key是递增的。 <br><span style="COLOR: #ff0000">II、部分排序</span><br>这些部分排序功能能够完成一段数据（而不是所有）的排序，在适当的适合使用可以节省计算量。不过用的人不多。<br><br>partial_sort(), partial_sort_copy() <br>这两个函数能够将整个区间中给定数目的元素进行排序，也就是说，结果中只有最小的M个元素是有序的。你当然也可以使用sort，区别就在于效率。如果M显著地小于N，时间就比较短；当然M太小了也不好，那还不如挨个找最小值了。<br><br>partial_sort接受三个参数，分别是区间的头，中间和结尾。执行后，将前面M（M=中间－头）个元素有序地放在前面，后面的元素肯定是比前面的大，但他们内部的次序没有保证。partial_sort_copy的区别在于把结果放到另外指定的迭代器区间中： <br>
<div class=dp-highlighter>
<div class=bar>
<div class=tools><a onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;" href="http://www.doserver.net/#"><font color=#808080><u>view plain</u></font></a><a onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" href="http://www.doserver.net/#"><font color=#808080><u>copy to clipboard</u></font></a><a onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;" href="http://www.doserver.net/#"><font color=#808080><u>print</u></font></a><a onclick="dp.sh.Toolbar.Command('About',this);return false;" href="http://www.doserver.net/#"><font color=#808080><u>?</u></font></a></div>
</div>
<ol class=dp-cpp>
    <li class=alt><span><span>C++: &nbsp;&nbsp;</span></span>
    <li class=""><span>&nbsp;&nbsp;</span>
    <li class=alt><span></span><span class=keyword><strong><font color=#5697d9>void</font></strong></span><span>&nbsp;func6() &nbsp;&nbsp;</span></span>
    <li class=""><span>{ &nbsp;&nbsp;</span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;ar[12]={69,23,80,42,17,15,26,51,19,12,35,8}; &nbsp;&nbsp;</span></span>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=comment><font color=#ee0000>//只排序前7个数据 </font></span><span>&nbsp;&nbsp;</span></span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;partial_sort(ar,&nbsp;ar+7,&nbsp;ar+12); &nbsp;&nbsp;</span>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=comment><font color=#ee0000>//结果是&nbsp;8&nbsp;12&nbsp;15&nbsp;17&nbsp;19&nbsp;23&nbsp;26&nbsp;80&nbsp;69&nbsp;51&nbsp;42&nbsp;35，后5个数据不定 </font></span><span>&nbsp;&nbsp;</span></span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;vector&lt;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&gt;&nbsp;res(7); &nbsp;&nbsp;</span></span>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=comment><font color=#ee0000>//前7项排序后放入res </font></span><span>&nbsp;&nbsp;</span></span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;partial_sort_copy(ar,&nbsp;ar+7,&nbsp;res.begin(),&nbsp;res.end(),&nbsp;greater&lt;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&gt;()&nbsp;); &nbsp;&nbsp;</span></span>
    <li class=""><span>}&nbsp; &nbsp;&nbsp;</span> </li>
</ol>
</div>
<textarea class=c style="DISPLAY: none" name=code rows=15 cols=100>C++:
void func6()
{
&nbsp; &nbsp;int ar[12]={69,23,80,42,17,15,26,51,19,12,35,8};
&nbsp; &nbsp;//只排序前7个数据
&nbsp; &nbsp;partial_sort(ar, ar+7, ar+12);
&nbsp; &nbsp;//结果是 8 12 15 17 19 23 26 80 69 51 42 35，后5个数据不定
&nbsp; &nbsp;vector&lt;int&gt; res(7);
&nbsp; &nbsp;//前7项排序后放入res
&nbsp; &nbsp;partial_sort_copy(ar, ar+7, res.begin(), res.end(), greater&lt;int&gt;() );
}
</textarea><br>这两个函数的实现使用的是堆的方法，先将前M个元素构造成堆，然后挨个检查后面的元素，看看是否小于堆的最大值，是的话就彼此交换，然后重排堆；最后将前面已经是最小的M个元素构成的堆作一次sort_heap就可以了。算法的复杂度差不多是O(N*logM)<br><br>nth_element <br>这个函数只真正排序出一个元素来，就是第n个。函数有三个迭代器的输入（当然还可以加上一个谓词），执行完毕后，中间位置指向的元素保证和完全排序后这个位置的元素一致，前面区间的元素都小于（精确地说，是不大于）后面区间的元素。<br><br>熟悉快速排序的马上就能发现，这实际上是一个按位置划分的算法。STL的规范中要求此函数的平均复杂度是线性的，和快速排序一样，这种算法的最坏复杂度比较差。在一般的实现（如SGI）中，采用三种取1的方法寻找划分元素，最坏复杂度是O(N^N)。虽然理论上有一些算法可以保证最坏线性复杂度，但算法过于复杂，STL一般也不采用。 <br><br><span style="COLOR: #ff0000">III、排序辅助功能</span><br>partition, stable_partition <br>merge, inplace_merge <br><span style="COLOR: #ff0000">IV、有序区间操作</span><br><br>这个准备单独写一篇<br><br>
<div class=quote>
<div class=quote-title>引用</div>
<div class=quote-content><br>让我们继续期待郭老师的补充！<br><br>郭老师的boke地址 www.skywind.name/blog<br></div>
</div>
<img src ="http://www.cppblog.com/dawnbreak/aggbug/85685.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/dawnbreak/" target="_blank">pear_li</a> 2009-05-25 11:51 <a href="http://www.cppblog.com/dawnbreak/articles/85685.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>模板类的定义和实现要放在同一文件中</title><link>http://www.cppblog.com/dawnbreak/articles/81704.html</link><dc:creator>pear_li</dc:creator><author>pear_li</author><pubDate>Sat, 02 May 2009 08:50:00 GMT</pubDate><guid>http://www.cppblog.com/dawnbreak/articles/81704.html</guid><wfw:comment>http://www.cppblog.com/dawnbreak/comments/81704.html</wfw:comment><comments>http://www.cppblog.com/dawnbreak/articles/81704.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/dawnbreak/comments/commentRss/81704.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/dawnbreak/services/trackbacks/81704.html</trackback:ping><description><![CDATA[<p><font color=#000000>今天写程序，将模板内的定义放在了.h文件中，而实现放在了.cpp中，</font></p>
<p><font color=#000000>编译通过了，可连接怎么也不能成功。。</font></p>
<p><font color=#000000>查了一些书才知道，模板类的定义和实现必须放在同一文件，</font></p>
<p><font color=#000000>《c++编程思想》中说：模板类定义很特殊，由template&lt;...&gt;定义的任何东西都意味着编译器在当时不为它分配内存空间，它一直处于等待状态，直到被一个模板实例告知，即模板参数是由编译器来替换的。&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 为了容易使用，几乎总是在头文件中放置全部的模板声明和定义。有时，也可能为了满足特殊需要而要在独立的cpp中放置模板的实现。但大部分现在的编译器还不支持模板类的定义和实现分开</font></p>
<img src ="http://www.cppblog.com/dawnbreak/aggbug/81704.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/dawnbreak/" target="_blank">pear_li</a> 2009-05-02 16:50 <a href="http://www.cppblog.com/dawnbreak/articles/81704.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>常量函数、常量引用参数、常量引用返回值[C++] </title><link>http://www.cppblog.com/dawnbreak/articles/80791.html</link><dc:creator>pear_li</dc:creator><author>pear_li</author><pubDate>Wed, 22 Apr 2009 15:09:00 GMT</pubDate><guid>http://www.cppblog.com/dawnbreak/articles/80791.html</guid><wfw:comment>http://www.cppblog.com/dawnbreak/comments/80791.html</wfw:comment><comments>http://www.cppblog.com/dawnbreak/articles/80791.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/dawnbreak/comments/commentRss/80791.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/dawnbreak/services/trackbacks/80791.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 1. 关于常量引用正像在C语言中使用指针一样，C++中通常使用引用 有一个函数... foo()并且这个函数返回一个引用...... &amp; foo()....,&nbsp;一个指向位图(Bitmap)的引用 ...Bitmap &amp; foo()....&nbsp;并且这个位图(bitmap)是常量const Bitmap &amp; foo ()当然你也可以用指针来做同样的事情:con...&nbsp;&nbsp;<a href='http://www.cppblog.com/dawnbreak/articles/80791.html'>阅读全文</a><img src ="http://www.cppblog.com/dawnbreak/aggbug/80791.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/dawnbreak/" target="_blank">pear_li</a> 2009-04-22 23:09 <a href="http://www.cppblog.com/dawnbreak/articles/80791.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>最快的Hash表 </title><link>http://www.cppblog.com/dawnbreak/articles/80536.html</link><dc:creator>pear_li</dc:creator><author>pear_li</author><pubDate>Mon, 20 Apr 2009 06:25:00 GMT</pubDate><guid>http://www.cppblog.com/dawnbreak/articles/80536.html</guid><wfw:comment>http://www.cppblog.com/dawnbreak/comments/80536.html</wfw:comment><comments>http://www.cppblog.com/dawnbreak/articles/80536.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/dawnbreak/comments/commentRss/80536.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/dawnbreak/services/trackbacks/80536.html</trackback:ping><description><![CDATA[<p style="TEXT-INDENT: 21pt"><font color=#000000><span style="FONT-SIZE: 10.5pt; COLOR: navy"><font face=宋体>一个简单的问题：有一个庞大的字符串数组，然后给你一个单独的字符串，让你从这个数组中查找是否有这个字符串并找到它，你会怎么做？有一个方法最简单，老老 实实从头查到尾，一个一个比较，直到找到为止，我想只要学过程序设计的人都能把这样一个程序作出来，但要是有程序员把这样的程序交给用户，我只能用无语来 评价，或许它真的能工作，但</font></span><span style="FONT-SIZE: 10.5pt; COLOR: navy">...</span></font><font face=宋体 color=#000000><span style="FONT-SIZE: 10.5pt; COLOR: navy">也只能如此了。</span></font></p>
<font color=#000000><span style="FONT-SIZE: 10.5pt; COLOR: navy"><font face=宋体>最合适的算法自然是使用</font></span><span style="FONT-SIZE: 10.5pt; COLOR: navy">HashTable</span><span style="FONT-SIZE: 10.5pt; COLOR: navy"><font face=宋体>（哈希表），先介绍介绍其中的基本知识，所谓</font></span><span style="FONT-SIZE: 10.5pt; COLOR: navy">Hash</span><span style="FONT-SIZE: 10.5pt; COLOR: navy"><font face=宋体>，一般是一个整数，通过某种算法，可以把一个字符串</font></span><span style="FONT-SIZE: 10.5pt; COLOR: navy">"</span><span style="FONT-SIZE: 10.5pt; COLOR: navy"><font face=宋体>压缩</font></span><span style="FONT-SIZE: 10.5pt; COLOR: navy">" </span><span style="FONT-SIZE: 10.5pt; COLOR: navy"><font face=宋体>成一个整数。当然，无论如何，一个</font></span><span style="FONT-SIZE: 10.5pt; COLOR: navy">32</span><span style="FONT-SIZE: 10.5pt; COLOR: navy"><font face=宋体>位整数是无法对应回一个字符串的，但在程序中，两个字符串计算出的</font></span><span style="FONT-SIZE: 10.5pt; COLOR: navy">Hash</span><span style="FONT-SIZE: 10.5pt; COLOR: navy"><font face=宋体>值相等的可能非常小，下面看看在</font></span><span style="FONT-SIZE: 10.5pt; COLOR: navy">MPQ</span><span style="FONT-SIZE: 10.5pt; COLOR: navy"><font face=宋体>中的</font></span><span style="FONT-SIZE: 10.5pt; COLOR: navy">Hash</span></font><font face=宋体><span style="FONT-SIZE: 10.5pt; COLOR: navy"><font color=#000000>算法：</font><br><br></span></font>
<p><font size=2><font face=宋体><span style="FONT-SIZE: 9pt; COLOR: navy">以下的</span><span style="FONT-SIZE: 9pt; COLOR: navy">函数</span><span style="FONT-SIZE: 9pt; COLOR: navy">生成一个长度为</span></font><span style="FONT-SIZE: 9pt; COLOR: navy">0x500</span><span style="FONT-SIZE: 9pt; COLOR: navy"><font face=宋体>（合</font></span><span style="FONT-SIZE: 9pt; COLOR: navy">10</span><span style="FONT-SIZE: 9pt; COLOR: navy"><font face=宋体>进制数：</font></span><span style="FONT-SIZE: 9pt; COLOR: navy">1280</span><span style="FONT-SIZE: 9pt; COLOR: navy"><font face=宋体>）的</font></span><span style="FONT-SIZE: 9pt; COLOR: navy">cryptTable[0x500]</span></font></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt"><strong><span style="COLOR: navy"><font size=2>void prepareCryptTable()</font></span></strong></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt"><span style="COLOR: navy"><font size=2>{ </font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt"><span style="COLOR: navy"><font size=2><span>&nbsp;&nbsp;&nbsp; </span>unsigned long seed = 0x00100001, index1 = 0, index2 = 0, i;</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt">&nbsp;</p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt"><span style="COLOR: navy"><font size=2><span>&nbsp;&nbsp;&nbsp; </span>for( index1 = 0; index1 &lt; 0x100; index1++ )</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt"><span style="COLOR: navy"><font size=2><span>&nbsp;&nbsp;&nbsp; </span>{ </font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt"><span style="COLOR: navy"><font size=2><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>for( index2 = index1, i = 0; i &lt; 5; i++, index2 += 0x100 )</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt"><span style="COLOR: navy"><font size=2><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>{ </font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt"><span style="COLOR: navy"><font size=2><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>unsigned long temp1, temp2;</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt">&nbsp;</p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt"><span style="COLOR: navy"><font size=2><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>seed = (seed * 125 + 3) % 0x2AAAAB;</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt"><span style="COLOR: navy"><font size=2><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>temp1 = (seed &amp; 0xFFFF) &lt;&lt; 0x10;</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt">&nbsp;</p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt"><span style="COLOR: navy"><font size=2><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>seed = (seed * 125 + 3) % 0x2AAAAB;</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt"><span style="COLOR: navy"><font size=2><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>temp2 = (seed &amp; 0xFFFF);</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt">&nbsp;</p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt"><font size=2><span style="COLOR: navy"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><strong><span style="COLOR: blue">cryptTable[index2]</span></strong><span style="COLOR: navy"> = ( temp1 | temp2 ); </span></font></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt"><span style="COLOR: navy"><font size=2><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>} </font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt"><span style="COLOR: navy"><font size=2><span>&nbsp;&nbsp; </span>} </font></span></p>
<span style="COLOR: navy"><font size=2>}<br></font></span>
<p style="MARGIN: 0cm 0cm 0pt"><font size=2><span style="FONT-SIZE: 9pt; COLOR: navy">以下函数计算</span><span style="FONT-SIZE: 9pt; COLOR: navy">lpszFileName </span><span style="FONT-SIZE: 9pt; COLOR: navy">字符串的</span><span style="FONT-SIZE: 9pt; COLOR: navy">hash</span><span style="FONT-SIZE: 9pt; COLOR: navy">值，其中</span><span style="FONT-SIZE: 9pt; COLOR: navy">dwHashType </span><span style="FONT-SIZE: 9pt; COLOR: navy">为</span><span style="FONT-SIZE: 9pt; COLOR: navy">hash</span><span style="FONT-SIZE: 9pt; COLOR: navy">的类型，在下面</span><span style="FONT-SIZE: 9pt; COLOR: navy">GetHashTablePos</span><span style="FONT-SIZE: 9pt; COLOR: navy">函数里面调用本函数，其可以取的值为</span><span style="FONT-SIZE: 9pt; COLOR: navy">0</span><span style="FONT-SIZE: 9pt; COLOR: navy">、</span><span style="FONT-SIZE: 9pt; COLOR: navy">1</span><span style="FONT-SIZE: 9pt; COLOR: navy">、</span><span style="FONT-SIZE: 9pt; COLOR: navy">2</span><span style="FONT-SIZE: 9pt; COLOR: navy">；该函数返回</span><span style="FONT-SIZE: 9pt; COLOR: navy">lpszFileName </span><span style="FONT-SIZE: 9pt; COLOR: navy">字符串的</span><span style="FONT-SIZE: 9pt; COLOR: navy">hash</span><span style="FONT-SIZE: 9pt; COLOR: navy">值；</span></font></p>
<p style="MARGIN: 0cm 0cm 0pt">&nbsp;</p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt"><strong><span style="COLOR: navy"><font size=2>unsigned long HashString( char *lpszFileName, unsigned long dwHashType )</font></span></strong></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt"><span style="COLOR: navy"><font size=2>{ </font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt"><span style="COLOR: navy"><font size=2><span>&nbsp;&nbsp;&nbsp; </span>unsigned char *key<span> </span>= (unsigned char *)lpszFileName;</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 21pt; LINE-HEIGHT: 12pt"><span style="COLOR: navy"><font size=2>unsigned long seed1 = 0x7FED7FED;</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 21pt; LINE-HEIGHT: 12pt"><span style="COLOR: navy"><font size=2>unsigned long seed2 = 0xEEEEEEEE;</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt"><span style="COLOR: navy"><font size=2><span>&nbsp;&nbsp;&nbsp; </span>int ch;</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt">&nbsp;</p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt"><span style="COLOR: navy"><font size=2><span>&nbsp;&nbsp;&nbsp; </span>while( *key != 0 )</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt"><span style="COLOR: navy"><font size=2><span>&nbsp;&nbsp;&nbsp; </span>{ </font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt"><span style="COLOR: navy"><font size=2><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ch = toupper(*key++);</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt">&nbsp;</p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt"><font size=2><span style="COLOR: navy"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>seed1 = </span><strong><span style="COLOR: blue">cryptTable[(dwHashType &lt;&lt; 8) + ch]</span></strong><span style="COLOR: navy"> ^ (seed1 + seed2);</span></font></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt"><span style="COLOR: navy"><font size=2><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>seed2 = ch + seed1 + seed2 + (seed2 &lt;&lt; 5) + 3; </font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt"><span style="COLOR: navy"><font size=2><span>&nbsp;&nbsp;&nbsp; </span>}</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt"><span style="COLOR: navy"><font size=2><span>&nbsp;&nbsp;&nbsp; </span>return seed1; </font></span></p>
<span style="COLOR: navy"><font size=2>}<br><br></font></span><font size=3><span style="COLOR: navy">Blizzard</span><span style="COLOR: navy">的这个算法是非常高效的，被称为</span><span style="COLOR: navy">"One-Way Hash"( A one-way hash is a an algorithm that is constructed in such a way that deriving the original string (set of strings, actually) is virtually impossible)</span><span style="COLOR: navy">。举个例子，字符串</span><span style="COLOR: navy">"unitneutralacritter.grp"</span><span style="COLOR: navy">通过这个算法得到的结果是</span><span style="COLOR: navy">0xA<st1:chmetcnv tcsc="0" numbertype="1" negative="False" hasspace="False" sourcevalue="26067" unitname="F">26067F</st1:chmetcnv>3</span><span style="COLOR: navy">。</span></font><span style="COLOR: navy"><br></span><font size=3><span style="COLOR: navy">　　</span><span style="COLOR: navy">是不是把第一个算法改进一下，改成逐个比较字符串的</span><span style="COLOR: navy">Hash</span><span style="COLOR: navy">值就可以了呢，答案是，远远不够，要想得到最快的算法，就不能进行逐个的比较，通常是构造一个哈希表</span><span style="COLOR: navy">(Hash Table)</span><span style="COLOR: navy">来解决问题，哈希表是一个大数组，这个数组的容量根据程序的要求来定义，例如</span><span style="COLOR: navy">1024</span><span style="COLOR: navy">，每一个</span><span style="COLOR: navy">Hash</span><span style="COLOR: navy">值通过取模运算</span><span style="COLOR: navy"> (mod) </span><span style="COLOR: navy">对应到数组中的一个位置，这样，只要比较这个字符串的哈希值对应的位置又没有被占用，就可以得到最后的结果了，想想这是什么速度？是的，是最快的</span><span style="COLOR: navy">O(1)</span><span style="COLOR: navy">，现在仔细看看这个算法吧：<br></span></font>
<p style="MARGIN: 0cm 0cm 0pt 5.4pt" align=left><span style="FONT-SIZE: 9pt; COLOR: navy"><font size=2>typedef struct</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt 5.4pt" align=left><span style="FONT-SIZE: 9pt; COLOR: navy"><font size=2>{</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt 5.4pt" align=left><span style="FONT-SIZE: 9pt; COLOR: navy"><font size=2><span>&nbsp;&nbsp;&nbsp; </span>int nHashA;</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt 5.4pt" align=left><span style="FONT-SIZE: 9pt; COLOR: navy"><font size=2><span>&nbsp;&nbsp;&nbsp; </span>int nHashB;</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt 5.4pt" align=left><span style="FONT-SIZE: 9pt; COLOR: navy"><font size=2><span>&nbsp;&nbsp;&nbsp; </span>char bExists;</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt 5.35pt; TEXT-INDENT: 4.5pt" align=left><span style="FONT-SIZE: 9pt; COLOR: navy"><font size=2><span>&nbsp;&nbsp; </span>......</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt 5.4pt" align=left><span style="FONT-SIZE: 9pt; COLOR: navy"><font size=2>} <strong>SOMESTRUCTRUE</strong>;</font></span></p>
<font size=2><span style="FONT-SIZE: 9pt; COLOR: rgb(204,153,255)">一种可能的结构体定义？<br><br></span></font>
<p style="MARGIN: 0cm 0cm 0pt"><font size=2><span style="FONT-SIZE: 9pt; COLOR: navy">lpszString </span><span style="FONT-SIZE: 9pt; COLOR: navy">为要在</span><span style="FONT-SIZE: 9pt; COLOR: navy">hash</span><span style="FONT-SIZE: 9pt; COLOR: navy">表中查找的字符串；</span><span style="FONT-SIZE: 9pt; COLOR: navy">lpTable </span><span style="FONT-SIZE: 9pt; COLOR: navy">为存储字符串</span><span style="FONT-SIZE: 9pt; COLOR: navy">hash</span><span style="FONT-SIZE: 9pt; COLOR: navy">值的</span><span style="FONT-SIZE: 9pt; COLOR: navy">hash</span><span style="FONT-SIZE: 9pt; COLOR: navy">表</span></font></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt">&nbsp;</p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt"><strong><span style="COLOR: navy"><font size=2>int GetHashTablePos( har *lpszString, SOMESTRUCTURE *lpTable )</font></span></strong></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt"><span style="COLOR: navy"><font size=2>{ </font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt"><span style="COLOR: navy"><font size=2><span>&nbsp;&nbsp;&nbsp; </span>int nHash = HashString(lpszString);</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt"><span style="COLOR: navy"><font size=2><span>&nbsp;&nbsp;&nbsp; </span>int nHashPos = nHash % nTableSize;</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt">&nbsp;</p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt"><span style="COLOR: navy"><font size=2><span>&nbsp;&nbsp;&nbsp; </span>if ( lpTable[nHashPos].bExists &amp;&amp; !strcmp( lpTable[nHashPos].pString, lpszString ) ) </font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt"><span style="COLOR: navy"><font size=2><span>&nbsp;&nbsp;&nbsp; </span>{</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt"><span style="COLOR: navy"><font size=2><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>return nHashPos; </font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt"><span style="COLOR: navy"><font size=2><span>&nbsp;&nbsp;&nbsp; </span>} </font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt"><span style="COLOR: navy"><font size=2><span>&nbsp;&nbsp;&nbsp; </span>else</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt"><span style="COLOR: navy"><font size=2><span>&nbsp;&nbsp;&nbsp; </span>{</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt"><span style="COLOR: navy"><font size=2><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>return -1;<span> </span></font></span></p>
<span style="COLOR: navy"><font size=2><span>&nbsp;&nbsp;&nbsp; </span>}<br><br></font></span>
<p style="MARGIN: 7.5pt 0cm; TEXT-INDENT: 21pt; TEXT-ALIGN: left" align=left><font size=3><span style="COLOR: navy">看到此，我想大家都在想一个很严重的问题：&#8220;如果两个字符串在哈希表中对应的位置相同怎么办？&#8221;</span><span style="COLOR: navy">,</span><span style="COLOR: navy">毕竟一个数组容量是有限的，这种可能性很大。解决该问题的方法很多，我首先想到的就是用&#8220;链表&#8221;</span><span style="COLOR: navy">,</span><span style="COLOR: navy">感谢大学里学的数据结构教会了这个百试百灵的法宝，我遇到的很多算法都可以转化成链表来解决，只要在哈希表的每个入口挂一个链表，保存所有对应的字符串就</span><span style="COLOR: navy">OK</span><span style="COLOR: navy">了。事情到此似乎有了完美的结局，如果是把问题独自交给我解决，此时我可能就要开始定义数据结构然后写代码了。然而</span><span style="COLOR: navy">Blizzard</span><span style="COLOR: navy">的程序员使用的方法则是更精妙的方法。基本原理就是：他们在哈希表中不是用一个哈希值而是用三个哈希值来校验字符串。</span></font></p>
<p style="MARGIN: 7.5pt 0cm; TEXT-INDENT: 21pt; TEXT-ALIGN: left" align=left><font size=3><span style="COLOR: navy">MPQ</span><span style="COLOR: navy">使用文件名哈希表来跟踪内部的所有文件。但是这个表的格式与正常的哈希表有一些不同。首先，它没有使用哈希作为下标，把实际的文件名存储在表中用于验证，实际上它根本就没有存储文件名。而是使用了</span><span style="COLOR: navy">3</span><span style="COLOR: navy">种不同的哈希：一个用于哈希表的下标，两个用于验证。这两个验证哈希替代了实际文件名。</span></font></p>
<font size=3><span style="COLOR: navy">当然了，这样仍然会出现</span><span style="COLOR: navy">2</span><span style="COLOR: navy">个不同的文件名哈希到</span><span style="COLOR: navy">3</span><span style="COLOR: navy">个同样的哈希。但是这种情况发生的概率平均是</span><span style="COLOR: navy">1:18889465931478580854784</span><span style="COLOR: navy">，这个概率对于任何人来说应该都是足够小的</span><span style="COLOR: navy">。现在再回到数据结构上，</span><span style="COLOR: navy">Blizzard</span><span style="COLOR: navy">使用的哈希表没有使用链表，而采用</span><span style="COLOR: navy">"</span><span style="COLOR: navy">顺延</span><span style="COLOR: navy">"</span><span style="COLOR: navy">的方式来解决问题，看看这个算法：<br></span></font>
<p style="MARGIN: 0cm 0cm 0pt" align=left><font size=2><span style="FONT-SIZE: 9pt; COLOR: navy">lpszString </span><span style="FONT-SIZE: 9pt; COLOR: navy">为要在</span><span style="FONT-SIZE: 9pt; COLOR: navy">hash</span><span style="FONT-SIZE: 9pt; COLOR: navy">表中查找的字符串；</span><span style="FONT-SIZE: 9pt; COLOR: navy">lpTable </span><span style="FONT-SIZE: 9pt; COLOR: navy">为存储字符串</span><span style="FONT-SIZE: 9pt; COLOR: navy">hash</span><span style="FONT-SIZE: 9pt; COLOR: navy">值的</span><span style="FONT-SIZE: 9pt; COLOR: navy">hash</span><span style="FONT-SIZE: 9pt; COLOR: navy">表；</span><span style="FONT-SIZE: 9pt; COLOR: navy">nTableSize </span><span style="FONT-SIZE: 9pt; COLOR: navy">为</span><span style="FONT-SIZE: 9pt; COLOR: navy">hash</span><span style="FONT-SIZE: 9pt; COLOR: navy">表的长度；</span></font></p>
<p style="MARGIN: 0cm 0cm 0pt" align=left>&nbsp;</p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt" align=left><strong><span style="COLOR: navy"><font size=2>int GetHashTablePos( char *lpszString, MPQHASHTABLE *lpTable, int nTableSize )</font></span></strong></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt" align=left><span style="COLOR: navy"><font size=2>{</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt" align=left><span style="COLOR: navy"><font size=2>&nbsp;&nbsp;&nbsp; const int<span> </span>HASH_OFFSET = 0, HASH_A = 1, HASH_B = 2;</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt" align=left>&nbsp;</p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt" align=left><span style="COLOR: navy"><font size=2>&nbsp;&nbsp;&nbsp; int<span> </span>nHash = HashString( lpszString, HASH_OFFSET );</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt" align=left><span style="COLOR: navy"><font size=2>&nbsp;&nbsp;&nbsp; int<span> </span>nHashA = HashString( lpszString, HASH_A );</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt" align=left><span style="COLOR: navy"><font size=2>&nbsp;&nbsp;&nbsp; int<span> </span>nHashB = HashString( lpszString, HASH_B );</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt" align=left><span style="COLOR: navy"><font size=2>&nbsp;&nbsp;&nbsp; int<span> </span>nHashStart = nHash % nTableSize;</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt" align=left><span style="COLOR: navy"><font size=2>&nbsp;&nbsp;&nbsp; int<span> </span>nHashPos = nHashStart;</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt" align=left>&nbsp;</p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt" align=left><span style="COLOR: navy"><font size=2>&nbsp;&nbsp;&nbsp; while ( lpTable[nHashPos].bExists )</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 21pt; LINE-HEIGHT: 12pt" align=left><span style="COLOR: navy"><font size=2>{</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 21pt; LINE-HEIGHT: 12pt" align=left><font size=2><font color=#800080><span style="FONT-SIZE: 9pt; COLOR: rgb(204,153,255)">/*</span><span style="FONT-SIZE: 9pt; COLOR: rgb(204,153,255)">如果仅仅是判断在该表中时候存在这个字符串，就比较这两个</span><span style="FONT-SIZE: 9pt; COLOR: rgb(204,153,255)">hash</span><span style="FONT-SIZE: 9pt; COLOR: rgb(204,153,255)">值就可以了，不用对</span></font></font></p>
<p style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 21pt; LINE-HEIGHT: 12pt" align=left><font size=2><font color=#800080><span style="FONT-SIZE: 9pt; COLOR: rgb(204,153,255)">*</span><span style="FONT-SIZE: 9pt; COLOR: rgb(204,153,255)">结构体中的字符串进行比较。这样会加快运行的速度？减少</span><span style="FONT-SIZE: 9pt; COLOR: rgb(204,153,255)">hash</span><span style="FONT-SIZE: 9pt; COLOR: rgb(204,153,255)">表占用的空间？这种</span></font></font></p>
<p style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 45pt; LINE-HEIGHT: 12pt" align=left><font size=2><font color=#800080><span style="FONT-SIZE: 9pt; COLOR: rgb(204,153,255)">*</span><span style="FONT-SIZE: 9pt; COLOR: rgb(204,153,255)">方法一般应用在什么场合？</span><span style="FONT-SIZE: 9pt; COLOR: rgb(204,153,255)">*/</span></font></font></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt" align=left><font size=2><span style="COLOR: navy">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ( </span><span style="COLOR: navy">　</span><span style="COLOR: navy"> <span>lpTable[nHashPos].nHashA == nHashA</span></span></font></p>
<p style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 52.5pt; LINE-HEIGHT: 12pt" align=left><span style="COLOR: navy"><font size=2>&amp;&amp;<span> </span>lpTable[nHashPos].nHashB == nHashB )</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 42pt; LINE-HEIGHT: 12pt" align=left><span style="COLOR: navy"><font size=2>{</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 52.5pt; LINE-HEIGHT: 12pt" align=left><span style="COLOR: navy"><font size=2>return nHashPos;</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 42pt; LINE-HEIGHT: 12pt" align=left><span style="COLOR: navy"><font size=2>}</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt" align=left><span style="COLOR: navy"><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 42pt; LINE-HEIGHT: 12pt" align=left><span style="COLOR: navy"><font size=2>{</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt" align=left><span style="COLOR: navy"><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nHashPos = (nHashPos + 1) % nTableSize;</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 42pt; LINE-HEIGHT: 12pt" align=left><span style="COLOR: navy"><font size=2>}</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 5.25pt; LINE-HEIGHT: 12pt" align=left>&nbsp;</p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt" align=left><span style="COLOR: navy"><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (nHashPos == nHashStart)</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt" align=left><span style="COLOR: navy"><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 5.25pt; LINE-HEIGHT: 12pt" align=left><span style="COLOR: navy"><font size=2>&nbsp;&nbsp;&nbsp; }</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 12pt" align=left><span style="COLOR: navy"><font size=2>&nbsp;&nbsp;&nbsp;&nbsp; return -1;</font></span></p>
<span style="COLOR: navy"><font size=2>}<br><br></font></span><span style="COLOR: navy"><span><font size=3>1.</font><span style="FONT-WEIGHT: normal; FONT-SIZE: 7pt; LINE-HEIGHT: normal; FONT-STYLE: normal; FONT-VARIANT: normal; font-size-adjust: none; font-stretch: normal">&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span style="COLOR: navy"><font size=3>计算出字符串的三个哈希值（一个用来确定位置，另外两个用来校验</font></span><font size=3><span style="COLOR: navy">)<br>2. </span><span style="COLOR: navy">察看哈希表中的这个位置</span></font><span style="COLOR: navy"><br><font size=3>3. </font></span><span style="COLOR: navy"><font size=3>哈希表中这个位置为空吗？如果为空，则肯定该字符串不存在，返回</font></span><span style="COLOR: navy"><br><font size=3>4. </font></span><span style="COLOR: navy"><font size=3>如果存在，则检查其他两个哈希值是否也匹配，如果匹配，则表示找到了该字符串，返回</font></span><span style="COLOR: navy"><br><font size=3>5. </font></span><span style="COLOR: navy"><font size=3>移到下一个位置，如果已经移到了表的末尾，则反绕到表的开始位置起继续查询　</font></span><span style="COLOR: navy"><br><font size=3>6. </font></span><span style="COLOR: navy"><font size=3>看看是不是又回到了原来的位置，如果是，则返回没找到</font></span><span style="COLOR: navy"><br><font size=3>7. </font></span><font size=3><span style="COLOR: navy">回到</span><span style="COLOR: navy">3<br><br></span></font><strong><span style="FONT-SIZE: 14pt; COLOR: rgb(51,51,153)">补充</span></strong><strong><span style="FONT-SIZE: 14pt; COLOR: rgb(51,51,153)">1</span></strong><strong><span style="FONT-SIZE: 14pt; COLOR: rgb(51,51,153)">：其他比较简单一些的</span></strong><strong><span style="FONT-SIZE: 14pt; COLOR: rgb(51,51,153)">hash</span></strong><strong><span style="FONT-SIZE: 14pt; COLOR: rgb(51,51,153)">函数：<br><br></span></strong>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 13pt"><font size=2><span style="COLOR: rgb(51,51,153)">/*key</span><span style="COLOR: rgb(51,51,153)">为一个字符串，</span><span style="COLOR: rgb(51,51,153)">nTableLength</span><span style="COLOR: rgb(51,51,153)">为哈希表的长度</span></font></p>
<p style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 5.25pt; LINE-HEIGHT: 13pt"><font size=2><span style="COLOR: rgb(51,51,153)">*</span><span style="COLOR: rgb(51,51,153)">该函数得到的</span><span style="COLOR: rgb(51,51,153)">hash</span><span style="COLOR: rgb(51,51,153)">值分布比较均匀</span><span style="COLOR: rgb(51,51,153)">*/</span></font></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 13pt"><span style="COLOR: rgb(51,51,153)"><font size=2>unsigned long getHashIndex( const char *key, int nTableLength )</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 13pt"><span style="COLOR: rgb(51,51,153)"><font size=2>{</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 13pt"><span style="COLOR: rgb(51,51,153)"><font size=2><span>&nbsp;&nbsp;&nbsp; </span>unsigned long nHash = 0;</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 13pt"><span style="COLOR: rgb(51,51,153)"><font size=2><span>&nbsp;&nbsp; </span></font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 13pt"><span style="COLOR: rgb(51,51,153)"><font size=2><span>&nbsp;&nbsp;&nbsp; </span>while (*key)</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 13pt"><span style="COLOR: rgb(51,51,153)"><font size=2><span>&nbsp;&nbsp;&nbsp; </span>{</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 13pt"><span style="COLOR: rgb(51,51,153)"><font size=2><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>nHash = (nHash&lt;&lt;5) + nHash + *key++;</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 13pt"><span style="COLOR: rgb(51,51,153)"><font size=2><span>&nbsp;&nbsp;&nbsp; </span>}</font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 13pt"><span style="COLOR: rgb(51,51,153)"><font size=2><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></font></span></p>
<p style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 13pt"><span style="COLOR: rgb(51,51,153)"><font size=2><span>&nbsp;&nbsp;&nbsp; </span>return ( nHash % nTableLength );</font></span></p>
<span style="COLOR: rgb(51,51,153)"><font size=3><font size=2>}<br><br></font></font></span>
<p style="MARGIN: 0cm 0cm 0pt"><strong><span style="FONT-SIZE: 14pt; COLOR: rgb(51,51,153)">补充</span></strong><strong><span style="FONT-SIZE: 14pt; COLOR: rgb(51,51,153)">2</span></strong><strong><span style="FONT-SIZE: 14pt; COLOR: rgb(51,51,153)">：</span></strong></p>
<span style="FONT-SIZE: 10.5pt; COLOR: rgb(51,51,153)"><font face=宋体>哈 希表的数组是定长的，如果太大，则浪费，如果太小，体现不出效率。合适的数组大小是哈希表的性能的关键。哈希表的尺寸最好是一个质数。当然，根据不同的数 据量，会有不同的哈希表的大小。对于数据量时多时少的应用，最好的设计是使用动态可变尺寸的哈希表，那么如果你发现哈希表尺寸太小了，比如其中的元素是哈 希表尺寸的</font></span><span style="FONT-SIZE: 10.5pt; COLOR: rgb(51,51,153)">2</span><font face=宋体><span style="FONT-SIZE: 10.5pt; COLOR: rgb(51,51,153)">倍时，我们就需要扩大哈希表尺寸，一般是扩大一倍。下面是哈希表尺寸大小的可能取值：<br></span></font>
<p style="MARGIN-LEFT: 5.4pt; LINE-HEIGHT: 14pt"><strong><span style="COLOR: rgb(51,51,153)"><font size=3>17, <span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>37, <span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>79, <span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>163, <span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>331, </font></span></strong></p>
<p style="MARGIN-LEFT: 5.4pt; LINE-HEIGHT: 14pt"><strong><span style="COLOR: rgb(51,51,153)"><font size=3>673, <span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>1361, <span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>2729, <span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>471, <span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>10949, <span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></font></span></strong></p>
<p style="MARGIN-LEFT: 5.4pt; LINE-HEIGHT: 14pt"><strong><span style="COLOR: rgb(51,51,153)"><font size=3>21911, <span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>43853, <span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>87719, <span>&nbsp;&nbsp;&nbsp;&nbsp;</span>175447,<span> </span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>350899,</font></span></strong></p>
<p style="MARGIN-LEFT: 5.4pt; LINE-HEIGHT: 14pt"><strong><span style="COLOR: rgb(51,51,153)"><font size=3>701819,<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>1403641,&nbsp;&nbsp;&nbsp;&nbsp; 2807303, <span>&nbsp;&nbsp;</span>5614657, <span>&nbsp;&nbsp;</span>11229331, </font></span></strong></p>
<p style="MARGIN-LEFT: 5.4pt; LINE-HEIGHT: 14pt"><strong><span style="COLOR: rgb(51,51,153)"><font size=3>22458671,<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>44917381,&nbsp;&nbsp;&nbsp;&nbsp; 89834777, 179669557, 359339171, </font></span></strong></p>
<strong><span style="COLOR: rgb(51,51,153)"><font size=3>718678369, <span>&nbsp;&nbsp;&nbsp;&nbsp; </span>1437356741, 2147483647<br><br>以下为该程序的源代码，在linux下测试通过：<br><br><br><br>#include &lt;stdio.h&gt;<br><br><br>/*crytTable[]里面保存的是HashString函数里面将会用到的一些数据，在prepareCryptTable<br>*函数里面初始化*/<br>unsigned long cryptTable[0x500];<br><br><br>/***********************************************************<br>*以下的函数生成一个长度为0x500（合10进制数：1280）的cryptTable[0x500]<br>*<br>*<br>***********************************************************/<br>void prepareCryptTable()<br>{<br>&nbsp;&nbsp;&nbsp; unsigned long seed = 0x00100001, index1 = 0, index2 = 0, i;<br><br>&nbsp;&nbsp;&nbsp; for( index1 = 0; index1 &lt; 0x100; index1++ )<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for( index2 = index1, i = 0; i &lt; 5; i++, index2 += 0x100 )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned long temp1, temp2;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; seed = (seed * 125 + 3) % 0x2AAAAB;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; temp1 = (seed &amp; 0xFFFF) &lt;&lt; 0x10;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; seed = (seed * 125 + 3) % 0x2AAAAB;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; temp2 = (seed &amp; 0xFFFF);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cryptTable[index2] = ( temp1 | temp2 );<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp; }<br>}<br><br><br>/***********************************************************<br>*以下函数计算lpszFileName 字符串的hash值，其中dwHashType 为hash的类型，<br>*在下面GetHashTablePos函数里面调用本函数，其可以取的值为0、1、2；该函数<br>*返回lpszFileName 字符串的hash值；<br>***********************************************************/<br>unsigned long HashString( char *lpszFileName, unsigned long dwHashType )<br>{<br>&nbsp;&nbsp;&nbsp; unsigned char *key = (unsigned char *)lpszFileName;<br>unsigned long seed1 = 0x7FED7FED;<br>unsigned long seed2 = 0xEEEEEEEE;<br>&nbsp;&nbsp;&nbsp; int ch;<br><br>&nbsp;&nbsp;&nbsp; while( *key != 0 )<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ch = toupper(*key++);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; seed1 = cryptTable[(dwHashType &lt;&lt; 8) + ch] ^ (seed1 + seed2);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; seed2 = ch + seed1 + seed2 + (seed2 &lt;&lt; 5) + 3;<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; return seed1;<br>}<br><br>/***********************************************************<br>*在main中测试argv[1]的三个hash值：<br>* ./hash "arr\units.dat"<br>* ./hash "unit\neutral\acritter.grp"<br>***********************************************************/<br>int main( int argc, char **argv )<br>{<br>&nbsp;&nbsp;&nbsp; unsigned long ulHashValue;<br>&nbsp;&nbsp;&nbsp; int i = 0;<br><br>&nbsp;&nbsp;&nbsp; if ( argc != 2 )<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("please input two arguments\n");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return -1;<br>&nbsp;&nbsp;&nbsp; }<br><br>&nbsp;&nbsp;&nbsp;&nbsp; /*初始化数组：crytTable[0x500]*/<br>&nbsp;&nbsp;&nbsp;&nbsp; prepareCryptTable();<br><br>&nbsp;&nbsp;&nbsp;&nbsp; /*打印数组crytTable[0x500]里面的值*/<br>&nbsp;&nbsp;&nbsp;&nbsp; for ( ; i &lt; 0x500; i++ )<br>&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ( i % 10 == 0 )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("\n");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("%-12X", cryptTable[i] );<br>&nbsp;&nbsp;&nbsp;&nbsp; }<br><br>&nbsp;&nbsp;&nbsp;&nbsp; ulHashValue = HashString( argv[1], 0 );<br>&nbsp;&nbsp;&nbsp;&nbsp; printf("\n----%X ----\n", ulHashValue );<br><br>&nbsp;&nbsp;&nbsp;&nbsp; ulHashValue = HashString( argv[1], 1 );<br>&nbsp;&nbsp;&nbsp;&nbsp; printf("----%X ----\n", ulHashValue );<br><br>&nbsp;&nbsp;&nbsp;&nbsp; ulHashValue = HashString( argv[1], 2 );<br>&nbsp;&nbsp;&nbsp;&nbsp; printf("----%X ----\n", ulHashValue );<br><br>&nbsp;&nbsp;&nbsp;&nbsp; return 0;<br>}</font></span></strong>
<div>&nbsp;</div>
<img src ="http://www.cppblog.com/dawnbreak/aggbug/80536.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/dawnbreak/" target="_blank">pear_li</a> 2009-04-20 14:25 <a href="http://www.cppblog.com/dawnbreak/articles/80536.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C/C++中const用法总结</title><link>http://www.cppblog.com/dawnbreak/articles/79254.html</link><dc:creator>pear_li</dc:creator><author>pear_li</author><pubDate>Wed, 08 Apr 2009 05:17:00 GMT</pubDate><guid>http://www.cppblog.com/dawnbreak/articles/79254.html</guid><wfw:comment>http://www.cppblog.com/dawnbreak/comments/79254.html</wfw:comment><comments>http://www.cppblog.com/dawnbreak/articles/79254.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/dawnbreak/comments/commentRss/79254.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/dawnbreak/services/trackbacks/79254.html</trackback:ping><description><![CDATA[<span lang=EN-US><font face="Times New Roman"></font>&nbsp;
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; FONT-FAMILY: 宋体; TEXT-ALIGN: left" align=left><font size=4><span style="FONT-SIZE: 12pt; COLOR: black">一、</span><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black">C</span><span style="FONT-SIZE: 12pt; COLOR: black">中</span><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black">const</span><span style="FONT-SIZE: 12pt; COLOR: black">的用法总结起来主要分为以下两种</span><span style="FONT-SIZE: 12pt; COLOR: black">：</span><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black"><o:p></o:p></span></font></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt 21pt; TEXT-INDENT: -21pt; FONT-FAMILY: 宋体; TEXT-ALIGN: left" align=left><font size=4><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black"><span><span style="FONT-WEIGHT: normal; FONT-SIZE: 7pt; LINE-HEIGHT: normal; FONT-STYLE: normal; FONT-VARIANT: normal; font-size-adjust: none; font-stretch: normal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span style="FONT-SIZE: 12pt; COLOR: black">在定义变量时使用（注意：在定义变量时使用</span><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black">const</span><span style="FONT-SIZE: 12pt; COLOR: black">，一定要进行初始化操作）：</span><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black"><o:p></o:p></span></font></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt 42pt; FONT-FAMILY: 宋体; TEXT-ALIGN: left" align=left><font size=4><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black">&nbsp;</span><span style="FONT-SIZE: 12pt; COLOR: black">最简单的用法，说明变量为一个常变量</span><span style="FONT-SIZE: 12pt; COLOR: black">：</span><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black"><o:p></o:p></span></font></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt 42pt; FONT-FAMILY: 宋体; TEXT-ALIGN: left" align=left><font size=4><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black">&nbsp;const int a=100; </span><span lang=EN-US style="FONT-SIZE: 10pt; COLOR: black"><br></span><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black">&nbsp;&nbsp;int const b=100; </span><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black"><o:p></o:p></span></font></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt 42pt; FONT-FAMILY: 宋体; TEXT-ALIGN: left" align=left><font size=4><span style="FONT-SIZE: 12pt; COLOR: black">说明指针为指向常数的指针，即指针本身的值是可以改变的：</span><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black"><o:p></o:p></span></font></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt 42pt; FONT-FAMILY: 宋体; TEXT-ALIGN: left" align=left><font size=4><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black">&nbsp;const int *a=&amp;b</span><span style="FONT-SIZE: 12pt; COLOR: black">；</span><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black"><o:p></o:p></span></font></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt 42pt; FONT-FAMILY: 宋体; TEXT-ALIGN: left" align=left><font size=4><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black">&nbsp;&nbsp;int const *a=&amp;b</span><span style="FONT-SIZE: 12pt; COLOR: black">；</span><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black"><o:p></o:p></span></font></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt 42pt; FONT-FAMILY: 宋体; TEXT-ALIGN: left" align=left><font size=4><span style="FONT-SIZE: 12pt; COLOR: black">说明指针本身的值不可改变，但指向的内容可改变</span><span style="FONT-SIZE: 12pt; COLOR: black">：</span><span lang=EN-US style="FONT-SIZE: 10pt; COLOR: black"><br></span><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black">&nbsp;&nbsp;int * const a = &amp;b</span><span style="FONT-SIZE: 12pt; COLOR: black">；</span><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black"><o:p></o:p></span></font></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt 42pt; FONT-FAMILY: 宋体; TEXT-ALIGN: left" align=left><font size=4><span style="FONT-SIZE: 12pt; COLOR: black">说明指针为指向常数的常指针</span><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black">,</span><span style="FONT-SIZE: 12pt; COLOR: black">即指针本身与指针指向的内容都不可改变</span><span style="FONT-SIZE: 12pt; COLOR: black">：</span><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black"><o:p></o:p></span></font></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt 42pt; FONT-FAMILY: 宋体; TEXT-ALIGN: left" align=left><font size=4><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black">&nbsp;&nbsp;const int * const a = &amp;b</span><span style="FONT-SIZE: 12pt; COLOR: black">；</span><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black"><o:p></o:p></span></font></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt 42pt; FONT-FAMILY: 宋体; TEXT-ALIGN: left" align=left><font size=4><span style="FONT-SIZE: 12pt; COLOR: black">说明引用为常数引用</span><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black">,</span><span style="FONT-SIZE: 12pt; COLOR: black">即不能改变引用的值：</span><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black"><o:p></o:p></span></font></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt 42pt; FONT-FAMILY: 宋体; TEXT-ALIGN: left" align=left><font size=4><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black">&nbsp;const int &amp;a=100</span><span style="FONT-SIZE: 12pt; COLOR: black">。</span><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black"><o:p></o:p></span></font></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; FONT-FAMILY: 宋体; TEXT-ALIGN: left" align=left><font size=4><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black">&nbsp;<o:p></o:p></span></font></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt 21pt; TEXT-INDENT: -21pt; FONT-FAMILY: 宋体; TEXT-ALIGN: left" align=left><font size=4><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black"><span><span style="FONT-WEIGHT: normal; FONT-SIZE: 7pt; LINE-HEIGHT: normal; FONT-STYLE: normal; FONT-VARIANT: normal; font-size-adjust: none; font-stretch: normal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span style="FONT-SIZE: 12pt; COLOR: black">在定义函数时使用</span><span style="FONT-SIZE: 12pt; COLOR: black">：</span><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black"><o:p></o:p></span></font></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt 42pt; FONT-FAMILY: 宋体; TEXT-ALIGN: left" align=left><font size=4><span style="FONT-SIZE: 12pt; COLOR: black">做为参数使用，说明函数体内是不能修改该参数的</span><span style="FONT-SIZE: 12pt; COLOR: black">：</span><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black"><o:p></o:p></span></font></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt 42pt; FONT-FAMILY: 宋体; TEXT-ALIGN: left" align=left><font size=4><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black">&nbsp;&nbsp; void func(const int a)</span><span style="FONT-SIZE: 12pt; COLOR: black">；</span><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black"><o:p></o:p></span></font></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt 42pt; TEXT-INDENT: 6pt; FONT-FAMILY: 宋体; TEXT-ALIGN: left" align=left><font size=4><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black">&nbsp;void func(int const a)</span><span style="FONT-SIZE: 12pt; COLOR: black">；</span><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black"><o:p></o:p></span></font></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt 42pt; FONT-FAMILY: 宋体; TEXT-ALIGN: left" align=left><font size=4><span style="FONT-SIZE: 12pt; COLOR: black">做为返回值使用，说明函数的返回值是不能被修改的：</span><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black"><o:p></o:p></span></font></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt 42pt; FONT-FAMILY: 宋体; TEXT-ALIGN: left" align=left><font size=4><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black">&nbsp;&nbsp;const int func()</span><span style="FONT-SIZE: 12pt; COLOR: black">；</span><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black"><o:p></o:p></span></font></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt 42pt; FONT-FAMILY: 宋体; TEXT-ALIGN: left" align=left><font size=4><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black">&nbsp;</span><span style="FONT-SIZE: 12pt; COLOR: black">在函数中使用</span><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black">const,</span><span style="FONT-SIZE: 12pt; COLOR: black">情况与定义变量的情况基本一致：</span><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black">&nbsp;</span><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black"><o:p></o:p></span></font></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt 42pt; FONT-FAMILY: 宋体; TEXT-ALIGN: left" align=left><font size=4><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black">&nbsp;int func() </span><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black"><o:p></o:p></span></font></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt 42pt; FONT-FAMILY: 宋体; TEXT-ALIGN: left" align=left><font size=4><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black">{</span><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black"><o:p></o:p></span></font></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt 42pt; FONT-FAMILY: 宋体; TEXT-ALIGN: left" align=left><font size=4><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black">Const int a=10</span><span style="FONT-SIZE: 12pt; COLOR: black">；</span><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black"><o:p></o:p></span></font></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt 42pt; FONT-FAMILY: 宋体; TEXT-ALIGN: left" align=left><font size=4><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black">&#8230;</span><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black"><o:p></o:p></span></font></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt 42pt; FONT-FAMILY: 宋体; TEXT-ALIGN: left" align=left><font size=4><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black">}<o:p></o:p></span></font></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; FONT-FAMILY: 宋体; TEXT-ALIGN: left" align=left><font size=4><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black"><o:p>&nbsp;</o:p></span></font></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; FONT-FAMILY: 宋体; TEXT-ALIGN: left" align=left><font size=4><span style="FONT-SIZE: 12pt; COLOR: black">二、</span><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black">C++</span><span style="FONT-SIZE: 12pt; COLOR: black">中区别于</span><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black">C</span><span style="FONT-SIZE: 12pt; COLOR: black">的</span><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black">const</span><span style="FONT-SIZE: 12pt; COLOR: black">用法主要分为以下两种</span><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black"><o:p></o:p></span></font></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt 21pt; TEXT-INDENT: -21pt; FONT-FAMILY: 宋体; TEXT-ALIGN: left" align=left><font size=4><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black"><span><span style="FONT-WEIGHT: normal; FONT-SIZE: 7pt; LINE-HEIGHT: normal; FONT-STYLE: normal; FONT-VARIANT: normal; font-size-adjust: none; font-stretch: normal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black">const</span><span style="FONT-SIZE: 12pt; COLOR: black">类成员</span><span style="FONT-SIZE: 12pt; COLOR: black"> </span><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black"><o:p></o:p></span></font></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt 21pt; TEXT-INDENT: -21pt; FONT-FAMILY: 宋体; TEXT-ALIGN: left" align=left><font size=4><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const</span><span style="FONT-SIZE: 12pt; COLOR: black">类成员在对象构造期间允许被初始化并且在以后不允许被改变。</span><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black">const</span><span style="FONT-SIZE: 12pt; COLOR: black">类成员和一般的</span><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black">const </span><span style="FONT-SIZE: 12pt; COLOR: black">变量有所不同。</span><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black">const</span><span style="FONT-SIZE: 12pt; COLOR: black">类成员是对应于每个对象而言，它在对象构造期间被初始化，在这个对象的生存周期中不允许被改变。</span><span style="FONT-SIZE: 12pt; COLOR: black"> </span><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black"><o:p></o:p></span><br><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black"><span><span style="FONT-WEIGHT: normal; FONT-SIZE: 7pt; LINE-HEIGHT: normal; FONT-STYLE: normal; FONT-VARIANT: normal; font-size-adjust: none; font-stretch: normal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black">const </span><span style="FONT-SIZE: 12pt; COLOR: black">成员函数</span><span style="FONT-SIZE: 12pt; COLOR: black"> </span><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black"><o:p></o:p></span></font></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt 31.5pt; FONT-FAMILY: 宋体; TEXT-ALIGN: left" align=left><font size=4><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black">const </span><span style="FONT-SIZE: 12pt; COLOR: black">成员函数不允许在此函数体内对此函数对应的类的所有成员变量进行修改，这样可以提高程序的健壮性。</span><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black">Const</span><span style="FONT-SIZE: 12pt; COLOR: black">一般放在函数体后：</span><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black"><o:p></o:p></span></font></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 36pt; FONT-FAMILY: 宋体; TEXT-ALIGN: left" align=left><font size=4><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black">void &nbsp; fun() &nbsp; const</span><span style="FONT-SIZE: 12pt; COLOR: black">。</span><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black">&nbsp;</span></font><span lang=EN-US style="FONT-SIZE: 12pt; COLOR: black"><o:p></o:p></span></p>
</span>
<img src ="http://www.cppblog.com/dawnbreak/aggbug/79254.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/dawnbreak/" target="_blank">pear_li</a> 2009-04-08 13:17 <a href="http://www.cppblog.com/dawnbreak/articles/79254.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> 在 console mode 中使用 C/C++ 编译器</title><link>http://www.cppblog.com/dawnbreak/articles/76947.html</link><dc:creator>pear_li</dc:creator><author>pear_li</author><pubDate>Tue, 17 Mar 2009 16:00:00 GMT</pubDate><guid>http://www.cppblog.com/dawnbreak/articles/76947.html</guid><wfw:comment>http://www.cppblog.com/dawnbreak/comments/76947.html</wfw:comment><comments>http://www.cppblog.com/dawnbreak/articles/76947.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/dawnbreak/comments/commentRss/76947.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/dawnbreak/services/trackbacks/76947.html</trackback:ping><description><![CDATA[<li><font face=Verdana>CL.exe<br>1. cl /c [filename]&nbsp;&nbsp; /c为只编译不链接的意思，默认cl.exe工具会在编译之后自动调用LINK.EXE进行链接<br>2. cl.exe运行需要指定include、lib等环境变量<br>3. [filename]需要指定文件全名（包含后缀名）<br>4. C/C++的编译是针对文件进行的</font>
<li><font face=Verdana>lib.exe Microsoft库管理工具<br>用于打包编译后的库文件(obj)，使生成一个库文件(lib)</font> <br>obj文件和lib文件一样，可以直接用于link.exe链接工具中，生成exe可执行文件。lib.exe的作用只是打包，将多个obj打包为一个
<li>link.exe 链接工具<br>link [file(s)]&nbsp; /dll选项用来生成一个动态链接库(dll)
<li><font face=Verdana>库文件的编写：<br>库文件中只包含需要的函数及数据即可，不需要main函数，也不能有main函数<br></font><font face=Verdana>库文件的调用者，需要用extern关键字申明要调用的外部函数</font>
<li><font face=Verdana>目前以lib后缀的库有两种，一种为静态链接库(Static&nbsp;&nbsp; Libary，以下简称&#8220;静态库&#8221;)，另一种为动态连接库(DLL，以下简称&#8220;动态库&#8221;)的导入库(Import&nbsp;&nbsp; Libary，以下简称&#8220;导入库&#8221;）。&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;静态库是一个或者多个obj文件的打包，所以有人干脆把从obj文件生成lib的过程称为Archive，即合并到一起。比如你链接一个静态库，如果其中有错，它会准确的找到是哪个obj有错，即静态lib只是壳子。&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;动态库一般会有对应的导入库，方便程序静态载入动态链接库，否则你可能就需要自己LoadLibary调入DLL文件，然后再手工GetProcAddress获得对应函数了。有了导入库，你只需要链接导入库后按照头文件函数接口的声明调用函数就可以了。<br>&nbsp;&nbsp;&nbsp;&nbsp;导入库和静态库的区别很大，他们实质是不一样的东西。静态库本身就包含了实际执行代码、符号表等等，而对于导入库而言，其实际的执行代码位于动态库中，导入库只包含了地址符号表等，确保程序找到对应函数的一些基本地址信息。</font>
<li><font face=Verdana>动态链接库(dll)<br>一般为提高程序可读性，使用<font face=Verdana>#define DllExport __declspec(dllexport)</font>宏定义Dll中需要导出的函数；在每个需要导出的函数前使用DllExport，例如<font face=Verdana>DllExport int multiple(int x,int y)</font><br>生成一个Dll的方法：1.使用cl.exe /c [filename]将源文件生成库文件 2.使用link.exe /dll [filename.obj]将此obj文件生成dll<br>调用Dll的方法一：1. 使用extern指定外部函数名 2.在程序中直接使用将要调用的Dll的函数名 3.链接时加入上一步骤中生成的库文件(.lib)</font>
<li>方法二：运行时动态加载<br>C代码示例：<br>//lib.c<br><font face=Verdana>#define DllExport __declspec(dllexport)<br></font><font face=Verdana>DllExport int multiple(int x,int y)<br>{<br>&nbsp;return x*y;<br>}<br></font>//libCaller.c<br><font face=Verdana>#include "windows.h"<br>//extern&nbsp; int multiple(int x,int y);<br>typedef UINT (CALLBACK* LPFNDLLFUNC1)(int,int);<br></font><font face=Verdana>HINSTANCE hf;<br>LPFNDLLFUNC1 func;<br>main()<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font face=Verdana>hf= LoadLibrary("lib.dll");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(hf!=0)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;func = (LPFNDLLFUNC1)GetProcAddress(hf,"multiple");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("Multiple:%d\r\n",func(5,12));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FreeLibrary(hf);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}</font>
<li>Windows中的.lib文件分两种类型<br>1.静态库文件，用于编译时供其他对象引用 2.动态链接库的导出文件。链接时使用，无需使用LoadLibrary动态加载。
<li><font face=Verdana>Link.exe工具在使用.def文件生成DLL时的两种命令行语句：<br>1. 如果DEF文件中没有LIBRARY语句，需要同时指定/DEF /DLL选项<br>2. 如果DEF文件中有LIBRARY语句，只需指定/DEF选项即可生成DLL</font>
<li><font face=Verdana>Microsoft Visult Studio开发工具中CL.exe工具CL的意思及Compile和Link</font>
<li><font face=Verdana>生成DLL的方式（listed in recommended order of use）：<br>1. 在源文件中使用VC++自定义关键字__declspec(dllexport)<br>2. 使用包含EXPORTS语句的.def文件<br>3. 使用Link.exe工具的/EXPORT选项（定义导出函数）</font> <font face=Verdana>
<li>//DEF源文件Sample<br>EXPORTS<br>add
<li>//DLL源文件Sample<br>int add(int x,int y)<br>{<br>&nbsp;return x + y;<br>}
<li>int substract(int x,int y)<br>{<br>&nbsp;return x-y;<br>} </li>
</font>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>我总是鼓励 C/C++ 的学习者，在刚接触这个程式语言的时候，先以 console mode（DOS-like）程式为目标。换言之，不要一开始就想写 GUI 程式、想开视窗、想有眩目亮丽的画面 -- 那只是未走先飞，揠苗助长罢了。<br>所谓 console 程式，就是文字模式的程式，我们可以在其中好好把 C/C++ 的语言根基练好，而不会分心於其他暂无必要的 GUI 枝节上。<br>我一直以为，这是理所当然的事情，却也一直发现，有不少大专院校的大一 C/C++ 课程，同学们必须写个小作家、小画家、小算盘┅做为期中或期末作业。<br>果然世界不能大同，各人看法殊异 :)<br>我不但认为 C/C++ 程式开发对象初期要以 console mode 为主，我也认为，C/C++ 的程式开发环境，初期也要以 console mode 为主。换言之，不要一开始就进入整合环境（IDE）。整合环境中那麽多视窗、那麽多功能、那麽多预设值，会让程式新手眼花撩乱，无法掌握程式编译过程中一些有价值的知识与经验。<br>等我们对编译程序有了起码的了解，再来使用整合环境，我认为这才最好。<br>所以不论在 &lt;深入浅出 MFC&gt; 或 &lt;多型与虚拟&gt; 书籍中，我都会简述console mode 下的作业方式。&lt;深入浅出 MFC&gt; 在 p.224 列出，&lt;多型与虚拟&gt; 在 p.233 列出。<br>但仍然偶而会收到网友（不论是否上两本书的读者）的询问，询问console mode 的编译方式，或询问他们所遭遇的问题。<br>我再次整理这个题目。再有类似问题，我就可以整篇 mail 给发问者了。<br>★★ 注意：以下适合 PC 环境 ★★<br>●C/C++ 编译器需要的环境变数设定<br>古早以来，PC 上的 C 编译器，就需要两个环境变数：<br>LIB：这个环境变数告诉编译器说，必要的 libraries 在哪里（哪个磁碟目录下）<br>INCLUDE：告诉编译器说，必要的 header files 在哪里（哪个磁碟目录下）<br>另外，为了让我们能够在任何 working directory 都叫得到编译器，当然我们必须设定 PATH。<br>从古早以来，一直到现在，C/C++ 编译器都需要这三个环境变数。<br>●以 Visual C++ 为例<br>以 Visual C++ 为例，如果安装後的档案布局如下：<br>C:\MSDEV\VC98\BIN : 这里放有编译器 CL.EXE<br>C:\MSDEV\VC98\INCLUDE : 这里放有 C/C++ header files<br>C:\MSDEV\VC98\LIB : 这里放有 C/C++ standard libraries<br>那麽你可以写一个批次档如下：<br><small>set PATH=C:\MSDEV\VC98\BIN;C:\MSDEV\COMMON\MSDEV98\BIN<br>set INCLUDE=C:\MSDEV\VC98\INCLUDE<br>set LIB=C:\MSDEV\VC98\LIB</small><br>之所以需要另外设定 PATH=C:\MSDEV\COMMON\MSDEV98\BIN，是因为编译器 CL.EXE 执行时需要 MSPDB60.DLL，而它被安装於 C:\MSDEV\COMMON\MSDEV98\BIN 之中。<br>如果你写的程式不只是单纯的 C/C++ 程式，还用到了 MFC，一样可以在 console mode 下编译，这时候你的环境变数应该如此设定：<br><small>set PATH=C:\MSDEV\VC98\BIN;C:\MSDEV\COMMON\MSDEV98\BIN<br>set INCLUDE=C:\MSDEV\VC98\INCLUDE;C:\MSDEV\VC98\MFC\INCLUDE<br>set LIB=C:\MSDEV\VC98\LIB;C:\MSDEV\VC98\MFC\LIB</small><br>多指定了 MFC\INCLUDE 和 MFC\LIB，就可以让编译器和联结器找到 MFC 的 header files 和 libraries。如果你还需要用到 ATL，就得在 INCLUDE 环境变数中再加上 C:\MSDEV\VC98\ATL\INCLUDE。<br>●以 Borland C++Builder 为例<br>以 Borland C++Builder 为例，如果安装後的档案布局如下：<br>C:\BORLAND\CBuilder3\BIN : 这里放有编译器 BCC32.EXE<br>C:\BORLAND\CBuilder3\INCLUDE : 这里放有 C/C++ header files<br>C:\BORLAND\CBuilder3\LIB : 这里放有 C/C++ standard libraries<br>那麽你可以写一个批次档如下：<br><small>set PATH=C:\BORLAND\CBuilder3\BIN<br>set INCLUDE=C:\BORLAND\CBuilder3\INCLUDE<br>set LIB=C:\BORLAND\CBuilder3\LIB</small><br>●如何在 console 中编译 C/C++ 程式<br>首先，开启一个 DOS Box（DOS Prompt, DOS VM），然後在该 DOS box 中执行上述写好的批次档，完成环境变数的设定。你可以再在 DOS 提示号下键入 set 命令，看看环境变数的设定内容正确与否。<br>然後就可以直接在 DOS 提示号下键入编译器名称，开始编译了。如果你使用 Visual C++，就这麽做：<br><small>C:\&gt; CL test.cpp <enter></small><br>如果你使用 C++Builder，就这麽做：<br><small>C:\&gt; BCC32 test.cpp <enter></small><br>至於特殊情况下需要什麽特殊的 options，就必须自己查一下啦。只要执行 CL /? 或 BCC32（其後不加任何引数），便可看到所有的 compile options。<br>●编译器与联结器的关系<br>早期的编译过程与联结过程是分开的。换句话说我们必须做两个动作：<br><small>C:\&gt; Cl test.cpp<br>C:\&gt; LINK test.obj xxx （xxx 代表各个必要的 libraries）</small><br>或是：<br><small>C:\&gt; BCC32 test.cpp<br>C:\&gt; TLINK32 test.obj xxx （xxx 代表各个必要的 libraries）<br></small><br>如今的编译过程与联结过程当然还是分开的，但是我们的动作只需一个：<br><small>C:\&gt; CL test.cpp</small><br>或是：<br><small>C:\&gt; BCC32 test.cpp</small><br>这是因为编译器变聪明了，除非你指定 /c option（表示只编译不联结），否则它便自动为你呼叫联结器进行联结动作。过去以来颇令 programmer烦恼的「该使用哪些 libraries」的问题，编译器也有了聪明的解决方案：它将程式中用到的 library functions 记录起来，同时也录下它们所属的library 名称，於是联结器就可以从这个表格中知道要联结哪些 libraries 了。<br>●环境变数与 DOS VM（Virtual Machine）的关系<br>你可以同时开起多个 DOS Box，但是你不能够在某个 DOS Box 中执行上述批次档而在另一个 DOS VM 中享受其环境设定。<br>这是因为每个 DOS Box 都是一个 Virtual Machine，彼此谁也看不到谁，互不相干。<br>除非你在 autoexec.bat 中就设定好上述那些环境变数。这麽一来，任何一个新开启的 DOS VM 便会因为继承最原始的 DOS VM 环境，而继承了那些变数设定。<br>●环境空间（environment space）不足<br>最易造成大家困扰的，就是环境空间（environment space）不足的问题。<br>当你安装好 Visual C++，会在其 BIN 子目录中发现一个名为 VCVARS32.BAT 的档案。这个档案其实就是做上述的环境变数设定动作（这在 Visual C++ 安装过程的最後一个步骤有说明。哎，有多少人安装软体不看说明！）。所以，你可以在任何 DOS Box 中执行此档，取代前述我们自己的批次档。<br>但是通常大家都有失败的经验，得到 "<strong>Out of environment space</strong>" 的错误讯息。这是因为 VCVARS32.BAT 使用以下句法：<br><small>set INCLUDE=%MSVCDir%\ATL\INCLUDE;%MSVCDir%\INCLUDE;%MSVCDir%\MFC\INCLUDE;%INCLUDE%<br>set LIB=%MSVCDir%\LIB;%MSVCDir%\MFC\LIB;%LIB%</small><br>意思是把 INCLUDE 的原始设定（%INCLUDE%）再附加其他设定，并把LIB 的原始设定（%LIB%）再附加其他设定。如果原始设定已经很长，多来这麽几次，便 "Out of environment space" 啦！<br>做法之一是调高环境空间的大小。请在 c:\config.sys 档中加上这行：<br><small>shell=C:\COMMAND.COM C:\ /E:1024 /P</small><br>其中 /E:1024 便是表示将环境空间调为 1024 bytes。（不够？再调）<br>做法之二是不要使用 VCVARS32.BAT 的那种「附加」句型，改用前述我们自己的批次档。要知道，我们可能有好几个编译器环境（VC、BCB、G++ ┅），需要轮番测试我们的程式；如果使用「附加」句型，多来几次，再大的环境空间也会消磨殆尽。<br>方法一和方法二要双管齐下唷。<br>●有任何规模上的限制吗？<br>使用 console 模式（或称 command line 模式）来编译联结程式，程式的大小可否有任何规模上的限制？答案是没有！<br>它的缺点是没有工具帮你管理档案、没有预设值让你少打几个字、没有分析工具帮你整理 objects，让你浏览 objects、symbols┅。所以一旦你基本功学会了，要开始中大型程式的设计，当然以整合环境（IDE）为佳。<br>●不要误会<br>我这不是开倒车，要大家回到茹毛饮血的时代，都回头去做山顶洞人。而是我觉得，对於一位 C/C++ 初学者，整合环境（IDE）的运用恐怕带来一头雾水，不如先在 console mode 下作业。一方面多认识一些环境设定方面的常识，满好的，一方面比较方便好用，也不必写个 1000 行的小小练习还得启动 五五加农炮，一方面求知的力量可以全部放在语言的练习上头。<br>等有了一定的程度，再使用整合环境，就不会如坠五里雾了。</p>
<img src ="http://www.cppblog.com/dawnbreak/aggbug/76947.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/dawnbreak/" target="_blank">pear_li</a> 2009-03-18 00:00 <a href="http://www.cppblog.com/dawnbreak/articles/76947.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Windows下 POSIX 线程编程</title><link>http://www.cppblog.com/dawnbreak/articles/76946.html</link><dc:creator>pear_li</dc:creator><author>pear_li</author><pubDate>Tue, 17 Mar 2009 15:58:00 GMT</pubDate><guid>http://www.cppblog.com/dawnbreak/articles/76946.html</guid><wfw:comment>http://www.cppblog.com/dawnbreak/comments/76946.html</wfw:comment><comments>http://www.cppblog.com/dawnbreak/articles/76946.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/dawnbreak/comments/commentRss/76946.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/dawnbreak/services/trackbacks/76946.html</trackback:ping><description><![CDATA[<h3><strong>1. POSIX 标准</strong></h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; POSIX是Portable Operating System Interface of Unix的缩写。由IEEE（Institute of Electrical and Electronic Engineering）开发，由ANSI和ISO标准化。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; POSIX的诞生和Unix的发展是密不可分的，Unix于70年代诞生于Bell lab，并于80年代向美各大高校分发V7版的源码以做研究。UC Berkeley在V7的基础上开发了BSD Unix。后来很多商业厂家意识到Unix的价值也纷纷以Bell Lab的System V或BSD为基础来开发自己的Unix，较著名的有Sun OS，AIX，VMS。由于各厂家对Unix的开发各自为政，造成了Unix的版本相当混乱，给软件的可移植性带来很大困难，对Unix的发展极为不利。为结束这种局面，IEEE开发了POSIX，POSIX在源代码级别上定义了一组最小的Unix(类Unix)操作系统接口。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; POSIX 表示可移植操作系统接口（Portable Operating System Interface ，缩写为 POSIX 是为了读音更像 UNIX）。电气和电子工程师协会（Institute of Electrical and Electronics Engineers，IEEE）最初开发 POSIX 标准，是为了提高 UNIX 环境下应用程序的可移植性。然而，POSIX 并不局限于 UNIX。许多其它的操作系统，例如 DEC OpenVMS 和 Microsoft Windows NT，都支持 POSIX 标准，尤其是 IEEE Std. 1003.1-1990（1995 年修订）或 POSIX.1，POSIX.1 提供了源代码级别的 C 语言应用编程接口（API）给操作系统的服务程序，例如读写文件。POSIX.1 已经被国际标准化组织（International Standards Organization，ISO）所接受，被命名为 ISO/IEC 9945-1:1990 标准。 <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; POSIX 现在已经发展成为一个非常庞大的标准族，某些部分正处在开发过程中。表 1-1 给出了 POSIX 标准的几个重要组成部分。POSIX 与 IEEE 1003 和 2003 家族的标准是可互换的。除 1003.1 之外，1003 和 2003 家族也包括在表中。 </p>
<p><strong>表1：标准的重要组成部分</strong><br>1003.0 <br>管理 POSIX 开放式系统环境（OSE）。IEEE 在 1995 年通过了这项标准。 ISO 的版本是 ISO/IEC 14252:1996。 <br>1003.1 <br>被广泛接受、用于源代码级别的可移植性标准。1003.1 提供一个操作系统的 C 语言应用编程接口（API）。IEEE 和 ISO 已经在 1990 年通过了这个标准，IEEE 在 1995 年重新修订了该标准。 <br>1003.1b <br>一个用于实时编程的标准（以前的 P1003.4 或 POSIX.4）。这个标准在 1993 年被 IEEE 通过，被合并进 ISO/IEC 9945-1。 <br>1003.1c <br>一个用于线程（在一个程序中当前被执行的代码段）的标准。以前是 P1993.4 或 POSIX.4 的一部分，这个标准已经在 1995 年被 IEEE 通过，归入 ISO/IEC 9945-1:1996。 <br>1003.1g <br>一个关于协议独立接口的标准，该接口可以使一个应用程序通过网络与另一个应用程序通讯。 1996 年，IEEE 通过了这个标准。 <br>1003.2 <br>一个应用于 shell 和 工具软件的标准，它们分别是操作系统所必须提供的命令处理器和工具程序。 1992 年 IEEE 通过了这个标准。ISO 也已经通过了这个标准（ISO/IEC 9945-2:1993）。 <br>1003.2d <br>改进的 1003.2 标准。 <br>1003.5 <br>一个相当于 1003.1 的 Ada 语言的 API。在 1992 年，IEEE 通过了这个标准。并在 1997 年对其进行了修订。ISO 也通过了该标准。 <br>1003.5b <br>一个相当于 1003.1b（实时扩展）的 Ada 语言的 API。IEEE 和 ISO 都已经通过了这个标准。ISO 的标准是 ISO/IEC 14519:1999。 <br>1003.5c <br>一个相当于 1003.1q（协议独立接口）的 Ada 语言的 API。在 1998 年， IEEE 通过了这个标准。ISO 也通过了这个标准。 <br>1003.9 <br>一个相当于 1003.1 的 FORTRAN 语言的 API。在 1992 年，IEEE 通过了这个标准，并于 1997 年对其再次确认。ISO 也已经通过了这个标准。 <br>1003.10 <br>一个应用于超级计算应用环境框架（Application Environment Profile，AEP）的标准。在 1995 年，IEEE 通过了这个标准。 <br>1003.13 <br>一个关于应用环境框架的标准，主要针对使用 POSIX 接口的实时应用程序。在 1998 年，IEEE 通过了这个标准。 <br>1003.22 <br>一个针对 POSIX 的关于安全性框架的指南。 <br>1003.23 <br>一个针对用户组织的指南，主要是为了指导用户开发和使用支持操作需求的开放式系统环境（OSE）框架 <br>2003 <br>针对指定和使用是否符合 POSIX 标准的测试方法，有关其定义、一般需求和指导方针的一个标准。在 1997 年，IEEE 通过了这个标准。 <br>2003.1 <br>这个标准规定了针对 1003.1 的 POSIX 测试方法的提供商要提供的一些条件。在 1992 年，IEEE 通过了这个标准。 <br>2003.2 <br>一个定义了被用来检查与 IEEE 1003.2（shell 和 工具 API）是否符合的测试方法的标准。在 1996 年，IEEE 通过了这个标准。 </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 除了 1003 和 2003 家族以外，还有几个其它的 IEEE 标准，例如 1224 和 1228，它们也提供开发可移植应用程序的 API。要想得到关于 IEEE 标准的最新信息，可以访问 IEEE 标准的主页，网址是 <a href="http://standards.ieee.org/"><u><font color=#0066cc>http://standards.ieee.org/</font></u></a>。有关 POSIX 标准的概述信息，请访问 Web 站点 <a href="http://standards.ieee.org/reading/ieee/stad_public/description/posix/"><u><font color=#0066cc>http://standards.ieee.org/reading/ieee/stad_public/description/posix/</font></u></a>。</p>
<h3><strong>2. Liniux下的线程编程</strong></h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Linux系统下的多线程遵循POSIX线程接口，称为pthread。从上面的描述不难知道，<strong>POSIX线程接口是POSIX众多标准中的一个（<a href="http://www.unix-systems.org/version3/ieee_std.html"><u><font color=#0066cc>POSIX 1003.1-2001</font></u></a>）</strong>。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 编写Linux下的多线程程序，需要使用头文件pthread.h，连接时需要使用库libpthread.a。顺便说一下，Linux下pthread的实现是通过系统调用 clone() 来实现的。clone() 是Linux所特有的系统调用，它的使用方式类似fork，关于 clone() 的详细情况，有兴趣的读者可以去查看有关文档说明。</p>
<p>下面是一个 POSIX 线程的简单示例程序(thread1.c)： </p>
<div style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: #e6e6e6; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 95%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid">
<div><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056480.gif" align=top><span style="COLOR: #000000">#include&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">pthread.h</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056480.gif" align=top>#include&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">stdlib.h</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056480.gif" align=top>#include&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">unistd.h</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br><img id=_95_198_Open_Image onclick="this.style.display='none'; document.getElementById('_95_198_Open_Text').style.display='none'; document.getElementById('_95_198_Closed_Image').style.display='inline'; document.getElementById('_95_198_Closed_Text').style.display='inline';" alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056210.gif" align=top><img id=_95_198_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; document.getElementById('_95_198_Closed_Text').style.display='none'; document.getElementById('_95_198_Open_Image').style.display='inline'; document.getElementById('_95_198_Open_Text').style.display='inline';" alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056233.gif" align=top>&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">thread_function(</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">arg)&nbsp;</span><span id=_95_198_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">...</span><span id=_95_198_Open_Text><span style="COLOR: #000000">{<br><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056264.gif" align=top>&nbsp;&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;i;<br><img id=_130_181_Open_Image onclick="this.style.display='none'; document.getElementById('_130_181_Open_Text').style.display='none'; document.getElementById('_130_181_Closed_Image').style.display='inline'; document.getElementById('_130_181_Closed_Text').style.display='inline';" alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056227.gif" align=top><img id=_130_181_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; document.getElementById('_130_181_Closed_Text').style.display='none'; document.getElementById('_130_181_Open_Image').style.display='inline'; document.getElementById('_130_181_Open_Text').style.display='inline';" alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056512.gif" align=top>&nbsp;&nbsp;</span><span style="COLOR: #0000ff">for</span><span style="COLOR: #000000">&nbsp;(&nbsp;i</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;&nbsp;i</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">20</span><span style="COLOR: #000000">;&nbsp;i</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">)&nbsp;</span><span id=_130_181_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">...</span><span id=_130_181_Open_Text><span style="COLOR: #000000">{<br><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056264.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;printf(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Thread&nbsp;says&nbsp;hi! </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056264.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;sleep(</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">);<br><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056186.gif" align=top>&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056264.gif" align=top>&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;NULL;<br><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056497.gif" align=top>}</span></span><span style="COLOR: #000000"><br><img id=_215_472_Open_Image onclick="this.style.display='none'; document.getElementById('_215_472_Open_Text').style.display='none'; document.getElementById('_215_472_Closed_Image').style.display='inline'; document.getElementById('_215_472_Closed_Text').style.display='inline';" alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056210.gif" align=top><img id=_215_472_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; document.getElementById('_215_472_Closed_Text').style.display='none'; document.getElementById('_215_472_Open_Image').style.display='inline'; document.getElementById('_215_472_Open_Text').style.display='inline';" alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056233.gif" align=top></span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;main(</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">)&nbsp;</span><span id=_215_472_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">...</span><span id=_215_472_Open_Text><span style="COLOR: #000000">{<br><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056264.gif" align=top>&nbsp;&nbsp;pthread_t&nbsp;mythread;<br><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056264.gif" align=top>&nbsp;&nbsp;<br><img id=_307_362_Open_Image onclick="this.style.display='none'; document.getElementById('_307_362_Open_Text').style.display='none'; document.getElementById('_307_362_Closed_Image').style.display='inline'; document.getElementById('_307_362_Closed_Text').style.display='inline';" alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056227.gif" align=top><img id=_307_362_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; document.getElementById('_307_362_Closed_Text').style.display='none'; document.getElementById('_307_362_Open_Image').style.display='inline'; document.getElementById('_307_362_Open_Text').style.display='inline';" alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056512.gif" align=top>&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(&nbsp;pthread_create(&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">mythread,&nbsp;NULL,&nbsp;thread_function,&nbsp;NULL)&nbsp;)&nbsp;</span><span id=_307_362_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">...</span><span id=_307_362_Open_Text><span style="COLOR: #000000">{<br><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056264.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;printf(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">error&nbsp;creating&nbsp;thread.</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056264.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;abort();<br><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056186.gif" align=top>&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><img id=_405_459_Open_Image onclick="this.style.display='none'; document.getElementById('_405_459_Open_Text').style.display='none'; document.getElementById('_405_459_Closed_Image').style.display='inline'; document.getElementById('_405_459_Closed_Text').style.display='inline';" alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056227.gif" align=top><img id=_405_459_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; document.getElementById('_405_459_Closed_Text').style.display='none'; document.getElementById('_405_459_Open_Image').style.display='inline'; document.getElementById('_405_459_Open_Text').style.display='inline';" alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056512.gif" align=top>&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(&nbsp;pthread_join&nbsp;(&nbsp;mythread,&nbsp;NULL&nbsp;)&nbsp;)&nbsp;</span><span id=_405_459_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">...</span><span id=_405_459_Open_Text><span style="COLOR: #000000">{<br><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056264.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;printf(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">error&nbsp;joining&nbsp;thread.</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056264.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;abort();<br><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056186.gif" align=top>&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056264.gif" align=top>&nbsp;&nbsp;exit(</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">);<br><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056497.gif" align=top>}</span></span><span style="COLOR: #000000"><br><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056480.gif" align=top></span></div>
</div>
<p>&nbsp;要编译这个程序，只需先将程序存为 thread1.c，然后输入：</p>
<div>$ gcc thread1.c -o thread1 -lpthread</div>
<p>运行则输入：</p>
<div>$ ./thread1</div>
<h3><strong>3. Windows下POSIX线程编程</strong></h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Windows本身没有提供对POSIX的支持。但有一个叫 <a href="http://sources.redhat.com/pthreads-win32/"><u><font color=#0066cc>POSIX Threads for Win32</font></u></a> 的开源项目给出了一个功能比较完善的Windows下pthreads API的实现。目前的最新版本是Pthreads-w32 release 2.8.0 (2006-12-22)。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我没有测试过这个最新版本，这里只给出2.7.0版的链接：<a title=ftp://sources.redhat.com/pub/pthreads-win32/pthreads-w32-2-7-0-release.exe href="ftp://sources.redhat.com/pub/pthreads-win32/pthreads-w32-2-7-0-release.exe"><u><font color=#0066cc>ftp://sources.redhat.com/pub/pthreads-win32/pthreads-w32-2-7-0-release.exe</font></u></a>。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 关于该开源项目的详细介绍见：<a title=http://sources.redhat.com/pthreads-win32/ href="http://sources.redhat.com/pthreads-win32/"><u><font color=#0066cc>http://sources.redhat.com/pthreads-win32/</font></u></a>。</p>
<h4><strong>3.1 简单使用</strong></h4>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 上面的exe文件是一个自解压文件，解压后，Pre-built.2目录中有编译所需要的头文件（include子目录）和库文件（lib子目录）。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 一个简单的测试程序(main.cpp)：</p>
<div style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: #e6e6e6; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 95%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid">
<div><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056480.gif" align=top><span style="COLOR: #000000">#include&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">stdio.h</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056480.gif" align=top>#include&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">pthread.h</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056480.gif" align=top>#include&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">assert.h</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056480.gif" align=top><br><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056480.gif" align=top></span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;Function_t(</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;Param)<br><img id=_91_214_Open_Image onclick="this.style.display='none'; document.getElementById('_91_214_Open_Text').style.display='none'; document.getElementById('_91_214_Closed_Image').style.display='inline'; document.getElementById('_91_214_Closed_Text').style.display='inline';" alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056210.gif" align=top><img id=_91_214_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; document.getElementById('_91_214_Closed_Text').style.display='none'; document.getElementById('_91_214_Open_Image').style.display='inline'; document.getElementById('_91_214_Open_Text').style.display='inline';" alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056233.gif" align=top></span><span id=_91_214_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">...</span><span id=_91_214_Open_Text><span style="COLOR: #000000">{<br><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056264.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;printf(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">I&nbsp;am&nbsp;a&nbsp;thread!&nbsp;&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056264.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;pthread_t&nbsp;myid&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;pthread_self();<br><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056264.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;printf(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">thread&nbsp;ID=%d&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">,&nbsp;myid);<br><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056264.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;NULL;<br><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056497.gif" align=top>}</span></span><span style="COLOR: #000000"><br><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056480.gif" align=top><br><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056480.gif" align=top></span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;main()<br><img id=_228_596_Open_Image onclick="this.style.display='none'; document.getElementById('_228_596_Open_Text').style.display='none'; document.getElementById('_228_596_Closed_Image').style.display='inline'; document.getElementById('_228_596_Closed_Text').style.display='inline';" alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056210.gif" align=top><img id=_228_596_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; document.getElementById('_228_596_Closed_Text').style.display='none'; document.getElementById('_228_596_Open_Image').style.display='inline'; document.getElementById('_228_596_Open_Text').style.display='inline';" alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056233.gif" align=top></span><span id=_228_596_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">...</span><span id=_228_596_Open_Text><span style="COLOR: #000000">{<br><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056264.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;pthread_t&nbsp;pid;<br><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056264.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;pthread_attr_t&nbsp;attr;<br><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056264.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;pthread_attr_init(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">attr);<br><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056264.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;pthread_attr_setscope(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">attr,&nbsp;PTHREAD_SCOPE_PROCESS);<br><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056264.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;pthread_attr_setdetachstate(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">attr,&nbsp;PTHREAD_CREATE_DETACHED);<br><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056264.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;pthread_create(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">pid,&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">attr,&nbsp;Function_t,&nbsp;NULL);<br><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056264.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;printf(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">========================================&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056264.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;getchar();<br><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056264.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;pthread_attr_destroy(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">attr);<br><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056264.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">;<br><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056497.gif" align=top>}</span></span><span style="COLOR: #000000">&nbsp;</span></div>
</div>
<p>使用 cl.exe 编译（不熟悉 cl.exe 的请参考：<a title=http://blog.csdn.net/liuyongjin1984/archive/2008/01/07/2029405.aspx href="http://blog.csdn.net/liuyongjin1984/archive/2008/01/07/2029405.aspx"><u><font color=#800080>http://blog.csdn.net/liuyongjin1984/archive/2008/01/07/2029405.aspx</font></u></a> 或者参见下面3.2部分）：</p>
<div style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: #e6e6e6; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 95%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid">
<div><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056480.gif" align=top>》<span style="COLOR: #000000">rem&nbsp;cl.bat<br><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056480.gif" align=top><br><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056480.gif" align=top>》cl.exe&nbsp;main.cpp &nbsp;</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">c&nbsp; </span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">I</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">c:pthreads-w32-2-7-0-releasePre-built.2include</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"><br><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056480.gif" align=top><br><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056480.gif" align=top>》link.exe&nbsp; </span><span style="COLOR: #000000">/</span><span style="COLOR: #0000ff">out</span><span style="COLOR: #000000">:main_cl.exe&nbsp; main.obj&nbsp; </span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">LIBPATH:</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">c:pthreads-w32-2-7-0-releasePre-built.2lib</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&nbsp;&nbsp; pthreadVC2.lib</span></div>
</div>
<p><font size=3>&nbsp;<strong>3.2 使用VC++ 6.0或Visual Studio 2005来运行上面的程序</strong></font></p>
<p>关键有两点：</p>
<p>1. 是将<strong>头文件（include子目录）和库文件（lib子目录）</strong>中的内容添加到VC++ 6.0或Visual Studio 2005开发环境对应的include和lib目录下。</p>
<p>具体来说（<strong>以添加include目录为例，添加lib目录类似</strong>）：</p>
<p><strong>图1：VC++ 6.0（添加include目录：工具--》选项--》目录）</strong></p>
<p><a href="http://p.blog.csdn.net/images/p_blog_csdn_net/liuyongjin1984/WindowsLiveWriter/WindowsPOSIX_14824/1.png"></a><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056899.bmp">&nbsp;</p>
<p>&nbsp;</p>
<p><strong>图2：Visual Studio 2005(添加include目录：tools--》options)</strong></p>
<p><a href="http://p.blog.csdn.net/images/p_blog_csdn_net/liuyongjin1984/WindowsLiveWriter/WindowsPOSIX_14824/2.png"></a><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056941.bmp">&nbsp;</p>
<p>&nbsp;</p>
<p><strong>2. 指定link时要连接的库的名称（pthreadVC2.lib）</strong></p>
<p><strong>图3：VC++ 6.0（工程--》设置--》连接）</strong></p>
<p><strong><a href="http://p.blog.csdn.net/images/p_blog_csdn_net/liuyongjin1984/WindowsLiveWriter/WindowsPOSIX_14824/3.png"></a></strong></p>
<p><strong><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056613.bmp"></strong></p>
<p><strong></strong></p>
<p><strong>图4：Visual Studio 2005(project--&gt;* property pages)</strong></p>
<p><a href="http://p.blog.csdn.net/images/p_blog_csdn_net/liuyongjin1984/WindowsLiveWriter/WindowsPOSIX_14824/4.png"></a><img alt="" src="http://www.newbooks.com.cn/infos/pic/200801/179364/20080109010056694.bmp">&nbsp;</p>
<strong></strong>
<h3>4. 书籍推荐<br><font size=3>&lt;&lt; POSIX Multithread Programming Primer &gt;&gt;:</font> <a href="http://download.csdn.net/source/237125"><u><font color=#0066cc>http://download.csdn.net/source/237125</font></u></a></h3>
<img src ="http://www.cppblog.com/dawnbreak/aggbug/76946.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/dawnbreak/" target="_blank">pear_li</a> 2009-03-17 23:58 <a href="http://www.cppblog.com/dawnbreak/articles/76946.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Reversing MS VC++ Part II: Classes, Methods and RTTI</title><link>http://www.cppblog.com/dawnbreak/articles/76231.html</link><dc:creator>pear_li</dc:creator><author>pear_li</author><pubDate>Wed, 11 Mar 2009 07:37:00 GMT</pubDate><guid>http://www.cppblog.com/dawnbreak/articles/76231.html</guid><wfw:comment>http://www.cppblog.com/dawnbreak/comments/76231.html</wfw:comment><comments>http://www.cppblog.com/dawnbreak/articles/76231.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/dawnbreak/comments/commentRss/76231.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/dawnbreak/services/trackbacks/76231.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 摘要&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MS VC++ 是Win32平台上最广泛使用的编译器，因此熟悉它的内部工作机制对于Win32逆向爱好者非常重要。能够理解编译器生成的附加（glue）代码有助于快速理解程序员写的实际代码。同样也有助于恢复程序的高级结构。&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在Part II中，我...&nbsp;&nbsp;<a href='http://www.cppblog.com/dawnbreak/articles/76231.html'>阅读全文</a><img src ="http://www.cppblog.com/dawnbreak/aggbug/76231.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/dawnbreak/" target="_blank">pear_li</a> 2009-03-11 15:37 <a href="http://www.cppblog.com/dawnbreak/articles/76231.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++对象内存模型</title><link>http://www.cppblog.com/dawnbreak/articles/76230.html</link><dc:creator>pear_li</dc:creator><author>pear_li</author><pubDate>Wed, 11 Mar 2009 07:36:00 GMT</pubDate><guid>http://www.cppblog.com/dawnbreak/articles/76230.html</guid><wfw:comment>http://www.cppblog.com/dawnbreak/comments/76230.html</wfw:comment><comments>http://www.cppblog.com/dawnbreak/articles/76230.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/dawnbreak/comments/commentRss/76230.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/dawnbreak/services/trackbacks/76230.html</trackback:ping><description><![CDATA[<font face="Courier New">/*<br>&nbsp;*C++中非多态类对象的内存映像模型<br>&nbsp;*<br>&nbsp;*<br>&nbsp;*<br>&nbsp;*/<br>#include&nbsp;&lt;iostream&gt;<br><br>using&nbsp;namespace&nbsp;std;<br><br>class&nbsp;rectangle<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;public:<br>&nbsp;&nbsp;rectangle():m_length(1),&nbsp;m_width(1){}<br>&nbsp;&nbsp;~rectangle(){}<br>&nbsp;&nbsp;float&nbsp;GetLength()&nbsp;const<br>&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;m_length;<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;void&nbsp;SetLength(float&nbsp;length)<br>&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_length&nbsp;=&nbsp;length;<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;float&nbsp;GetWidth()&nbsp;const<br>&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;m_width;<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;void&nbsp;SetWidth(float&nbsp;width)<br>&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_width&nbsp;=&nbsp;width;<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;void&nbsp;Draw()&nbsp;const&nbsp;<br>&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//...<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//...<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//...<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;static&nbsp;unsigned&nbsp;int&nbsp;GetCount()<br>&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;m_count;<br>&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;protected:<br>&nbsp;&nbsp;rectangle(const&nbsp;rectangle&nbsp;&amp;&nbsp;copy)<br>&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//...<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//...<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//...<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;rectangle&amp;&nbsp;operator=(const&nbsp;rectangle&nbsp;&amp;&nbsp;assign)<br>&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//...<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//...<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//...<br>&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;private:<br>&nbsp;&nbsp;float&nbsp;m_length;<br>&nbsp;&nbsp;float&nbsp;m_width;<br>&nbsp;&nbsp;static&nbsp;unsigned&nbsp;int&nbsp;m_count;<br>};<br>int&nbsp;main()<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;rectangle&nbsp;rect;<br>&nbsp;&nbsp;&nbsp;&nbsp;rect.SetWidth(10);<br>&nbsp;&nbsp;&nbsp;&nbsp;cout&lt;&lt;rect.GetWidth()&lt;&lt;endl;<br>&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;0;<br>}<br>/*<br>&nbsp;*有如下规则：<br>非静态数据成员被放在每一个对象体内作为对象专有的数据成员<br>静态数据成员被提取出来放在程序的静态数据区内，为该类所有对象共享，因此只存在一份。<br>静态和非静态成员函数最终都被提取出来放在程序的代码段中并为该类所有对象共享，因此每一个成员函数也只能存在一份代码实体。<br>因此，构成对象本身的只有数据，任何成员函数都不隶属于任何一个对象，非静态成员函数与对象的关系就是绑定，绑定的中介就是this指针。<br>成员函数为该类所有对象共享，不仅是处于简化语言实现、节省存储的目的，而且是为了使同类对象有一致的行为。同类对象的行为虽然一致，但是操作不同的数据成员。<br>&nbsp;*/</font><br>
<img src ="http://www.cppblog.com/dawnbreak/aggbug/76230.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/dawnbreak/" target="_blank">pear_li</a> 2009-03-11 15:36 <a href="http://www.cppblog.com/dawnbreak/articles/76230.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>通过重载new和delete实现简单的对象池 </title><link>http://www.cppblog.com/dawnbreak/articles/76229.html</link><dc:creator>pear_li</dc:creator><author>pear_li</author><pubDate>Wed, 11 Mar 2009 07:33:00 GMT</pubDate><guid>http://www.cppblog.com/dawnbreak/articles/76229.html</guid><wfw:comment>http://www.cppblog.com/dawnbreak/comments/76229.html</wfw:comment><comments>http://www.cppblog.com/dawnbreak/articles/76229.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/dawnbreak/comments/commentRss/76229.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/dawnbreak/services/trackbacks/76229.html</trackback:ping><description><![CDATA[<p>通过重载new和delete实现简单的对象池 </p>
<p>对象池的用途在这里就不介绍了，本例中只是其一个简单的实现。 </p>
<p><span style="FONT-SIZE: 10pt; FONT-FAMILY: Courier Std"><span style="COLOR: blue">#include</span> <span style="COLOR: #a31515">&lt;list&gt;<br><br></span><span style="COLOR: blue">#include</span> <span style="COLOR: #a31515">&lt;iostream&gt;<br></span><span style="COLOR: blue">using</span> <span style="COLOR: blue">namespace</span> std;<br><br><span style="COLOR: blue">template</span>&lt;<span style="COLOR: blue">class</span> T&gt;<br><span style="COLOR: blue">class</span> object_pool<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;list&lt;<span style="COLOR: blue">void</span> *&gt; data_list;<br><span style="COLOR: blue">public</span>:<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: blue">void</span>* malloc_data()<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: blue">if</span>(!data_list.empty())<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;list&lt;<span style="COLOR: blue">void</span> *&gt;::iterator it = data_list.begin();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: blue">void</span> *p = *it;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;data_list.pop_front();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: blue">return</span> p;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: blue">else<br></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: blue">void</span>* p = malloc(<span style="COLOR: blue">sizeof</span>(T));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: blue">return</span> p;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: blue">void</span> free_data(<span style="COLOR: blue">void</span>* p)<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;data_list.push_back(p);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>};<br><br><span style="COLOR: blue">class</span> A<br>{<br><span style="COLOR: blue">public</span>:<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: blue">int</span> value;<br>&nbsp;&nbsp;&nbsp;&nbsp;A(<span style="COLOR: blue">int</span> a):value(a){cout&lt;&lt;<span style="COLOR: #a31515">"A("</span>&lt;&lt;a&lt;&lt;<span style="COLOR: #a31515">") called"</span>&lt;&lt;endl;}<br>&nbsp;&nbsp;&nbsp;&nbsp;~A() {cout&lt;&lt;<span style="COLOR: #a31515">"~A("</span>&lt;&lt;value&lt;&lt;<span style="COLOR: #a31515">") called"</span>&lt;&lt;endl;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: blue">static</span> object_pool&lt;A&gt; pool_;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: blue">void</span>* <span style="COLOR: blue">operator</span> <span style="COLOR: blue">new</span> (size_t size)<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: blue">return</span> pool_.malloc_data();<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: blue">void</span> <span style="COLOR: blue">operator</span> <span style="COLOR: blue">delete</span>(<span style="COLOR: blue">void</span>* p)<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pool_.free_data(p);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>}; <br><br>object_pool&lt;A&gt; A::pool_;<br><br><span style="COLOR: blue">void</span> main()<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;A* a1=<span style="COLOR: blue">new</span> A(1);<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: blue">delete</span> a1;<br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;A* a2=<span style="COLOR: blue">new</span> A(2);<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: blue">delete</span> a2;<br>} </span></p>
<img src ="http://www.cppblog.com/dawnbreak/aggbug/76229.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/dawnbreak/" target="_blank">pear_li</a> 2009-03-11 15:33 <a href="http://www.cppblog.com/dawnbreak/articles/76229.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++对象内存模型</title><link>http://www.cppblog.com/dawnbreak/articles/76224.html</link><dc:creator>pear_li</dc:creator><author>pear_li</author><pubDate>Wed, 11 Mar 2009 06:36:00 GMT</pubDate><guid>http://www.cppblog.com/dawnbreak/articles/76224.html</guid><wfw:comment>http://www.cppblog.com/dawnbreak/comments/76224.html</wfw:comment><comments>http://www.cppblog.com/dawnbreak/articles/76224.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/dawnbreak/comments/commentRss/76224.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/dawnbreak/services/trackbacks/76224.html</trackback:ping><description><![CDATA[<div class=content>
<p>由于种种原因，我们无法保证所有通过new在heap中申请的内存资源都安全地得到释放，例如：</p>
<p class=code><code>class&nbsp;base{...};<br>void&nbsp;f()<br>{<br>&nbsp;&nbsp;&nbsp;base*&nbsp;ptr&nbsp;=&nbsp;new&nbsp;base;<br>&nbsp;&nbsp;&nbsp;...<br>&nbsp;&nbsp;&nbsp;delete&nbsp;ptr;<br>}<br></code></p>
<p>这样一系列操作中，在"..."中可能会抛出异常，最终导致delete无法得到执行，于是就造成了内存泄露。尽管我们可以设法避免在"..."中抛出异常，但是，对于后来维护和修改这段代码了人员来说，很可能加入一些控制流，从而过早地从函数从中返回，内存泄露再次发生。</p>
<p>一种解决之道就是，将资源封装在一个类中，利用类的析构函数来释放该资源，这样一来，无论是何种方式退出f()函数的作用域，类的析构函数总是会被调用，从而有效地避免了资源泄露。</p>
<p>auto_ptr就是这种方法的一种典型应用。它被称为智能指针，是一个模板类。声明一个只能指针的方法是：auto_ptr<t> ptr(new T);为了说明auto_ptr的用法，下面是一个具体的例子：</p>
<p class=code><code>#include&nbsp;&lt;iostream&gt;<br>#include&nbsp;&lt;memory&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//为了使用auto_ptr你必须包含头文件:#include&nbsp;&lt;memory&gt;<br>using&nbsp;namespace&nbsp;std;<br><br>class&nbsp;demo<br>{<br>public:<br>&nbsp;&nbsp;demo&nbsp;()<br>&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;cout&nbsp;&lt;&lt;&nbsp;"demo()"&nbsp;&lt;&lt;&nbsp;endl;<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;~demo&nbsp;()<br>&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;cout&nbsp;&lt;&lt;&nbsp;"~demo()"&nbsp;&lt;&lt;&nbsp;endl;<br>&nbsp;&nbsp;}<br>};<br>void<br>test_org&nbsp;()<br>{<br>&nbsp;&nbsp;cout&nbsp;&lt;&lt;&nbsp;"test_org():"&nbsp;&lt;&lt;&nbsp;endl;<br>&nbsp;&nbsp;demo&nbsp;*ptr&nbsp;=&nbsp;new&nbsp;demo;<br>&nbsp;&nbsp;return;<br>}<br><br>void<br>test_auto&nbsp;()<br>{<br>&nbsp;&nbsp;cout&nbsp;&lt;&lt;&nbsp;"test_auto():"&nbsp;&lt;&lt;&nbsp;endl;<br>&nbsp;&nbsp;auto_ptr&nbsp;&lt;&nbsp;demo&nbsp;&gt;&nbsp;ptr&nbsp;(new&nbsp;demo);<br>&nbsp;&nbsp;return;<br>}<br><br>int<br>main&nbsp;(int&nbsp;narg,&nbsp;char&nbsp;**arg)<br>{<br>&nbsp;&nbsp;test_org&nbsp;();<br>&nbsp;&nbsp;test_auto&nbsp;();<br>&nbsp;&nbsp;return&nbsp;0;<br>}<br></code></p>
<p>linux下，g++编译器的输出结果如下：<br>~$ g++ test.cpp -o test<br>~$ ./test<br>test_org():<br>demo()<br>test_auto():<br>demo()<br>~demo()<br></p>
<p class=cloudreamHelperLink style="DISPLAY: none" codetype="post" entryid="306"></p>
</div>
<img src ="http://www.cppblog.com/dawnbreak/aggbug/76224.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/dawnbreak/" target="_blank">pear_li</a> 2009-03-11 14:36 <a href="http://www.cppblog.com/dawnbreak/articles/76224.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>如何检测内存泄漏——重载new和delete</title><link>http://www.cppblog.com/dawnbreak/articles/76223.html</link><dc:creator>pear_li</dc:creator><author>pear_li</author><pubDate>Wed, 11 Mar 2009 06:33:00 GMT</pubDate><guid>http://www.cppblog.com/dawnbreak/articles/76223.html</guid><wfw:comment>http://www.cppblog.com/dawnbreak/comments/76223.html</wfw:comment><comments>http://www.cppblog.com/dawnbreak/articles/76223.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/dawnbreak/comments/commentRss/76223.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/dawnbreak/services/trackbacks/76223.html</trackback:ping><description><![CDATA[版权申明 <br>本文可以被自由转载，但是必须遵循如下版权约定： <br>1、保留本约定，并保留在文章的开头部分。 <br>2、不能任意修改文章内容，或者删节，增加。如果认为本文内容有不当之处需要修改，请 <br>与作者联系。 <br>3、不能摘抄本文的内容，必须全文发表或者引用。 <br>4、必须保留作者署名、注明文章出处。（本文授权给www.linuxaid.com.cn） <br>5、如不遵守本规定，则无权转载本文。&nbsp; <br>作者 <br>ariesram <br>电子邮件地址 <br>ariesram@linuxaid.com.cn,&nbsp;或&nbsp;ariesram@may10.ca <br>本文及本人所有文章均收集在bambi.may10.ca/~ariesram/articles/中。 <br>本文授权给www.linuxaid.com.cn。 <br><br>正文： <br>我曾经参与过一个比较大的项目，在这个项目里面，我们没有一个完全确定的设计文档，所以程序的实现常常变动。虽然我们有一个比较灵活的框架，但是从程序的角度来讲，它使我们的程序非常的混乱。直到发布的日期临近，我们还没有一个稳定的可以用来做alpha测试的版本。所以我们必须尽快的删除掉无用的代码，让这个版本足够的稳定。但是，在这个没有足够规范的软件公司，我们没有时间也没有足够的精力来做边界测试之类的工作。所以我们只能采用变通的办法。在软件中最大的问题就是内存泄漏。因为往往出现这样的情况，我们在一段代码中分配了内存，但是却没有释放它。这造成了很大的问题。我们需要一个简单的解决方案，能够简单的编译进这个项目，在运行的时候，它能够产生一个没有被释放的内存的列表，用这个列表，我们能够改正程序的错误。这就是我们称之为内存跟踪的方法。首先，我们需要一种代码，能够被加入到源代码中去，而且这种代码能够被重用。代码重用是一种很重要的特性，能够节省大量的时间和金钱以及程序员的劳动。另外，我们的这种代码必须简单，因为我们当时已经没有那么多的时间和精力去完全重看一遍所有的代码来重新编写以及改正错误从而使内存跟踪能够起作用。 <br><br>好在，我们总能够找到解决的办法。首先，我们检查了代码，发现所有的代码都是用new来分配内存，用delete来释放内存。那么，我们能够用一个全程替换，来替换掉所有的new和delete操作符吗？不能。因为代码的规模太大了，那样做除了浪费时间没有别的任何好处。好在我们的源代码是用C++来写成的，所以，这意味着没有必要替换掉所有的new和delete,而只用重载这两个操作符。对了，值用重载这两个操作符，我们就能在分配和释放内存之前做点什么。这是一个绝对的好消息。我们也知道该如何去做。因为，MFC也是这么做的。我们需要做的是：跟踪所有的内存分配和交互引用以及内存释放。我们的源代码使用Visual&nbsp;C++写成，当然这种解决方法也可以很轻松的使用在别的C++代码里面。要做的第一件事情是重载new和delete操作符，它们将会在所有的代码中被使用到。我们在stdafx.h中，加入： <br>#ifdef&nbsp;_DEBUG <br>inline&nbsp;void&nbsp;*&nbsp;__cdecl&nbsp;operator&nbsp;new(unsigned&nbsp;int&nbsp;size,&nbsp; <br>const&nbsp;char&nbsp;*file,&nbsp;int&nbsp;line) <br>{ <br>}; <br><br>inline&nbsp;void&nbsp;__cdecl&nbsp;operator&nbsp;delete(void&nbsp;*p) <br>{ <br>}; <br>#endif <br>这样，我们就重载了new和delete操作符。我们用$ifdef和#endif来包住这两个重载操作符，这样，这两个操作符就不会在发布版本中出现。看一看这段代码，会发现，new操作符有三个参数，它们是，分配的内存大小，出现的文件名，和行号。这对于寻找内存泄漏是必需的和重要的。否则，就会需要很多时间去寻找它们出现的确切地方。加入了这段代码，我们的调用new()的代码仍然是指向只接受一个参数的new操作符，而不是这个接受三个参数的操作符。另外，我们也不想记录所有的new操作符的语句去包含__FILE__和__LINE__参数。我们需要做的是自动的让所有的接受一个参数的new操作符调用接受三个参数的new操作符。这一点可以用一点点小的技巧去做，例如下面的这一段宏定义， <br>#ifdef&nbsp;_DEBUG <br>#define&nbsp;DEBUG_NEW&nbsp;new(__FILE__,&nbsp;__LINE__) <br>#else <br>#define&nbsp;DEBUG_NEW&nbsp;new <br>#endif <br>#define&nbsp;new&nbsp;DEBUG_NEW <br>现在我们所有的接受一个参数的new操作符都成为了接受三个参数的new操作符号，__FILE__和__LINE__被预编译器自动的插入到其中了。然后，就是作实际的跟踪了。我们需要加入一些例程到我们的重载的函数中去，让它们能够完成分配内存和释放内存的工作。这样来做，&nbsp;#ifdef&nbsp;_DEBUG <br>inline&nbsp;void&nbsp;*&nbsp;__cdecl&nbsp;operator&nbsp;new(unsigned&nbsp;int&nbsp;size, <br>const&nbsp;char&nbsp;*file,&nbsp;int&nbsp;line) <br>{ <br>void&nbsp;*ptr&nbsp;=&nbsp;(void&nbsp;*)malloc(size); <br>AddTrack((DWORD)ptr,&nbsp;size,&nbsp;file,&nbsp;line); <br>return(ptr); <br>}; <br>inline&nbsp;void&nbsp;__cdecl&nbsp;operator&nbsp;delete(void&nbsp;*p) <br>{ <br>RemoveTrack((DWORD)p); <br>free(p); <br>}; <br>#endif <br>另外，还需要用相同的方法来重载new[]和delete[]操作符。这里就省略掉它们了。 <br>最后，我们需要提供一套函数AddTrack()和RemoveTrack()。我用STL来维护存储内存分配记录的连接表。 <br>这两个函数如下： <br>typedef&nbsp;struct&nbsp;{ <br>DWORD&nbsp;address; <br>DWORD&nbsp;size; <br>char&nbsp;file[64]; <br>DWORD&nbsp;line; <br>}&nbsp;ALLOC_INFO; <br><br>typedef&nbsp;list&lt;ALLOC_INFO*&gt;&nbsp;AllocList; <br><br>AllocList&nbsp;*allocList; <br><br>void&nbsp;AddTrack(DWORD&nbsp;addr,&nbsp;DWORD&nbsp;asize,&nbsp;const&nbsp;char&nbsp;*fname,&nbsp;DWORD&nbsp;lnum) <br>{ <br>ALLOC_INFO&nbsp;*info; <br><br>if(!allocList)&nbsp;{ <br>allocList&nbsp;=&nbsp;new(AllocList); <br>} <br><br>info&nbsp;=&nbsp;new(ALLOC_INFO); <br>info-&gt;address&nbsp;=&nbsp;addr; <br>strncpy(info-&gt;file,&nbsp;fname,&nbsp;63); <br>info-&gt;line&nbsp;=&nbsp;lnum; <br>info-&gt;size&nbsp;=&nbsp;asize; <br>allocList-&gt;insert(allocList-&gt;begin(),&nbsp;info); <br>}; <br><br>void&nbsp;RemoveTrack(DWORD&nbsp;addr) <br>{ <br>AllocList::iterator&nbsp;i; <br><br>if(!allocList) <br>return; <br>for(i&nbsp;=&nbsp;allocList-&gt;begin();&nbsp;i&nbsp;!=&nbsp;allocList-&gt;end();&nbsp;i++) <br>{ <br>if((*i)-&gt;address&nbsp;==&nbsp;addr) <br>{ <br>allocList-&gt;remove((*i)); <br>break; <br>} <br>} <br>}; <br>现在，在我们的程序退出之前，allocList存储了没有被释放的内存分配。为了看到它们是什么和在哪里被分配的，我们需要打印出allocList中的数据。我使用了Visual&nbsp;C++中的Output窗口来做这件事情。 <br>void&nbsp;DumpUnfreed() <br>{ <br>AllocList::iterator&nbsp;i; <br>DWORD&nbsp;totalSize&nbsp;=&nbsp;0; <br>char&nbsp;buf[1024]; <br><br>if(!allocList) <br>return; <br><br>for(i&nbsp;=&nbsp;allocList-&gt;begin();&nbsp;i&nbsp;!=&nbsp;allocList-&gt;end();&nbsp;i++)&nbsp;{ <br>sprintf(buf,&nbsp;"%-50s:&nbsp;LINE&nbsp;%d,&nbsp;ADDRESS&nbsp;%d&nbsp;%d&nbsp;unfreed&nbsp;", <br>(*i)-&gt;file,&nbsp;(*i)-&gt;line,&nbsp;(*i)-&gt;address,&nbsp;(*i)-&gt;size); <br>OutputDebugString(buf); <br>totalSize&nbsp;+=&nbsp;(*i)-&gt;size; <br>} <br>sprintf(buf,&nbsp;"-----------------------------------------------------------&nbsp;"); <br>OutputDebugString(buf); <br>sprintf(buf,&nbsp;"Total&nbsp;Unfreed:&nbsp;%d&nbsp;bytes&nbsp;",&nbsp;totalSize); <br>OutputDebugString(buf); <br>}; <br>现在我们就有了一个可以重用的代码，用来监测跟踪所有的内存泄漏了。这段代码可以用来加入到所有的项目中去。虽然它不会让你的程序看起来更好，但是起码它能够帮助你检查错误，让程序更加的稳定。
<img src ="http://www.cppblog.com/dawnbreak/aggbug/76223.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/dawnbreak/" target="_blank">pear_li</a> 2009-03-11 14:33 <a href="http://www.cppblog.com/dawnbreak/articles/76223.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++的类型转换符：static_cast、dynamic_cast、reinterpret_cast、和const_cast 很全面</title><link>http://www.cppblog.com/dawnbreak/articles/68122.html</link><dc:creator>pear_li</dc:creator><author>pear_li</author><pubDate>Fri, 28 Nov 2008 17:53:00 GMT</pubDate><guid>http://www.cppblog.com/dawnbreak/articles/68122.html</guid><wfw:comment>http://www.cppblog.com/dawnbreak/comments/68122.html</wfw:comment><comments>http://www.cppblog.com/dawnbreak/articles/68122.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/dawnbreak/comments/commentRss/68122.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/dawnbreak/services/trackbacks/68122.html</trackback:ping><description><![CDATA[<div class="postText">
<p style="font-family: 新宋体;"><font size="2">使用标准C++的类型转换符：static_cast、dynamic_cast、reinterpret_cast、和const_cast。</font></p>
<p style="font-family: 新宋体;"><font size="2">3.1 static_cast<br>用法：static_cast &lt; type-id &gt; ( expression )&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>该运算符把expression转换为type-id类型，但没有运行时类型检查来保证转换的安全性。它主要有如下几种用法：<br>①用于类层次结构中基类和子类之间指针或引用的转换。<br>　　进行上行转换（把子类的指针或引用转换成基类表示）是安全的；<br>　　进行下行转换（把基类指针或引用转换成子类表示）时，由于没有动态类型检查，所以是不安全的。<br>②用于基本数据类型之间的转换，如把int转换成char，把int转换成enum。这种转换的安全性也要开发人员来保证。<br>③把空指针转换成目标类型的空指针。<br>④把任何类型的表达式转换成void类型。</font></p>
<p style="font-family: 新宋体;"><font size="2">注意：static_cast不能转换掉expression的const、volitale、或者__unaligned属性。</font></p>
<p style="font-family: 新宋体;"><br><font size="2">3.2 dynamic_cast<br>用法：dynamic_cast &lt; type-id &gt; ( expression )<br>该运算符把expression转换成type-id类型的对象。Type-id必须是类的指针、类的引用或者void *；<br>如果type-id是类指针类型，那么expression也必须是一个指针，如果type-id是一个引用，那么expression也必须是一个引用。</font></p>
<p style="font-family: 新宋体;"><font size="2">dynamic_cast主要用于类层次间的上行转换和下行转换，还可以用于类之间的交叉转换。<br>在类层次间进行上行转换时，dynamic_cast和static_cast的效果是一样的；<br>在进行下行转换时，dynamic_cast具有类型检查的功能，比static_cast更安全。<br>class B{<br>public:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int m_iNum;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; virtual void foo();<br>};</font></p>
<p style="font-family: 新宋体;"><font size="2">class D:public B{<br>&nbsp;&nbsp;&nbsp; public:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char *m_szName[100];<br>};</font></p>
<p style="font-family: 新宋体;"><font size="2">void func(B *pb){<br>&nbsp;&nbsp;&nbsp; D *pd1 = static_cast&lt;D *&gt;(pb);<br>&nbsp;&nbsp;&nbsp; D *pd2 = dynamic_cast&lt;D *&gt;(pb);<br>}</font></p>
<p style="font-family: 新宋体;"><font size="2">在上面的代码段中，如果pb指向一个D类型的对象，pd1和pd2是一样的，并且对这两个指针执行D类型的任何操作都是安全的；<br>但是，如果pb指向的是一个B类型的对象，那么pd1将是一个指向该对象的指针，对它进行D类型的操作将是不安全的（如访问m_szName），<br>而pd2将是一个空指针。</font></p>
<p style="font-family: 新宋体;"><font size="2">另外要注意：B要有虚函数，否则会编译出错；static_cast则没有这个限制。<br>这是由于运行时类型检查需要运行时类型信息，而这个信息存储在类的虚函数表（<br>关于虚函数表的概念，详细可见&lt;Inside c++ object model&gt;）中，只有定义了虚函数的类才有虚函数表，<br>没有定义虚函数的类是没有虚函数表的。</font></p>
<p style="font-family: 新宋体;"><font size="2">另外，dynamic_cast还支持交叉转换（cross cast）。如下代码所示。<br>class A{<br>public:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int m_iNum;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; virtual void f(){}<br>};</font></p>
<p style="font-family: 新宋体;"><font size="2">class B:public A{<br>};</font></p>
<p style="font-family: 新宋体;"><font size="2">class D:public A{<br>};</font></p>
<p style="font-family: 新宋体;"><font size="2">void foo(){<br>&nbsp;&nbsp;&nbsp; B *pb = new B;<br>&nbsp;&nbsp;&nbsp; pb-&gt;m_iNum = 100;</font></p>
<p style="font-family: 新宋体;"><font size="2">&nbsp;&nbsp;&nbsp; D *pd1 = static_cast&lt;D *&gt;(pb);&nbsp;&nbsp;&nbsp; //compile error<br>&nbsp;&nbsp;&nbsp; D *pd2 = dynamic_cast&lt;D *&gt;(pb);&nbsp; //pd2 is NULL<br>&nbsp;&nbsp;&nbsp; delete pb;<br>}</font></p>
<p style="font-family: 新宋体;"><font size="2">在函数foo中，使用static_cast进行转换是不被允许的，将在编译时出错；而使用 dynamic_cast的转换则是允许的，结果是空指针。</font></p>
<p style="font-family: 新宋体;"><br><font size="2">3.3 reinpreter_cast<br>用法：reinpreter_cast&lt;type-id&gt; (expression)<br>type-id必须是一个指针、引用、算术类型、函数指针或者成员指针。<br>它可以把一个指针转换成一个整数，也可以把一个整数转换成一个指针（先把一个指针转换成一个整数，<br>在把该整数转换成原类型的指针，还可以得到原先的指针值）。</font></p>
<p style="font-family: 新宋体;"><font size="2">该运算符的用法比较多。</font></p>
<p style="font-family: 新宋体;"><font size="2">3.4 const_cast <br>用法：const_cast&lt;type_id&gt; (expression)<br>该运算符用来修改类型的const或volatile属性。除了const 或volatile修饰之外， type_id和expression的类型是一样的。<br>常量指针被转化成非常量指针，并且仍然指向原来的对象；<br>常量引用被转换成非常量引用，并且仍然指向原来的对象；常量对象被转换成非常量对象。</font></p>
<p style="font-family: 新宋体;"><font size="2">Voiatile和const类试。举如下一例：<br>class B{<br>public:<br>&nbsp;&nbsp;&nbsp;&nbsp; int m_iNum;<br>}<br>void foo(){<br>&nbsp;const B b1;<br>&nbsp;b1.m_iNum = 100;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //comile error<br>&nbsp;B b2 = const_cast&lt;B&gt;(b1);<br>&nbsp;b2. m_iNum = 200;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //fine<br>}<br>上面的代码编译时会报错，因为b1是一个常量对象，不能对它进行改变；<br>使用const_cast把它转换成一个常量对象，就可以对它的数据成员任意改变。注意：b1和b2是两个不同的对象。</font></p>
</div><img src ="http://www.cppblog.com/dawnbreak/aggbug/68122.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/dawnbreak/" target="_blank">pear_li</a> 2008-11-29 01:53 <a href="http://www.cppblog.com/dawnbreak/articles/68122.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>