??xml version="1.0" encoding="utf-8" standalone="yes"?>精产国品久久一二三产区区别,免费观看久久精彩视频,区亚洲欧美一级久久精品亚洲精品成人网久久久久 http://www.shnenglu.com/dawnbreak/category/8913.htmlPearLi's Blogzh-cnSat, 15 Aug 2009 13:02:13 GMTSat, 15 Aug 2009 13:02:13 GMT60OnPaint()函数的作用原?/title><link>http://www.shnenglu.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.shnenglu.com/dawnbreak/articles/93425.html</guid><wfw:comment>http://www.shnenglu.com/dawnbreak/comments/93425.html</wfw:comment><comments>http://www.shnenglu.com/dawnbreak/articles/93425.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/dawnbreak/comments/commentRss/93425.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/dawnbreak/services/trackbacks/93425.html</trackback:ping><description><![CDATA[<font color=#000099 face=q圆>WM_PAINT是窗口每ơ重l都?x)生的一个消息?/font> <p><font style="FONT-FAMILY: q圆; FONT-SIZE: 16px" color=#000099>OnPaint是对q个消息的反应函?/font></p> <p><font style="FONT-FAMILY: q圆; FONT-SIZE: 16px" color=#000099> <wbr></font></p> <p><font style="FONT-FAMILY: q圆; FONT-SIZE: 16px" color=#000099>mfc ?CWnd::OnPaint 没做什么,只是丢给pȝ处理?/font></p> <p><font style="FONT-FAMILY: q圆; FONT-SIZE: 20px" color=#000099><strong>一 Q?/strong></font></p> <p><font style="FONT-FAMILY: q圆" color=#000099> <wbr> <wbr> 先执行OnEraseBkgndQ擦除背景(如果惌l控Ӟq个函数直接return TRUE可以了(jin)Q这样就不会(x)擦除背景Q不?x)闪Q?/font></p> <p><font style="FONT-FAMILY: q圆" color=#000099> <wbr></font></p> <p><strong><font style="FONT-FAMILY: q圆; FONT-SIZE: 18px" color=#000099>OnEraseBkGnd与OnPaint的区别与联系</font></strong></p> <p><font style="FONT-FAMILY: q圆; FONT-SIZE: 16px" color=#000099>在OnEraseBkGnd?如果你不调用原来~省的OnEraseBkGnd只是重画背景则不?x)有闪?而在<font style="BACKGROUND-COLOR: #ccff00">OnPaint里面,׃它隐含的调用?jin)OnEraseBkGnd</font>,而你又没有处理OnEraseBkGnd 函数,q时和H口~省的背景刷相关?~省?OnEraseBkGnd操作使用H口的缺省背景刷h背景(一般情况下是白?,而随后你又自己重画背景造成屏幕闪动.</font></p> <p><font style="FONT-FAMILY: q圆; FONT-SIZE: 16px" color=#000099><font style="BACKGROUND-COLOR: #ccff00">OnEraseBkGnd不是每次都会(x)被调用的</font>.如果?font style="BACKGROUND-COLOR: #ccff00">调用Invalidate</font>的时候参Cؓ(f)TRUE,那么在OnPaint里面隐含<font style="BACKGROUND-COLOR: #ccff00">调用BeginPaint</font>的时候就?font style="BACKGROUND-COLOR: #ccff00">生WM_ERASEBKGND消息</font>,如果参数是FALSE,则不?x)重刯?</font></p> <p><font style="FONT-FAMILY: q圆; FONT-SIZE: 16px" color=#000099><strong>ZYP解释Q?/strong><font color=#000000>void Invalidate( BOOL bErase = TRUE ); 该函数的作用是整个H口客户区无效。窗口的客户区无效意味着需要重l,参数bErase为TRUEӞ重绘区域内的背景被重绘x除,否则Q背景将保持不变。调用Invalidate{函数后H口不会(x)立即重绘Q这是由于WM_PAINT消息的优先很低Q它需要等消息队列中的其它消息发送完后才能被处理?/font></font></p> <p><font style="FONT-FAMILY: q圆; FONT-SIZE: 16px" color=#000099><font color=#000000><font style="BACKGROUND-COLOR: #ccff00" color=#000099>OnPaint里面?x)调?/font>BeginPaint函数自动讄昄讑֤内容的剪切区域而排除Q何更新区域外的区域更新区域。如果更新区域被标记为可擦除的,BeginPaint发送一个WM_ERASEBKGND消息l窗口。WM_ERASEBKGND消息的响应函数既?font style="BACKGROUND-COLOR: #ccff00" color=#000099>OnEraseBkGndQ)(j)</font></font><br><br>所以解x法有三个?<br>1.用OnEraseBkGnd实现,不要调用原来的OnEraseBkGnd函数.<br>2.用OnPaint实现,同时重蝲OnEraseBkGnd,其中直接q回.<br>3.用OnPaint实现,创徏H口时设|背景刷为空<br>4.用OnPaint实现,但是要求h时用Invalidate(FALSE)q样<br>的函?(不过q种情况?H口覆盖{造成的刷新还是要闪一<br>?所以不是彻底的解决Ҏ(gu))<br>都挺单的.</font></p> <table> <tbody> <tr> <td> <p><font style="FONT-FAMILY: q圆; FONT-SIZE: 16px" color=#000099>在MFC?M一個window元g的繪?都是攑֜這兩個member function?br>在設定上 OnEraseBkgnd()是用來畫底圖?而OnPaint()是用來畫主要物g?br>舉例說明 一個按鈕是灰色?上面還有文字<br>則OnEraseBkgnd()所做的事就是把按鈕畫成灰色<br>而OnPaint()所做的?是畫上文字</font></p> <p><br><font style="FONT-FAMILY: q圆; FONT-SIZE: 16px" color=#000099>既然這兩個member function都是用來畫出元g?br>那為何還要分OnPaint() ?OnEraseBkgnd() ?br>其實OnPaint() ?OnEraseBkgnd() Ҏ(gu)是有差?br>1. <wbr><wbr><wbr><wbr><wbr>OnEraseBkgnd()的要求是快速在裡面的繪圖程式最好是不要太耗時?br><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr>因為<font style="BACKGROUND-COLOR: #ccff00">每當window元g有Q何小變動都會馬上呼叫OnEraseBkgnd()</font><br>2. <wbr><wbr><wbr><wbr><wbr><font style="BACKGROUND-COLOR: #ccff33" color=#000099>OnPaint() 是只有在E式有空閒的時候才會被呼叫<br></font>3. <wbr><wbr><wbr><wbr><wbr>OnEraseBkgnd() 是在 OnPaint() 之前呼叫?br>所?OnPaint()被呼叫一ơ之?可能會呼叫OnEraseBkgnd()好幾?br><br>如果我們是一個在做圖形化使用者介面的?br>常會需要把一늾的圖片a為我們dialog的底?br>把繪圖的E式放在OnPaint() 之中 可能會常到一些問?br>比方說拖曳一個視H在我們做的dialog上面一直移?br>則dialog會變成灰?直到動作停止才恢?br>這是因為每次需要重J的時?E式都會馬上呼叫OnEraseBkgnd()<br>OnEraseBkgnd()把dialog畫成灰色<br>而只有動作停止之?E式才會呼叫OnPaint() 這時才會把我們要畫的底圖g?br><br>這個問的解法 比較差點的方法是把OnEraseBkgnd() 改寫成不做事的function<br>如下所C?/font></p> <div> <table border=1 cellSpacing=0 cellPadding=0 width="100%" height=32> <tbody> <tr> <td> <p><font style="FONT-FAMILY: q圆; 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: q圆; FONT-SIZE: 16px" color=#000099>以上本來是會呼叫CDialog::OnEraseBkgnd() 但是如果我們不呼叫的話<br>E式便不會畫上灰色的底色?/font></p> <p><font style="FONT-FAMILY: q圆; FONT-SIZE: 16px" color=#000099><font color=#000000>Q:Z对话框的E序中如何重载OnEraseBkGnd()函数</font></font></p> <p><font style="FONT-FAMILY: q圆; FONT-SIZE: 16px" color=#000099>A:<font color=#000000>q是一个消息WM_ERASEBKWND  <wbr><br> <wbr> 在CLASS  <wbr> WIZARD? <wbr><br> <wbr> 选择CLASSINFO面  <wbr><br> <wbr> 在MESSAGEFILTER中的选项讑֜WINDOW可以看到这个消息了(jin).</font><br><br>比較好的做法是直接將J圖的程式從OnPaint()UdOnEraseBkgnd()來做<br>如下所C?/font></p> <div> <table border=1 cellSpacing=0 cellPadding=0 width="100%" height=280> <tbody> <tr> <td> <p><br><font style="FONT-FAMILY: q圆; FONT-SIZE: 16px" color=#000099>// m_bmpBKGND ZCBitmap物g 且事先早已載(j)入我們的底圖<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(&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->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(),&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: q圆; FONT-SIZE: 16px" color=#000099>特別要注意的?取得重畫大小是用GetUpdateRect() 而不是GetClientRect()<br>如果使用GetClientRect() 會把不該重畫的地斚w?/font></div> <div><font style="FONT-SIZE: 16px"><wbr></font></div> <div><font style="FONT-FAMILY: q圆; 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: q圆" color=#000099> <wbr> </font> </pre> <p><strong><font style="FONT-FAMILY: q圆; FONT-SIZE: 20px" color=#000099>?Q?/font></strong></p> <p><font style="FONT-FAMILY: q圆" color=#000099> <wbr> <wbr> <wbr> <wbr> pȝ的Onpaint中调用了(jin)OnDrawQ但如果我们自己l承?jin)一个OnPaint函数又没有显式调用OnDrawQ则OnDraw׃?x)被调?OnInitialUpdate在OnDraw之前Q是H口被创Z后调用的W一个函数?/font></p> <p><font style="FONT-FAMILY: q圆" color=#000099> <wbr></font></p> <p><strong><font style="FONT-FAMILY: q圆" color=#000099 size=4>MFC中OnDraw与OnPaint的区?/font></strong></p> <p><font style="FONT-FAMILY: q圆" color=#000099>在OnPaint中调用OnDrawQ一般来_(d)用户自己的绘图代码应攑֜OnDraw中?/font></p> <p><font style="FONT-FAMILY: q圆" color=#000099 size=3>OnPaint()是CWnd的类成员Q负责响应WM_PAINT消息。OnDraw()是CVIEW的成员函敎ͼ没有响应消息的功?当视囑֏得无效时Q包括大的改变Q移动,被遮盖等{)(j)QW(xu)indows发送WM_PAINT消息。该视图的OnPaint 处理函数通过创徏CPaintDCcȝDC对象来响应该消息q调用视囄OnDraw成员函数.<font style="BACKGROUND-COLOR: #ccff00">OnPaint最后也要调用OnDraw</font>,因此一般在OnDraw函数中进行绘制?/font></p> <p><font size=3><br><font style="FONT-FAMILY: q圆" 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: q圆" color=#000099 size=3>在OnPaint中,调用BeginPaintQ用来获得客户区的显C备环境,q以此调用GDI函数执行l图操作。在l图操作完成后,调用EndPaint以释放显C备环境。而OnDraw在BeginPaint与EndPaint间被调用。(<font color=#000000>一个应用程序除?jin)响应WM_PAINT消息外,不应该调用BeginPaint。每ơ调用BeginPaint都应该有相应的EndPaint函数?/font>Q?/font></p> <p><font size=3><font style="FONT-FAMILY: q圆" color=#000099>1) 在mfcl构里OnPaint是CWnd的成员函? OnDraw是CView的成员函?<br>2) OnPaint()调用OnDraw()QOnPrint也会(x)调用OnDraw()Q所以OnDraw()是显C和打印的共同操作?br><br>OnPaint是WM_PAINT消息引发的重l消息处理函敎ͼ在OnPaint中会(x)调用OnDraw来进行绘图。OnPaint中首先构造一个CPaintDCcd实例Q然后一q个实例为参数来调用虚函数OnPrepareDC来进行一些绘制前的一些处理,比设|映模式,最后调用OnDraw。?/font><a name=baidusnap0></a><font color=#000099><font style="FONT-FAMILY: 楷体_GB2312,楷体"><font style="FONT-FAMILY: q圆"><strong style="BACKGROUND-COLOR: rgb(255,255,102); COLOR: black">OnDraw?/strong>OnPrepareDC不是消息处理函数。所以在不是因ؓ(f)重绘消息所引发的OnPaintDOnDraw被调用时Q比如在OnLButtonDown{消息处理函Cl图Ӟ要先自己调用OnPrepareDC?br>至于CPaintDC和CClientDCҎ(gu)是两回事?CPaintDC是一个设备环境类Q在OnPaint中作为参C递给OnPrepareDC来作讑֤环境的设|。真正和CClientDCh可比性的是CWindowDCQ他们一个是描述客户区域Q一个是描述整个屏幕?br>如果是对CVIEW或从CVIEWcL生的H口l图时应该用OnDraw?/font></font></font></font></p> <p><font size=3><font style="FONT-FAMILY: q圆" color=#000099>OnDraw()</font><a name=baidusnap1></a><font color=#000099><font style="FONT-FAMILY: 楷体_GB2312,楷体"><font style="FONT-FAMILY: q圆"><strong style="BACKGROUND-COLOR: rgb(160,255,255); COLOR: black">和OnPaint</strong>()有什么区别呢Q?br>首先Q我们先要明CViewcL生自CWndcR而OnPaint()是CWnd的类成员Q同时负责响应WM_PAINT消息。OnDraw()是CVIEW的成员函敎ͼq且没有响应消息的功能。这是Z么你用VC成的E序代码Ӟ在视囄只有OnDraw没有</font></font></font><a name=baidusnap2></a><font color=#000099><font style="FONT-FAMILY: 楷体_GB2312,楷体"><font style="FONT-FAMILY: q圆"><strong style="BACKGROUND-COLOR: rgb(153,255,153); COLOR: black">OnPaint?/strong>原因。而在Z对话框的E序中,只有OnPaint?br>其次Q我们在W《每天跟我学MFC?的开始部分已l说C(jin)。要惛_屏幕上绘图或昄囑ŞQ首先需要徏立设备环境DC。其实DC是一个数据结构,它包含输备(不单指你17寸的U屏昄器,q包括打印机之类的输备)(j)的绘囑ֱ性的描述。MFC提供?jin)CPaintDCcdCWindwoDCcL实时的响应,而CPaintDC支持重画。当视图变得无效Ӟ包括大小的改变,UdQ被遮盖{等Q,W(xu)indows ?WM_PAINT 消息发送给它。该视图的OnPaint 处理函数通过创徏 CPaintDC cȝDC对象来响应该消息q调用视囄 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: q圆"><font size=3>CPaintDC dc(this);<br>OnPrepareDC(&dc)Q?br>OnDraw(&dc); <wbr></font><wbr>//调用?jin)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: q圆" color=#000099 size=3>既然OnPaint最后也要调用OnDraw,因此我们一般会(x)在OnDraw函数中进行绘制。下面是一个典型的E序?br>///视图中的l图代码首先(g)索指向文档的指针Q然后通过DCq行l图调用?br>void CMyView::OnDraw( CDC* pDC )<br>{</font></p> <p><font style="FONT-FAMILY: q圆" color=#000099 size=3>CMyDoc* pDoc = GetDocument();<br>CString s = pDoc->GetData();<br>GetClientRect( &rect ); // Returns a CString CRect rect;<br>pDC->SetTextAlign( TA_BASELINE | TA_CENTER );<br>pDC->TextOut( rect.right / 2, rect.bottom / 2, s, s.GetLength() );<br>}<br>最后:(x)现在大家明白q哥俩之间的关系?jin)吧。因此我们一般用OnPaintl护H口的客户区Q例如我们的H口客户区加一个背景图片)(j)Q用OnDrawl护视图的客户区Q例如我们通过鼠标在视图中dQ。当然你也可以不按照上面规律来,只要辑ֈ目的q且没有问题Q怎么q都成。补充:(x)我们q可以利用Invalidate(),ValidateRgn(),ValidateRect()函数强制的重ȝ口,具体的请参考MSDN吧?/font></p> <p><font style="FONT-FAMILY: q圆" color=#000099 size=3>OnDraw中可以绘制用户区域。OnPaint中只是当H口无效旉l不?x)保留C(j)ClientDCl制的内宏V?/font></p> <p><font style="FONT-FAMILY: q圆" color=#000099 size=3>q两个函数有区别也有联系Q?/font></p> <p><font style="FONT-FAMILY: q圆" color=#000099 size=3>1、区别:(x)OnDraw是一个纯虚函敎ͼ定义为virtual void OnDraw( CDC* pDC ) = 0; 而OnPaint是一个消息响应函敎ͼ它响应了(jin)WMQPANIT消息Q也是是H口重绘消息?/font></p> <p><font style="FONT-FAMILY: q圆" color=#000099 size=3>2、联p:(x)我们一般在视类中作囄时候,往往不直接响应WMQPANIT消息Q而是重蝲OnDrawU虚函数Q这是因为在CVIEWcM的WMQPANIT消息响应函数中调用了(jin)OnDraw函数Q如果在CMYVIEWcM响应?jin)WMQPAINT消息Q不昑ּ地调用OnDraw函数的话Q是不会(x)在窗口重l的时候调用OnDraw函数的?/font></p> <p><font style="FONT-FAMILY: q圆" color=#000099 size=3>应用E序中几乎所有的l图都在视图?OnDraw 成员函数中发生,必须在视囄中重写该成员函数。(鼠标l图是个特例Q这在通过视图解释用户输入中讨论。)(j)</font></p> <p><font size=3><br><font color=#000099><font style="FONT-FAMILY: 楷体_GB2312,楷体"><font style="FONT-FAMILY: q圆">OnDraw 重写Q?br>通过调用(zhn)提供的文档成员函数获取数据?br>通过调用框架传递给 OnDraw 的设备上下文对象的成员函数来昄数据?br>当文档的数据以某U方式更改后Q必重l视图以反映该更攏V默认的 OnUpdate 实现使视囄整个工作区无效。当视图变得无效ӞW(xu)indows ?WM_PAINT 消息发送给它。该视图?OnPaint 处理函数通过创徏 CPaintDC cȝ讑֤上下文对象来响应该消息ƈ调用视图?OnDraw 成员函数?br><br>当没有添加WM_PAINT消息处理?H口重绘?由O(jin)nDraw来进行消息响?..当添加WM_PAINT消息处理?H口重绘?WM_PAINT消息被投?由O(jin)nPaint来进行消息响?q时׃能隐式调用OnDraw?必须昑ּ调用( <wbr></font></font></font></font><wbr><font style="FONT-FAMILY: q圆" color=#000099>CDC *pDC=GetDC(); OnDraw(pDC); <wbr><wbr>)..<br>隐式调用:当由O(jin)nPaint来进行消息响应时,pȝ自动调用CView::OnDraw(&pDC).</font></p> <p><br><font style="FONT-FAMILY: q圆" color=#000099 size=3>惌一下,H口昄的内容和打印的内Ҏ(gu)差不多的Q所以,一般情况下Q统一由O(jin)nDraw来画。窗口前景需要刷新时Q系l会(x)?x)调用到OnPaintQ而OnPaint一般情况下是对DC作一些初始化操作后,调用OnDraw()?/font></p> <p><font size=3><br><font color=#000099><font style="FONT-FAMILY: 楷体_GB2312,楷体"><font style="FONT-FAMILY: q圆">OnEraseBkGnd()Q是H口背景需要刷新时ql调用的。明昄一个例子是讄H口的背景颜Ԍ你可以把q放在OnPaint中去做,但是?x)产生闪烁的现象?j)?<wbr></font></font></font></font><wbr><br><font style="FONT-FAMILY: q圆" color=#000099>至于怎么界定背景和前景,那要具体问题具体分析?jin),一般情况下Q你q是很容易区别的吧?/font></p> <p><font style="FONT-FAMILY: q圆" color=#000099 size=3>的确QOnPaint()用来响应WM_PAINT消息Q视cȝOnPaint()内部Ҏ(gu)是打印还是屏q绘制分别以不同的参数调用OnDraw()虚函数。所以在OnDraw()里你可以区别对待打印和屏q绘制?br>其实QMFC在进行打印前后还做了(jin)很多工作Q调用了(jin)很多虚函敎ͼ比如OnPreparePrint(){?/font></p> <p><br><strong><font style="FONT-FAMILY: q圆; FONT-SIZE: 20px" color=#000099>另外OnInitialUpdate</font></strong></p> <p><font style="FONT-FAMILY: q圆; FONT-SIZE: 16px" color=#000099>视图H口完全建立后第一个被框架调用的函数。框架在W一ơ调用OnDraw前会(x)调用OnInitialUpdateQ因此OnInitialUpdate是设|滚动视囄逻辑寸和映模式的最合适的地方?/font></p> <img src ="http://www.shnenglu.com/dawnbreak/aggbug/93425.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/dawnbreak/" target="_blank">pear_li</a> 2009-08-15 14:56 <a href="http://www.shnenglu.com/dawnbreak/articles/93425.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>c++操作W重?http://www.shnenglu.com/dawnbreak/articles/92979.htmlpear_lipear_liTue, 11 Aug 2009 16:00:00 GMThttp://www.shnenglu.com/dawnbreak/articles/92979.htmlhttp://www.shnenglu.com/dawnbreak/comments/92979.htmlhttp://www.shnenglu.com/dawnbreak/articles/92979.html#Feedback0http://www.shnenglu.com/dawnbreak/comments/commentRss/92979.htmlhttp://www.shnenglu.com/dawnbreak/services/trackbacks/92979.html阅读全文

pear_li 2009-08-12 00:00 发表评论
]]>
Makefile教程http://www.shnenglu.com/dawnbreak/articles/86350.htmlpear_lipear_liMon, 01 Jun 2009 01:35:00 GMThttp://www.shnenglu.com/dawnbreak/articles/86350.htmlhttp://www.shnenglu.com/dawnbreak/comments/86350.htmlhttp://www.shnenglu.com/dawnbreak/articles/86350.html#Feedback0http://www.shnenglu.com/dawnbreak/comments/commentRss/86350.htmlhttp://www.shnenglu.com/dawnbreak/services/trackbacks/86350.html

0 Makefile概述


什么是makefileQ或许很多Winodws的程序员都不知道q个东西Q因为那些Windows的IDE都ؓ(f)你做?jin)这个工作,但我觉得要作一个好的和professional的程序员Qmakefileq是要懂。这好像现在有q么多的HTML的编辑器Q但如果你想成ؓ(f)一个专业h士,你还是要?jin)解HTML的标识的含义。特别在Unix下的软g~译Q你׃能不自己写makefile?jin),会(x)不会(x)写makefileQ从一个侧面说明了(jin)一个h是否具备完成大型工程的能力?

因ؓ(f)Qmakefile关系C(jin)整个工程的编译规则。一个工E中的源文g不计敎ͼ其按cd、功能、模块分别放在若q个目录中,makefile定义?jin)一pd的规则来指定Q哪些文仉要先~译Q哪些文仉要后~译Q哪些文仉要重新编译,甚至于进行更复杂的功能操作,因ؓ(f)makefile像一个Shell脚本一P其中也可以执行操作系l的命o(h)?

makefile带来的好处就是—?#8220;自动化编?#8221;Q一旦写好,只需要一个make命o(h)Q整个工E完全自动编译,极大的提高了(jin)软g开发的效率。make是一个命令工P是一个解释makefile中指令的命o(h)工具Q一般来_(d)大多数的IDE都有q个命o(h)Q比如:(x)Delphi的makeQVisual C++的nmakeQLinux下GNU的make。可见,makefile都成Z(jin)一U在工程斚w的编译方法?

现在讲述如何写makefile的文章比较少Q这是我惛_q篇文章的原因。当?dng)不同产商的make各不相同Q也有不同的语法Q但其本质都是在“文g依赖?#8221;上做文章Q这里,我仅对GNU的makeq行讲述Q我的环境是RedHat Linux 8.0Qmake的版本是3.80。必竟,q个make是应用最为广泛的Q也是用得最多的。而且其还是最遵@于IEEE 1003.2-1992 标准的(POSIX.2Q?

在这文档中Q将以C/C++的源码作为我们基Q所以必然涉?qing)一些关于C/C++的编译的知识Q相关于q方面的内容Q还请各位查看相关的~译器的文档。这里所默认的编译器是UNIX下的GCC和CC?

0.1 关于E序的编译和链接

在此Q我惛_说关于程序编译的一些规范和Ҏ(gu)Q一般来_(d)无论是C、C++、还是pasQ首先要把源文g~译成中间代码文Ӟ在Windows下也是 .obj 文gQUNIX下是 .o 文gQ即 Object FileQ这个动作叫做编译(compileQ。然后再把大量的Object File合成执行文gQ这个动作叫作链接(linkQ?

~译Ӟ~译器需要的是语法的正确Q函C变量的声明的正确。对于后者,通常是你需要告诉编译器头文件的所在位|(头文件中应该只是声明Q而定义应该放在C/C++文g中)(j)Q只要所有的语法正确Q编译器可以编译出中间目标文g。一般来_(d)每个源文仉应该对应于一个中间目标文ӞO文g或是OBJ文gQ?

链接Ӟ主要是链接函数和全局变量Q所以,我们可以使用q些中间目标文gQO文g或是OBJ文gQ来链接我们的应用程序。链接器q不函数所在的源文Ӟ只管函数的中间目标文ӞObject FileQ,在大多数时候,׃源文件太多,~译生成的中间目标文件太多,而在链接旉要明昑֜指出中间目标文g名,q对于编译很不方便,所以,我们要给中间目标文g打个包,在Windows下这U包?#8220;库文?#8221;QLibrary File)Q也是 .lib 文gQ在UNIX下,是Archive FileQ也是 .a 文g?

ȝ一下,源文仉先会(x)生成中间目标文gQ再׃间目标文件生成执行文件。在~译Ӟ~译器只(g)程序语法,和函数、变量是否被声明。如果函数未被声明,~译器会(x)l出一个警告,但可以生成Object File。而在链接E序Ӟ链接器会(x)在所有的Object File中找d数的实现Q如果找不到Q那到就?x)报链接错误码(Linker ErrorQ,在VC下,q种错误一般是QLink 2001错误Q意思说是说Q链接器未能扑ֈ函数的实现。你需要指定函数的Object File.

好,a归正传,GNU的make有许多的内容Q闲a叙Q还是让我们开始吧?

1 Makefile 介绍


make命o(h)执行Ӟ需要一?Makefile 文gQ以告诉make命o(h)需要怎么L(fng)ȝ译和链接E序?

首先Q我们用一个示例来说明Makefile的书写规则。以便给大家一个感兴认识。这个示例来源于GNU的make使用手册Q在q个CZ中,我们的工E有8个C文gQ和3个头文gQ我们要写一个Makefile来告诉make命o(h)如何~译和链接这几个文g。我们的规则是:(x)

  1. 如果q个工程没有~译q,那么我们的所有C文g都要~译q被链接?
  2. 如果q个工程的某几个C文g被修改,那么我们只编译被修改的C文gQƈ链接目标E序?
  3. 如果q个工程的头文g被改变了(jin)Q那么我们需要编译引用了(jin)q几个头文g的C文gQƈ链接目标E序?

只要我们的Makefile写得够好Q所有的q一切,我们只用一个make命o(h)可以完成,make命o(h)?x)自动智能地?gu)当前的文件修改的情况来确定哪些文仉要重~译Q从而自q译所需要的文g和链接目标程序?

1.1 Makefile的规?

在讲q这个Makefile之前Q还是让我们先来_略地看一看Makefile的规则?
target ... : prerequisites ...
command
...
...
target也就是一个目标文Ӟ可以是Object FileQ也可以是执行文件。还可以是一个标{(LabelQ,对于标签q种Ҏ(gu),在后l的“伪目?#8221;章节中会(x)有叙q?

prerequisites是Q要生成那个target所需要的文g或是目标?

command也就是make需要执行的命o(h)。(L的Shell命o(h)Q?

q是一个文件的依赖关系Q也是_(d)targetq一个或多个的目标文件依赖于prerequisites中的文gQ其生成规则定义在command中。说白一点就是说Qprerequisites中如果有一个以上的文g比target文g要新的话Qcommand所定义的命令就?x)被执行。这是Makefile的规则。也是Makefile中最核心(j)的内宏V?

说到底,Makefile的东西就是这样一点,好像我的q篇文档也该l束?jin)。呵c(din)还不尽?dng)q是Makefile的主U和核心(j)Q但要写好一个Makefileq不够,我会(x)以后面一点一点地l合我的工作l验l你慢慢到来。内容还多着呢。:(x)Q?

1.2 一个示?

正如前面所说的Q如果一个工E有3个头文gQ和8个C文gQ我们ؓ(f)?jin)完成前面所q的那三个规则,我们的Makefile应该是下面的q个样子的?
    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
反斜杠(\Q是换行W的意思。这h较便于Makefile的易诅R我们可以把q个内容保存在文件ؓ(f)“Makefile”?#8220;makefile”的文件中Q然后在该目录下直接输入命o(h)“make”可以生成执行文件edit。如果要删除执行文g和所有的中间目标文gQ那么,只要单地执行一?#8220;make clean”可以了(jin)?

在这个makefile中,目标文gQtargetQ包含:(x)执行文gedit和中间目标文Ӟ*.oQ,依赖文gQprerequisitesQ就是冒号后面的那些 .c 文g?.h文g。每一?.o 文g都有一l依赖文Ӟ而这?.o 文g又是执行文g edit 的依赖文件。依赖关pȝ实质上就是说明了(jin)目标文g是由哪些文g生成的,换言之,目标文g是哪些文件更新的?

在定义好依赖关系后,后箋的那一行定义了(jin)如何生成目标文g的操作系l命令,一定要以一个Tab键作为开头。记住,makeq不命令是怎么工作的,他只执行所定义的命令。make?x)比较targets文g和prerequisites文g的修Ҏ(gu)期,如果prerequisites文g的日期要比targets文g的日期要斎ͼ或者target不存在的话,那么Qmake׃(x)执行后箋定义的命令?

q里要说明一点的是,clean不是一个文Ӟ它只不过是一个动作名字,有点像C语言中的lable一P其冒号后什么也没有Q那么,make׃?x)自动去找文件的依赖性,也就不会(x)自动执行其后所定义的命令。要执行其后的命令,p在make命o(h)后明昑־指出q个lable的名字。这L(fng)Ҏ(gu)非常有用Q我们可以在一个makefile中定义不用的~译或是和编译无关的命o(h)Q比如程序的打包Q程序的备䆾Q等{?

1.3 make是如何工作的

在默认的方式下,也就是我们只输入make命o(h)。那么,

  1. make?x)在当前目录下找名字?#8220;Makefile”?#8220;makefile”的文件?
  2. 如果扑ֈQ它?x)找文g中的W一个目标文ӞtargetQ,在上面的例子中,他会(x)扑ֈ“edit”q个文gQƈ把这个文件作为最l的目标文g?
  3. 如果edit文g不存在,或是edit所依赖的后面的 .o 文g的文件修Ҏ(gu)间要比editq个文g斎ͼ那么Q他׃(x)执行后面所定义的命令来生成editq个文g?
  4. 如果edit所依赖?o文g也存在,那么make?x)在当前文g中找目标?o文g的依赖性,如果扑ֈ则再Ҏ(gu)那一个规则生?o文g。(q有点像一个堆栈的q程Q?
  5. 当然Q你的C文g和H文g是存在的啦,于是make?x)生?.o 文gQ然后再?.o 文g生命make的终极Q务,也就是执行文件edit?jin)?

q就是整个make的依赖性,make?x)一层又一层地L文g的依赖关p,直到最l编译出W一个目标文件。在扑֯的过E中Q如果出现错误,比如最后被依赖的文件找不到Q那么make׃(x)直接退出,q报错,而对于所定义的命令的错误Q或是编译不成功QmakeҎ(gu)不理。make只管文g的依赖性,卻I如果在我找了(jin)依赖关系之后Q冒号后面的文gq是不在Q那么对不vQ我׃工作啦?

通过上述分析Q我们知道,像cleanq种Q没有被W一个目标文件直接或间接兌Q那么它后面所定义的命令将不会(x)被自动执行,不过Q我们可以显Cmake执行。即命o(h)—?#8220;make clean”Q以此来清除所有的目标文gQ以侉K~译?

于是在我们编E中Q如果这个工E已被编译过?jin),当我们修改?jin)其中一个源文gQ比如file.cQ那么根据我们的依赖性,我们的目标file.o?x)被重编译(也就是在q个依性关pd面所定义的命令)(j)Q于是file.o的文件也是最新的啦,于是file.o的文件修Ҏ(gu)间要比edit要新Q所以edit也会(x)被重新链接了(jin)Q详见edit目标文g后定义的命o(h)Q?

而如果我们改变了(jin)“command.h”Q那么,kdb.o、command.o和files.o都会(x)被重~译Qƈ且,edit?x)被重链接?

1.4 makefile中用变?

在上面的例子中,先让我们看看edit的规则:(x)
      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
我们可以看到[.o]文g的字W串被重复了(jin)两次Q如果我们的工程需要加入一个新的[.o]文gQ那么我们需要在两个地方加(应该是三个地方,q有一个地方在clean中)(j)。当?dng)我们的makefileq不复杂Q所以在两个地方加也不篏Q但如果makefile变得复杂Q那么我们就有可能会(x)忘掉一个需要加入的地方Q而导致编译失败。所以,Z(jin)makefile的易l护Q在makefile中我们可以用变量。makefile的变量也是一个字W串Q理解成C语言中的宏可能会(x)更好?

比如Q我们声明一个变量,叫objects, OBJECTS, objs, OBJS, obj, 或是 OBJQ反正不什么啦Q只要能够表Cobj文gp?jin)。我们在makefile一开始就q样定义Q?

     objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
于是Q我们就可以很方便地在我们的makefile中以“$(objects)”的方式来使用q个变量?jin),于是我们的改良版makefile变成下面这个样子:(x)
    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)

于是如果有新?.o 文g加入Q我们只需单地修改一?objects 变量可以了(jin)?

关于变量更多的话题,我会(x)在后l给你一一道来?

1.5 让make自动推导

GNU的make很强大,它可以自动推导文件以?qing)文件依赖关pd面的命o(h)Q于是我们就没必要去在每一个[.o]文g后都写上cM的命令,因ؓ(f)Q我们的make?x)自动识别,q自己推导命令?

只要make看到一个[.o]文gQ它?yu)׃?x)自动的把[.c]文g加在依赖关系中,如果make扑ֈ一个whatever.oQ那么whatever.cQ就?x)是whatever.o的依赖文件。ƈ?cc -c whatever.c 也会(x)被推导出来,于是Q我们的makefile再也不用写得q么复杂。我们的是新的makefile又出炉了(jin)?

    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)
q种Ҏ(gu)Q也是make?#8220;隐晦规则”。上面文件内容中Q?#8220;.PHONY”表示Qclean是个伪目标文件?

关于更ؓ(f)详细?#8220;隐晦规则”?#8220;伪目标文?#8221;Q我?x)在后箋l你一一道来?

1.6 另类风格的makefile

即然我们的make可以自动推导命o(h)Q那么我看到那堆[.o]和[.h]的依赖就有点不爽Q那么多的重复的[.h]Q能不能把其收拢hQ好吧,没有问题Q这个对于make来说很容易,谁叫它提供了(jin)自动推导命o(h)和文件的功能呢?来看看最新风格的makefile吧?
    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)
q种风格Q让我们的makefile变得很简单,但我们的文g依赖关系显得有点凌׃(jin)。鱼和熊掌不可兼得。还看你的喜好了(jin)。我是不喜欢q种风格的,一是文件的依赖关系看不清楚Q二是如果文件一多,要加入几个新?o文gQ那q不清楚了(jin)?

1.7 清空目标文g的规?

每个Makefile中都应该写一个清I目标文Ӟ.o和执行文Ӟ(j)的规则,q不仅便于重~译Q也很利于保持文件的清洁。这是一?#8220;修养”Q呵呵,q记得我的《编E修充R吗Q。一般的风格都是Q?
        clean:
rm edit $(objects)
更ؓ(f)E_的做法是Q?
        .PHONY : clean
clean :
-rm edit $(objects)
前面说过Q?PHONY意思表Cclean是一?#8220;伪目?#8221;Q。而在rm命o(h)前面加了(jin)一个小减号的意思就是,也许某些文g出现问题Q但不要,l箋做后面的事。当?dng)clean的规则不要放在文件的开_(d)不然Q这׃(x)变成make的默认目标,怿谁也不愿意这栗不成文的规矩是—?#8220;clean从来都是攑֜文g的最?#8221;?

上面是一个makefile的概貌,也是makefile的基Q下面还有很多makefile的相关细节,准备好了(jin)吗?准备好了(jin)来?


2 Makefile 总述

2.1 Makefile里有什么?

Makefile里主要包含了(jin)五个东西Q显式规则、隐晦规则、变量定义、文件指C和注释?

  1. 昑ּ规则。显式规则说明了(jin)Q如何生成一个或多的的目标文件。这是由Makefile的书写者明显指出,要生成的文gQ文件的依赖文gQ生成的命o(h)?
  2. 隐晦规则。由于我们的make有自动推导的功能Q所以隐晦的规则可以让我们比较粗p地略地书写MakefileQ这是由make所支持的?
  3. 变量的定义。在Makefile中我们要定义一pd的变量,变量一般都是字W串Q这个有点你C语言中的宏,当Makefile被执行时Q其中的变量都会(x)被扩展到相应的引用位|上?
  4. 文g指示。其包括?jin)三个部分,一个是在一个Makefile中引用另一个MakefileQ就像C语言中的include一P另一个是指根据某些情冉|定Makefile中的有效部分Q就像C语言中的预编?if一Pq有是定义一个多行的命o(h)。有兌一部分的内容,我会(x)在后l的部分中讲q?
  5. 注释。Makefile中只有行注释Q和UNIX的Shell脚本一P其注释是?#8220;#”字符Q这个就像C/C++中的“//”一栗如果你要在你的Makefile中?#8220;#”字符Q可以用反斜框进行{义,如:(x)“\#”?

最后,q值得一提的是,在Makefile中的命o(h)Q必要以[Tab]键开始?

2.2Makefile的文件名

默认的情况下Qmake命o(h)?x)在当前目录下按序扑֯文g名ؓ(f)“GNUmakefile”?#8220;makefile”?#8220;Makefile”的文Ӟ扑ֈ?jin)解释这个文件。在q三个文件名中,最好?#8220;Makefile”q个文g名,因ؓ(f)Q这个文件名W一个字Wؓ(f)大写Q这h一U显目的感觉。最好不要用“GNUmakefile”Q这个文件是GNU的make识别的。有另外一些make只对全小写的“makefile”文g名敏感,但是基本上来_(d)大多数的make都支?#8220;makefile”?#8220;Makefile”q两U默认文件名?

当然Q你可以使用别的文g名来书写MakefileQ比如:(x)“Make.Linux”Q?#8220;Make.Solaris”Q?#8220;Make.AIX”{,如果要指定特定的MakefileQ你可以使用make?#8220;-f”?#8220;--file”参数Q如Qmake -f Make.Linux或make --file Make.AIX?

2.3 引用其它的Makefile

在Makefile使用include关键字可以把别的Makefile包含q来Q这很像C语言?includeQ被包含的文件会(x)原模原样的放在当前文件的包含位置。include的语法是Q?
include <filename>
filename可以是当前操作系lShell的文件模式(可以保含路径和通配W)(j)

在include前面可以有一些空字符Q但是绝不能是[Tab]键开始。include?filename>可以用一个或多个I格隔开。D个例子,你有q样几个MakefileQa.mk、b.mk、c.mkQ还有一个文件叫foo.makeQ以?qing)一个变?(bar)Q其包含?jin)e.mk和f.mkQ那么,下面的语句:(x)

    include foo.make *.mk $(bar)
{h(hun)于:(x)
    include foo.make a.mk b.mk c.mk e.mk f.mk
make命o(h)开始时Q会(x)把找寻include所指出的其它MakefileQƈ把其内容安置在当前的位置。就好像C/C++?include指o(h)一栗如果文仉没有指定l对路径或是相对路径的话Qmake?x)在当前目录下首先寻找,如果当前目录下没有找刎ͼ那么Qmakeq会(x)在下面的几个目录下找Q?

  1. 如果make执行Ӟ?#8220;-I”?#8220;--include-dir”参数Q那么make׃(x)在这个参数所指定的目录下d找?
  2. 如果目录 /includeQ一般是Q?usr/local/bin?usr/includeQ存在的话,make也会(x)L?

如果有文件没有找到的话,make?x)生成一条警告信息,但不?x)马上出现致命错误。它?x)l蝲入其它的文gQ一旦完成makefile的读取,make?x)再重试q些没有扑ֈQ或是不能读取的文gQ如果还是不行,make才会(x)出现一条致命信息。如果你惌make不理那些无法d的文Ӟ而l执行,你可以在include前加一个减?#8220;-”。如Q?

-include <filename>
其表C,无论includeq程中出C么错误,都不要报错l执行。和其它版本make兼容的相兛_令是sincludeQ其作用和这一个是一L(fng)?

2.4 环境变量 MAKEFILES

如果你的当前环境中定义了(jin)环境变量MAKEFILESQ那么,make?x)把q个变量中的值做一个类ginclude的动作。这个变量中的值是其它的MakefileQ用I格分隔。只是,它和include不同的是Q从q个环境变中引入的Makefile?#8220;目标”不会(x)起作用,如果环境变量中定义的文g发现错误Qmake也会(x)不理?

但是在这里我q是不要使用q个环境变量Q因为只要这个变量一被定义,那么当你使用makeӞ所有的Makefile都会(x)受到它的影响Q这l不是你想看到的。在q里提这个事Q只是ؓ(f)?jin)告诉大Ӟ也许有时候你的Makefile出现?jin)怪事Q那么你可以看看当前环境中有没有定义q个变量?

2.5 make的工作方?

GNU的make工作时的执行步骤入下Q(x其它的make也是cMQ?

  1. d所有的Makefile?
  2. d被include的其它Makefile?
  3. 初始化文件中的变量?
  4. 推导隐晦规则Qƈ分析所有规则?
  5. 为所有的目标文g创徏依赖关系链?
  6. Ҏ(gu)依赖关系Q决定哪些目标要重新生成?
  7. 执行生成命o(h)?

1-5步ؓ(f)W一个阶D,6-7为第二个阶段。第一个阶D中Q如果定义的变量被用了(jin)Q那么,make?x)把其展开在用的位置。但makeq不?x)完全马上展开Qmake使用的是拖g战术Q如果变量出现在依赖关系的规则中Q那么仅当这条依赖被军_要用了(jin)Q变量才?x)在其内部展开?

当然Q这个工作方式你不一定要清楚Q但是知道这个方式你也会(x)对make更ؓ(f)熟?zhn)。有?jin)这个基Q后l部分也容易看懂了(jin)?

3 Makefile书写规则


规则包含两个部分Q一个是依赖关系Q一个是生成目标的方法?

在Makefile中,规则的顺序是很重要的Q因为,Makefile中只应该有一个最l目标,其它的目标都是被q个目标所q带出来的,所以一定要让make知道你的最l目标是什么。一般来_(d)定义在Makefile中的目标可能?x)有很多Q但是第一条规则中的目标将被确立ؓ(f)最l的目标。如果第一条规则中的目标有很多个,那么Q第一个目标会(x)成ؓ(f)最l的目标。make所完成的也是q个目标?

好了(jin)Q还是让我们来看一看如何书写规则?

3.1 规则举例

 foo.o : foo.c defs.h       # foo模块
cc -c -g foo.c
看到q个例子Q各位应该不是很陌生?jin),前面也已说过Qfoo.o是我们的目标Qfoo.c和defs.h是目标所依赖的源文gQ而只有一个命?#8220;cc -c -g foo.c”Q以Tab键开_(d)(j)。这个规则告诉我们两件事Q?

  1. 文g的依赖关p,foo.o依赖于foo.c和defs.h的文Ӟ如果foo.c和defs.h的文件日期要比foo.o文g日期要新Q或是foo.o不存在,那么依赖关系发生?
  2. 如果生成Q或更新Qfoo.o文g。也是那个cc命o(h)Q其说明?jin),如何生成foo.oq个文g。(当然foo.c文ginclude?jin)defs.h文gQ?

3.2 规则的语?

      targets : prerequisites
command
...
或是q样Q?
      targets : prerequisites ; command
command
...
targets是文件名Q以I格分开Q可以用通配W。一般来_(d)我们的目标基本上是一个文Ӟ但也有可能是多个文g?

command是命令行Q如果其不与“target:prerequisites”在一行,那么Q必M[Tab键]开_(d)如果和prerequisites在一行,那么可以用分号做为分隔。(见上Q?

prerequisites也就是目标所依赖的文Ӟ或依赖目标)(j)。如果其中的某个文g要比目标文g要新Q那么,目标p认ؓ(f)?#8220;q时?#8221;Q被认ؓ(f)是需要重生成的。这个在前面已经讲过?jin)?

如果命o(h)太长Q你可以使用反斜框(‘\’Q作为换行符。make对一行上有多个字符没有限制。规则告诉make两g事,文g的依赖关pd如何成成目标文g?

一般来_(d)make?x)以UNIX的标准ShellQ也是/bin/sh来执行命令?

3.3 在规则中使用通配W?

如果我们惛_义一pd比较cM的文Ӟ我们很自然地想起用通配W。make支持三各通配W:(x)“*”Q?#8220;?”?#8220;[...]”。这是和Unix的B-Shell是相同的?

波浪P“~”Q字W在文g名中也有比较Ҏ(gu)的用途。如果是“~/test”Q这pC当前用L(fng)$HOME目录下的test目录。?#8220;~hchen/test”则表C用户hchen的宿ȝ录下的test目录。(q些都是Unix下的知识了(jin)Qmake也支持)(j)而在Windows或是MS-DOS下,用户没有宿主目录Q那么L号所指的目录则根据环境变?#8220;HOME”而定?

通配W代替了(jin)你一pd的文Ӟ?#8220;*.c”表示所以后~为c的文件。一个需要我们注意的是,如果我们的文件名中有通配W,如:(x)“*”Q那么可以用转义字符“\”Q如“\*”来表C真实的“*”字符Q而不是Q意长度的字符丌Ӏ?

好吧Q还是先来看几个例子吧:(x)

    clean:
rm -f *.o
上面q个例子我不不多说了(jin)Q这是操作系lShell所支持的通配W。这是在命o(h)中的通配W?
    print: *.c
lpr -p $?
touch print
上面q个例子说明?jin)通配W也可以在我们的规则中,目标print依赖于所有的[.c]文g。其中的“$?”是一个自动化变量Q我?x)在后面l你讲述?
    objects = *.o
上面q个例子Q表CZ(jin)Q通符同样可以用在变量中。ƈ不是说[*.o]?x)展开Q不Qobjects的值就?#8220;*.o”。Makefile中的变量其实是C/C++中的宏。如果你要让通配W在变量中展开Q也是让objects的值是所有[.o]的文件名的集合,那么Q你可以q样Q?
    objects := $(wildcard *.o)
q种用法由关键字“wildcard”指出Q关于Makefile的关键字Q我们将在后面讨论?

3.4 文g搜寻

在一些大的工E中Q有大量的源文gQ我们通常的做法是把这许多的源文g分类Qƈ存放在不同的目录中。所以,当make需要去扑֯文g的依赖关pLQ你可以在文件前加上路径Q但最好的Ҏ(gu)是把一个\径告诉makeQ让make在自动去找?

Makefile文g中的Ҏ(gu)变量“VPATH”是完成q个功能的,如果没有指明q个变量Qmake只会(x)在当前的目录中去扑֯依赖文g和目标文件。如果定义了(jin)q个变量Q那么,make׃(x)在当当前目录找不到的情况下,到所指定的目录中LL件了(jin)?

    VPATH = src:../headers
上面的的定义指定两个目录Q?#8220;src”?#8220;../headers”Qmake?x)按照这个顺序进行搜索。目录由“冒号”分隔。(当然Q当前目录永q是最高优先搜索的地方Q?

另一个设|文件搜索\径的Ҏ(gu)是用make?#8220;vpath”关键字(注意Q它是全写的)(j)Q这不是变量Q这是一个make的关键字Q这和上面提到的那个VPATH变量很类|但是它更为灵zR它可以指定不同的文件在不同的搜索目录中。这是一个很灉|的功能。它的用方法有三种Q?

  1. vpath < pattern> < directories>
    为符合模?lt; pattern>的文件指定搜索目?lt; directories>?
  2. vpath < pattern>
    清除W合模式< pattern>的文件的搜烦(ch)目录?
  3. vpath
    清除所有已被设|好?jin)的文g搜烦(ch)目录?

vapth使用Ҏ(gu)中的< pattern>需要包?#8220;%”字符?#8220;%”的意思是匚w零或若干字符Q例如,“%.h”表示所有以“.h”l尾的文件?lt; pattern>指定?jin)要搜?ch)的文仉Q?lt; directories>则指定了(jin) 的文仉的搜索的目录。例如:(x)

    vpath %.h ../headers
该语句表C,要求make?#8220;../headers”目录下搜索所有以“.h”l尾的文件。(如果某文件在当前目录没有扑ֈ的话Q?

我们可以q箋C用vpath语句Q以指定不同搜烦(ch){略。如果连l的vpath语句中出C(jin)相同?lt; pattern>Q或是被重复?jin)?lt; pattern>Q那么,make?x)按照vpath语句的先后顺序来执行搜烦(ch)。如Q?

    vpath %.c foo
vpath %   blish
vpath %.c bar
其表C?#8220;.c”l尾的文Ӟ先在“foo”目录Q然后是“blish”Q最后是“bar”目录?
    vpath %.c foo:bar
vpath %   blish
而上面的语句则表C?#8220;.c”l尾的文Ӟ先在“foo”目录Q然后是“bar”目录Q最后才?#8220;blish”目录?

3.5 伪目?

最早先的一个例子中Q我们提到过一?#8220;clean”的目标,q是一?#8220;伪目?#8221;Q?

    clean:
rm *.o temp
正像我们前面例子中的“clean”一P即然我们生成?jin)许多文件编译文Ӟ我们也应该提供一个清除它们的“目标”以备完整地重~译而用?Q以“make clean”来用该目标Q?

因ؓ(f)Q我们ƈ不生?#8220;clean”q个文g?#8220;伪目?#8221;q不是一个文Ӟ只是一个标{,׃“伪目?#8221;不是文gQ所以make无法生成它的依赖关系和决定它是否要执行。我们只有通过昄地指明这?#8220;目标”才能让其生效。当?dng)?#8220;伪目?#8221;的取名不能和文g名重名,不然其就失去?#8220;伪目?#8221;的意义了(jin)?

当然Qؓ(f)?jin)避免和文g重名的这U情况,我们可以使用一个特D的标记“.PHONY”来显C地指明一个目标是“伪目?#8221;Q向make说明Q不是否有q个文gQ这个目标就?#8220;伪目?#8221;?

    .PHONY : clean
只要有这个声明,不管是否?#8220;clean”文gQ要q行“clean”q个目标Q只?#8220;make clean”q样。于是整个过E可以这样写Q?
     .PHONY: clean
clean:
rm *.o temp
伪目标一般没有依赖的文g。但是,我们也可以ؓ(f)伪目标指定所依赖的文件。伪目标同样可以作ؓ(f)“默认目标”Q只要将其放在第一个。一个示例就是,如果你的Makefile需要一口气生成若干个可执行文gQ但你只想简单地敲一个make完事Qƈ且,所有的目标文g都写在一个Makefile中,那么你可以?#8220;伪目?#8221;q个Ҏ(gu):(x)
    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
我们知道QMakefile中的W一个目标会(x)被作为其默认目标。我们声明了(jin)一?#8220;all”的伪目标Q其依赖于其它三个目标。由于伪目标的特性是QL被执行的Q所以其依赖的那三个目标L不如“all”q个目标新。所以,其它三个目标的规则L?x)被册。也pC(jin)我们一口气生成多个目标的目的?#8220;.PHONY : all”声明?#8220;all”q个目标?#8220;伪目?#8221;?

随便提一句,从上面的例子我们可以看出Q目标也可以成ؓ(f)依赖。所以,伪目标同样也可成Z赖。看下面的例子:(x)

    .PHONY: cleanall cleanobj cleandiff
cleanall : cleanobj cleandiff
rm program
cleanobj :
rm *.o
cleandiff :
rm *.diff
“make clean”清除所有要被清除的文g?#8220;cleanobj”?#8220;cleandiff”q两个伪目标有点?#8220;子程?#8221;的意思。我们可以输?#8220;make cleanall”?#8220;make cleanobj”?#8220;make cleandiff”命o(h)来达到清除不同种cL件的目的

3.6 多目?

Makefile的规则中的目标可以不止一个,其支持多目标Q有可能我们的多个目标同时依赖于一个文Ӟq且其生成的命o(h)大体cM。于是我们就能把其合qv来。当?dng)多个目标的生成规则的执行命o(h)是同一个,q可能会(x)可我们带来麻?ch),不过好在我们的可以用一个自动化变量“$@”Q关于自动化变量Q将在后面讲qͼ(j)Q这个变量表C着目前规则中所有的目标的集合,q样说可能很抽象Q还是看一个例子吧?

    bigoutput littleoutput : text.g
generate text.g -$(subst output,,$@) > $@
    上述规则{h(hun)于:(x)
bigoutput : text.g generate text.g -big > bigoutput littleoutput : text.g generate text.g -little > littleoutput
其中Q?$(subst output,,$@)中的“$”表示执行一个Makefile的函敎ͼ函数名ؓ(f)substQ后面的为参数。关于函敎ͼ在后面讲述。这里的q个函数是截取字W串的意思,“$@”表示目标的集合,像一个数l,“$@”依次取出目标Qƈ执于命o(h)?

3.7 静态模?

静态模式可以更加容易地定义多目标的规则Q可以让我们的规则变得更加的有弹性和灉|。我们还是先来看一下语法:(x)

<targets ...>: <target-pattern>: <prereq-patterns ...>
   <commands>
...

targets定义?jin)一pd的目标文Ӟ可以有通配W。是目标的一个集合?

target-parrtern是指明了(jin)targets的模式,也就是的目标集模式?

prereq-parrterns是目标的依赖模式Q它对target-parrtern形成的模式再q行一ơ依赖目标的定义?

q样描述q三个东西,可能q是没有说清楚,q是举个例子来说明一下吧。如果我们的<target-parrtern>定义?#8220;%.o”Q意思是我们?target>集合中都是以“.o”l尾的,而如果我们的<prereq-parrterns>定义?#8220;%.c”Q意思是?lt;target-parrtern>所形成的目标集q行二次定义Q其计算Ҏ(gu)是,?lt;target-parrtern>模式中的“%”Q也是L?jin)[.o]q个l尾Q,qؓ(f)其加上[.c]q个l尾QŞ成的新集合?

所以,我们?#8220;目标模式”或是“依赖模式”中都应该?#8220;%”q个字符Q如果你的文件名中有“%”那么你可以用反斜杠“\”q行转义Q来标明真实?#8220;%”字符?

看一个例子:(x)

    objects = foo.o bar.o
all: $(objects)
$(objects): %.o: %.c
$(CC) -c $(CFLAGS) $< -o $@
上面的例子中Q指明了(jin)我们的目标从$object中获取,“%.o”表明要所有以“.o”l尾的目标,也就?#8220;foo.o bar.o”Q也是变量$object集合的模式,而依赖模?#8220;%.c”则取模式“%.o”?#8220;%”Q也是“foo bar”Qƈ为其加下“.c”的后~Q于是,我们的依赖目标就?#8220;foo.c bar.c”。而命令中?#8220;$<”?#8220;$@”则是自动化变量,“$<”表示所有的依赖目标集(也就?#8220;foo.c bar.c”Q,“$@”表示目标集(也褪恰癴oo.o bar.o”Q。于是,上面的规则展开后等价于下面的规则:(x)
    foo.o : foo.c
$(CC) -c $(CFLAGS) foo.c -o foo.o
bar.o : bar.c
$(CC) -c $(CFLAGS) bar.c -o bar.o
试想Q如果我们的“%.o”有几百个Q那U我们只要用q种很简单的“静态模式规?#8221;可以写完一堆规则,实在是太有效率了(jin)?#8220;静态模式规?#8221;的用法很灉|Q如果用得好Q那?x)一个很强大的功能。再看一个例子:(x)
    files = foo.elc bar.o lose.o
$(filter %.o,$(files)): %.o: %.c
$(CC) -c $(CFLAGS) $< -o $@
$(filter %.elc,$(files)): %.elc: %.el
emacs -f batch-byte-compile $<

$(filter %.o,$(files))表示调用Makefile的filter函数Q过?#8220;$filter”集,只要其中模式?#8220;%.o”的内宏V其的它内容Q我׃用多说了(jin)吧。这个例字展CZ(jin)Makefile中更大的Ҏ(gu)?

3.8 自动生成依赖?

在Makefile中,我们的依赖关pd能会(x)需要包含一pd的头文gQ比如,如果我们的main.c中有一?#8220;#include "defs.h"”Q那么我们的依赖关系应该是:(x)

    main.o : main.c defs.h
但是Q如果是一个比较大型的工程Q你必需清楚哪些C文g包含?jin)哪些头文gQƈ且,你在加入或删除头文gӞ也需要小?j)地修改MakefileQ这是一个很没有l护性的工作。ؓ(f)?jin)避免这U繁重而又Ҏ(gu)出错的事情,我们可以使用C/C++~译的一个功能。大多数的C/C++~译器都支持一?#8220;-M”的选项Q即自动扑֯源文件中包含的头文gQƈ生成一个依赖关pR例如,如果我们执行下面的命令:(x)
    cc -M main.c
其输出是Q?
    main.o : main.c defs.h
于是q译器自动生成的依赖关p,q样一来,你就不必再手动书写若q文件的依赖关系Q而由~译器自动生成了(jin)。需要提醒一句的是,如果你用GNU的C/C++~译器,你得?#8220;-MM”参数Q不?dng)?#8220;-M”参数?x)把一些标准库的头文g也包含进来?

gcc -M main.c的输出是Q?

    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
gcc -MM main.c的输出则是:(x)
    main.o: main.c defs.h
那么Q编译器的这个功能如何与我们的Makefile联系在一起呢。因样一来,我们的Makefile也要Ҏ(gu)q些源文仉新生成,让Makefile自已依赖于源文gQ这个功能ƈ不现实,不过我们可以有其它手D|q回地实现这一功能。GNUl织把编译器为每一个源文g的自动生成的依赖关系攑ֈ一个文件中Qؓ(f)每一?#8220;name.c”的文仉生成一?#8220;name.d”的Makefile文gQ[.d]文g中就存放对应[.c]文g的依赖关pR?

于是Q我们可以写出[.c]文g和[.d]文g的依赖关p,q让make自动更新或自成[.d]文gQƈ把其包含在我们的主Makefile中,q样Q我们就可以自动化地生成每个文g的依赖关pM(jin)?

q里Q我们给Z(jin)一个模式规则来产生[.d]文gQ?

    %.d: %.c
@set -e; rm -f $@; \
$(CC) -M $(CPPFLAGS) $< > $@.$$$$; \
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
rm -f $@.$$$$

q个规则的意思是Q所有的[.d]文g依赖于[.c]文gQ?#8220;rm -f $@”的意思是删除所有的目标Q也是[.d]文gQ第二行的意思是Qؓ(f)每个依赖文g“$<”Q也是[.c]文g生成依赖文gQ?#8220;$@”表示模式“%.d”文gQ如果有一个C文g是name.cQ那?#8220;%”是“name”Q?#8220;$$$$”意ؓ(f)一个随机编PW二行生成的文g有可能是“name.d.12345”Q第三行使用sed命o(h)做了(jin)一个替换,关于sed命o(h)的用法请参看相关的用文档。第四行是删除临时文g?

总而言之,q个模式要做的事是在编译器生成的依赖关pM加入[.d]文g的依赖,x依赖关系Q?

    main.o : main.c defs.h
转成Q?
    main.o main.d : main.c defs.h
于是Q我们的[.d]文g也会(x)自动更新?jin),q会(x)自动生成?jin),当然Q你q可以在q个[.d]文g中加入的不只是依赖关p,包括生成的命令也可一q加入,让每个[.d]文g都包含一个完赖的规则。一旦我们完成这个工作,接下来,我们p把这些自动生成的规则放进我们的主Makefile中。我们可以用Makefile?#8220;include”命o(h)Q来引入别的Makefile文gQ前面讲q)(j)Q例如:(x)
    sources = foo.c bar.c
include $(sources:.c=.d)
上述语句中的“$(sources:.c=.d)”中的“.c=.d”的意思是做一个替换,把变?(sources)所有[.c]的字串都替换成[.d]Q关于这?#8220;替换”的内容,在后面我?x)有更?f)详细的讲q。当?dng)你得注意ơ序Q因为include是按ơ来载入文gQ最先蝲入的[.d]文g中的目标?x)成为默认目?

4 Makefile 书写命o(h)


每条规则中的命o(h)和操作系lShell的命令行是一致的。make?x)一按顺序一条一条的执行命o(h)Q每条命令的开头必M[Tab]键开_(d)除非Q命令是紧跟在依赖规则后面的分号后的。在命o(h)行之间中的空格或是空行会(x)被忽略,但是如果该空格或I是以Tab键开头的Q那么make?x)认为其是一个空命o(h)?

我们在UNIX下可能会(x)使用不同的ShellQ但是make的命令默认是?#8220;/bin/sh”——U(ku)NIX的标准Shell解释执行的。除非你特别指定一个其它的Shell。Makefile中,“#”是注释符Q很像C/C++中的“//”Q其后的本行字符都被注释?

4.1 昄命o(h)

通常Qmake?x)把其要执行的命令行在命令执行前输出到屏q上。当我们?#8220;@”字符在命令行前,那么Q这个命令将不被make昄出来Q最具代表性的例子是,我们用这个功能来像屏q显CZ些信息。如Q?

    @echo 正在~译XXX模块......
当make执行Ӟ?x)输?#8220;正在~译XXX模块......”字串Q但不会(x)输出命o(h)Q如果没?#8220;@”Q那么,make输出:(x)
    echo 正在~译XXX模块......
正在~译XXX模块......
如果make执行Ӟ带入make参数“-n”?#8220;--just-print”Q那么其只是昄命o(h)Q但不会(x)执行命o(h)Q这个功能很有利于我们调试我们的MakefileQ看看我们书写的命o(h)是执行v来是什么样子的或是什么顺序的?

而make参数“-s”?#8220;--slient”则是全面止命o(h)的显C?

4.2 命o(h)执行

当依赖目标新于目标时Q也是当规则的目标需要被更新Ӟmake?x)一条一条的执行其后的命令。需要注意的是,如果你要让上一条命令的l果应用在下一条命令时Q你应该使用分号分隔q两条命令。比如你的第一条命令是cd命o(h)Q你希望W二条命令得在cd之后的基上运行,那么你就不能把这两条命o(h)写在两行上,而应该把q两条命令写在一行上Q用分号分隔。如Q?

    CZ一Q?
exec:
cd /home/hchen
pwd
CZ二:(x)
exec:
cd /home/hchen; pwd

当我们执?#8220;make exec”ӞW一个例子中的cd没有作用Qpwd?x)打印出当前的Makefile目录Q而第二个例子中,cdpv作用?jin),pwd?x)打印?#8220;/home/hchen”?

make一般是使用环境变量SHELL中所定义的系lShell来执行命令,默认情况下用UNIX的标准Shell—?bin/sh来执行命令。但在MS-DOS下有点特D,因ؓ(f)MS-DOS下没有SHELL环境变量Q当然你也可以指定。如果你指定?jin)UNIX风格的目录Ş式,首先Qmake?x)在SHELL所指定的\径中扑֯命o(h)解释器,如果找不刎ͼ其会(x)在当前盘W中的当前目录中LQ如果再找不刎ͼ其会(x)在PATH环境变量中所定义的所有\径中L。MS-DOS中,如果你定义的命o(h)解释器没有找刎ͼ其会(x)l你的命令解释器加上诸如“.exe”?#8220;.com”?#8220;.bat”?#8220;.sh”{后~?

4.3 命o(h)出错

每当命o(h)q行完后Qmake?x)检每个命令的q回码,如果命o(h)q回成功Q那么make?x)执行下一条命令,当规则中所有的命o(h)成功q回后,q个规则q是成功完成了(jin)。如果一个规则中的某个命令出错了(jin)Q命令退出码非零Q,那么make׃(x)l止执行当前规则Q这有可能l止所有规则的执行?

有些时候,命o(h)的出错ƈ不表C就是错误的。例如mkdir命o(h)Q我们一定需要徏立一个目录,如果目录不存在,那么mkdir成功执行,万事大吉Q如果目录存在,那么出错了(jin)。我们之所以用mkdir的意思就是一定要有这L(fng)一个目录,于是我们׃希望mkdir出错而终止规则的q行?

Z(jin)做到q一点,忽略命o(h)的出错,我们可以在Makefile的命令行前加一个减?#8220;-”Q在Tab键之后)(j)Q标Cؓ(f)不管命o(h)Z出错都认为是成功的。如Q?

   clean:
-rm -f *.o
q有一个全局的办法是Q给make加上“-i”或是“--ignore-errors”参数Q那么,Makefile中所有命令都?x)忽略错误。而如果一个规则是?#8220;.IGNORE”作ؓ(f)目标的,那么q个规则中的所有命令将?x)忽略错误。这些是不同U别的防止命令出错的Ҏ(gu)Q你可以Ҏ(gu)你的不同喜欢讄?

q有一个要提一下的make的参数的?#8220;-k”或是“--keep-going”Q这个参数的意思是Q如果某规则中的命o(h)出错?jin),那么q目该规则的执行,但l执行其它规则?

4.4 嵌套执行make

在一些大的工E中Q我们会(x)把我们不同模块或是不同功能的源文件放在不同的目录中,我们可以在每个目录中都书写一个该目录的MakefileQ这有利于让我们的Makefile变得更加地简z,而不至于把所有的东西全部写在一个Makefile中,q样?x)很隄护我们的MakefileQ这个技术对于我们模块编译和分段~译有着非常大的好处?

例如Q我们有一个子目录叫subdirQ这个目录下有个Makefile文gQ来指明?jin)这个目录下文g的编译规则。那么我们L的Makefile可以q样书写Q?

    subsystem:
cd subdir && $(MAKE)
其等价于Q?
subsystem:
$(MAKE) -C subdir
定义$(MAKE)宏变量的意思是Q也许我们的make需要一些参敎ͼ所以定义成一个变量比较利于维护。这两个例子的意思都是先q入“subdir”目录Q然后执行make命o(h)?

我们把这个Makefile叫做“LMakefile”QLMakefile的变量可以传递到下的Makefile中(如果你显C的声明Q,但是不会(x)覆盖下层的Makefile中所定义的变量,除非指定?#8220;-e”参数?

如果你要传递变量到下Makefile中,那么你可以用这L(fng)声明Q?

export <variable ...>
如果你不惌某些变量传递到下Makefile中,那么你可以这样声明:(x)
unexport <variable ...>
如:(x)
CZ一Q?
export variable = value
其等价于Q?
variable = value
export variable
其等价于Q?
export variable := value
其等价于Q?
variable := value
export variable
CZ二:(x)
export variable += value
其等价于Q?
variable += value
export variable
如果你要传递所有的变量Q那么,只要一个exportp?jin)。后面什么也不用跟,表示传递所有的变量?

需要注意的是,有两个变量,一个是SHELLQ一个是MAKEFLAGSQ这两个变量不管你是否exportQ其L要传递到下层Makefile中,特别是MAKEFILES变量Q其中包含了(jin)make的参C息,如果我们执行“LMakefile”时有make参数或是在上层Makefile中定义了(jin)q个变量Q那么MAKEFILES变量会(x)是这些参敎ͼq会(x)传递到下层Makefile中,q是一个系l的环境变量?

但是make命o(h)中的有几个参数ƈ不往下传递,它们?#8220;-C”,“-f”,“-h”“-o”?#8220;-W”Q有关Makefile参数的细节将在后面说明)(j)Q如果你不想往下层传递参敎ͼ那么Q你可以q样来:(x)

subsystem:
cd subdir && $(MAKE) MAKEFLAGS=
如果你定义了(jin)环境变量MAKEFLAGSQ那么你得确信其中的选项是大安?x)用到的Q如果其中有“-t”,“-n”,?#8220;-q”参数Q那么将?x)有让你意想不到的结果,或许会(x)让你异常地恐慌?

q有一个在“嵌套执行”中比较有用的参数Q?#8220;-w”或是“--print-directory”?x)在make的过E中输出一些信息,让你看到目前的工作目录。比如,如果我们的下Umake目录?#8220;/home/hchen/gnu/make”Q如果我们?#8220;make -w”来执行,那么当进入该目录Ӟ我们?x)看刎ͼ?x)

make: Entering directory `/home/hchen/gnu/make'.
而在完成下层make后离开目录Ӟ我们?x)看刎ͼ?x)
make: Leaving directory `/home/hchen/gnu/make'
当你使用“-C”参数来指定make下层MakefileӞ“-w”?x)被自动打开的。如果参C?#8220;-s”Q?#8220;--slient”Q或?#8220;--no-print-directory”Q那么,“-w”L失效的?

4.5 定义命o(h)?

如果Makefile中出C些相同命令序列,那么我们可以些相同的命o(h)序列定义一个变量。定义这U命令序列的语法?#8220;define”开始,?#8220;endef”l束Q如Q?

    define run-yacc
yacc $(firstword $^)
mv y.tab.c $@
endef
q里Q?#8220;run-yacc”是这个命令包的名字,其不要和Makefile中的变量重名。在“define”?#8220;endef”中的两行是命o(h)序列。这个命令包中的W一个命令是q行YaccE序Q因为YaccE序L生成“y.tab.c”的文Ӟ所以第二行的命令就是把q个文gҎ(gu)名字。还是把q个命o(h)包放C个示例中来看看吧?
    foo.c : foo.y
$(run-yacc)
我们可以看见Q要使用q个命o(h)包,我们好像用变量一栗在q个命o(h)包的使用中,命o(h)?#8220;run-yacc”中的“$^”是“foo.y”Q?#8220;$@”是“foo.c”Q有兌U以“$”开头的Ҏ(gu)变量Q我们会(x)在后面介l)(j)Qmake在执行命令包Ӟ命o(h)包中的每个命令会(x)被依ơ独立执行?

pear_li 2009-06-01 09:35 发表评论
]]>
C++格式化输?QstreamQ?/title><link>http://www.shnenglu.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.shnenglu.com/dawnbreak/articles/85932.html</guid><wfw:comment>http://www.shnenglu.com/dawnbreak/comments/85932.html</wfw:comment><comments>http://www.shnenglu.com/dawnbreak/articles/85932.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/dawnbreak/comments/commentRss/85932.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/dawnbreak/services/trackbacks/85932.html</trackback:ping><description><![CDATA[<h4><font color=#ff0000>本文摘自C++primerW四版附录A3</font></h4> <h4>A.3.3. 控制输出格式</h4> <p>许多操纵W我们能够改变输出的外观。有两大cȝ输出控制Q控制数值的表示Q以?qing)控制填充符的数量和布局?/p> <h5>控制布尔值和格式</h5> <p>改变对象格式化状态的操纵W的一个例子是 <tt><font face=NSimsun>boolalpha</font></tt> 操纵W。默认情况下Q将 <tt><font face=NSimsun>bool</font></tt> 值显CZؓ(f) 1 ?0Q?tt><font face=NSimsun>true</font></tt> 值显CZؓ(f) 1Q?<tt><font face=NSimsun>false</font></tt> 值显CZؓ(f) 0。可以通过的 <tt><font face=NSimsun>boolalpha</font></tt> 操纵W覆盖这个格式化Q?/p> <pre>cout << "default bool values: "<br> << true << " " << false<br> << "\nalpha bool values: "<br> << boolalpha<br> << true << " " << false<br> << endl;</pre> <p>执行Ӟq段E序产生下面的输出:(x)</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> “?#8221;?<tt><font face=NSimsun>cout</font></tt>Q从q个点v改变了(jin) <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> q行昄?/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>Q?/p> <pre>bool bool_val;<br>cout << boolalpha // <span>sets internal state of</span> <span>cout</span><br> << bool_val<br> << 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>Qƈ且立卛_重|ؓ(f)原来的状态?/p> <h5>指定整型值的基数</h5> <p>默认情况下,用十q制d整型倹{通过使用操纵W?<tt><font face=NSimsun>hex</font></tt>?tt><font face=NSimsun>oct</font></tt> ?<tt><font face=NSimsun>dec</font></tt>Q程序员可以表C制改为八q制、十六进制或恢复十进Ӟ点值的表示不受影响Q:(x)</p> <pre>const int ival = 15, jval = 1024; // <span>const,</span> <span>so values never change</span><br> cout << "default: ival = " << ival<br> << " jval = " << jval << endl;<br> cout << "printed in octal: ival = " << oct << ival<br> << " jval = " << jval << endl;<br> cout << "printed in hexadecimal: ival = " << hex << ival<br> << " jval = " << jval << endl;<br> cout << "printed in decimal: ival = " << dec << ival<br> << " jval = " << jval << endl;</pre> <p>~译和执行的时候,E序产生下面的输出:(x)</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>注意Q像 <tt><font face=NSimsun>boolalpha</font></tt> 一Pq些操纵W改变格式状态。它们媄(jing)响紧接在后面的输出,以及(qing)所有后l的整型输出Q直到通过调用另一操纵W重围格式ؓ(f)止?/p> <a></a> <h5>指出输出的基?/h5> <p>默认情况下,昄数值的时候,不存在关于所用基数的可见记号。例如,20 ?20,q是 16 的八q制表示Q按十进制模式显C数值的时候,?x)按我们期待的格式打印数倹{如果需要打印八q制或十六进制|可能应该也?<tt><font face=NSimsun>showbase</font></tt> 操纵W?tt><font face=NSimsun>showbase</font></tt> 操纵W导致输出流使用的约定,与指定整型常量基数所用的相同Q?/p> <ul> <li> <p>?0x 为前DC十六进制?/p> <li> <p>?0 为前DC八q制?/p> <li> <p>没有M前导表示十进制?/p> </li> </ul> <p>修改E序使用 <tt><font face=NSimsun>showbase</font></tt> 如下Q?/p> <pre>const int ival = 15, jval = 1024; // <span>const</span> <span>so values never change</span><br> cout << showbase; // <span>show base when printing integral values</span><br> cout << "default: ival = " << ival<br> << " jval = " << jval << endl;<br> cout << "printed in octal: ival = " << oct << ival<br> << " jval = " << jval << endl;<br> cout << "printed in hexadecimal: ival = " << hex << ival<br> << " jval = " << jval << endl;<br> cout << "printed in decimal: ival = " << dec << ival<br> << " jval = " << jval << endl;<br> cout << noshowbase; // <span>reset state of the stream</span></pre> <p>修改后的输出使得基础值到底是什么很清楚Q?/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> 操纵W重|?<tt><font face=NSimsun>cout</font></tt>Q以便它不再昄整型值的表示基数?/p> <p>默认情况下,十六q制值用带小?<tt><font face=NSimsun>x</font></tt> 的小写Ş式打印。可以应?<tt><font face=NSimsun>uppercase</font></tt> 操纵W显C?<tt><font face=NSimsun>X</font></tt> q将十六q制数字 <tt><font face=NSimsun>a</font></tt> - <tt><font face=NSimsun>f</font></tt> 昄为大写字母?/p> <pre>cout << uppercase << showbase << hex<br> << "printed in hexadecimal: ival = " << ival<br> << " jval = " << jval << endl<br> << nouppercase << endl;</pre> <p>前面的程序生下面的输出Q?/p> <pre><span>printed in hexadecimal: ival = 0XF jval = 0X400</span></pre> <p>要恢复小写,应?nouppercase 操纵W?/p> <h5>控制点值的格式</h5> <p>对于点值的格式化,可以控制下面三个斚wQ?/p> <ul> <li> <p>_ֺQ显C多位数字?/p> <li> <p>记数法:(x)用小数还是科学记法法昄?/p> <li> <p>Ҏ(gu)整数的Q点值的数点的处理?/p> </li> </ul> <p>默认情况下,使用六位数字的精度显CQ点倹{如果值没有小数部分,则省略小数点。用小数Ş式还是科学记数法昄数值取决于被显C的点数的|标准库选择增强数值可L的格式Q非常大和非常小的g用科学记数法昄Q其他g用小数Ş式?/p> <h5>指定昄_ֺ</h5> <p>默认情况下,_ֺ控制昄的数字M数。显C的时候,Q点值四舍五入到当前_ֺ。因此,如果当前_ֺ?4Q则 <tt><font face=NSimsun>3.14159</font></tt> 成ؓ(f) <tt><font face=NSimsun>3.142</font></tt>Q如果精度是 3Q打Cؓ(f) <tt><font face=NSimsun>3.14</font></tt>?/p> <p>通过名ؓ(f) <tt><font face=NSimsun>precision</font></tt> 的成员函敎ͼ或者通过使用 <tt><font face=NSimsun>setprecision</font></tt> 操纵W,可以改变_ֺ?tt><font face=NSimsun>precision</font></tt> 成员是重载的Q?a ><font color=#bf7fa1>W?7.8 ?/font></a>Q:(x)一个版本接受一?<tt><font face=NSimsun>int</font></tt> 值ƈ精度设|ؓ(f)那个新|它返?span>先前</span>的精度|另一个版本不接受实参q返回当前精度倹{?tt><font face=NSimsun>setprecision</font></tt> 操纵W接受一个实参,用来讄_ֺ?/p> <p>下面的程序说明控制显CQ点值所用精度的不同Ҏ(gu)Q?/p> <pre>// <span>cout.precision</span> <span>reports current precision value</span><br> cout << "Precision: " << cout.precision()<br> << ", Value: " << sqrt(2.0) << endl;<br> // <span>cout.precision(12)</span> <span>asks that 12 digits of precision to be printed</span><br> cout.precision(12);<br> cout << "Precision: " << cout.precision()<br> << ", Value: " << sqrt(2.0) << endl;<br> // <span>alternative way to set precision using</span> <span>setprecision</span> <span>manipulator</span><br> cout << setprecision(3);<br> cout << "Precision: " << cout.precision()<br> << ", Value: " << sqrt(2.0) << endl;</pre> <p>~译q执行后Q程序生下面的输出Q?/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>q个E序调用标准库中?<tt><font face=NSimsun>sqrt</font></tt> 函数Q可以在头文?<tt><font face=NSimsun>cmath</font></tt> 中找到它?tt><font face=NSimsun>sqrt</font></tt> 函数量重载的Q可以用 <tt><font face=NSimsun>float</font></tt>?tt><font face=NSimsun>double</font></tt> ?<tt><font face=NSimsun>long double</font></tt> 实参调用Q它q回实参的^Ҏ(gu)?/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>操纵W和其他接受实参的操U늬定义在头文g <tt><font face=NSimsun>iomanip</font></tt> 中?/p> </td> </tr> </tbody> </table> </p> </div> <h5>控制记数?/h5> <p>默认情况下,用于昄点值的记数法取决于数的大小Q如果数很大或很,按U学记数法显C,否则Q用固定位数的数。标准库选择使得数容易阅ȝ记数法?/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>QҎ(gu)昄为普通数Q相对于昄货币、百分比Q那时我们希望控制值的外观Q的时候,通常最好让标准库来选择使用的记数法。要强制U学记数法或固定位数数的一U情冉|在显C的时候,表中的小数点应该寚w?/p> </td> </tr> </tbody> </table> </p> </div> <p>如果希望强制U学记数法或固定位数数表示Q可以通过使用适当的操U늬做到q一点:(x)<tt><font face=NSimsun>scientific</font></tt> 操纵W将变Z用科学记数法。像在十六进制g昄 <tt><font face=NSimsun>x</font></tt> 一P也可以通过 <tt><font face=NSimsun>uppercase</font></tt> 操纵W控制科学记数法中的 <tt><font face=NSimsun>e</font></tt>?tt><font face=NSimsun>fixed</font></tt> 操纵W将ؓ(f)使用固定位数数表示?/p> <p>q些操纵W改变流_ֺ的默认含义。执?<tt><font face=NSimsun>scientific</font></tt> ?<tt><font face=NSimsun>fixed</font></tt> 之后Q精度值控制小数点之后的数位。默认情况下Q精度指定数字的M数——小数点之前和之后。?<tt><font face=NSimsun>fixed</font></tt> ?<tt><font face=NSimsun>scientific</font></tt> 命名我们能够按列寚w来显C数Q这一{略保证数Ҏ(gu)L在相对于被显C的数部分固定的位|?/p> <h5>恢复点值的默认记数?/h5> <p>与其他操U늬不同Q不存在流恢复为根据被昄值选择记数法的默认状态的操纵W,相反Q我们必调?<tt><font face=NSimsun>unsetf</font></tt> 成员来取?<tt><font face=NSimsun>scientific</font></tt> ?<tt><font face=NSimsun>fixed</font></tt> 所做的改变。要流恢复为Q点值的默认处理Q将名ؓ(f) <tt><font face=NSimsun>floatfield</font></tt> 的标准库定义gl?<tt><font face=NSimsun>unsetf</font></tt> 函数Q?/p> <pre>// <span>reset to default handling for notation</span><br> cout.unsetf(ostream::floatfield);</pre> <p>除了(jin)取消它们的效果之外,使用q些操纵W像使用L其他操纵W一P(x)</p> <pre>cout << sqrt(2.0) << '\n' << endl;<br> cout << "scientific: " << scientific << sqrt(2.0) << '\n'<br> << "fixed decimal: " << fixed << sqrt(2.0) << "\n\n";<br> cout << uppercase<br> << "scientific: " << scientific << sqrt(2.0) << '\n'<br> << "fixed decimal: " << fixed << sqrt(2.0) << endl<br> << nouppercase;<br> // <span>reset to default handling for notation</span><br> cout.unsetf(ostream::floatfield);<br> cout << '\n' << sqrt(2.0) << endl;</pre> <p>产生如下输出Q?/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>默认情况下,当Q点值的数部分?0 的时候,不显C小数点?tt><font face=NSimsun>showpoint</font></tt> 操纵W强制显C小数点Q?/p> <pre>cout << 10.0 << endl; // <span>prints</span> <span>10</span><br> cout << showpoint << 10.0 // <span>prints</span> <span>10.0000</span><br> << noshowpoint << endl; // <span>revert to default handling of decimal point</span></pre> <p><tt><font face=NSimsun>noshowpoint</font></tt> 操纵W恢复默认行为。下一个输辑ּ具有默认行为,卻I如果点值小数部分ؓ(f) 0,取消小数点?/p> <h5>填充输出</h5> <p>按栏昄数据的时候,l常很希望很好地控制数据的格式化。标准库提供下面几个操纵帮助我们实现需要的控制Q?/p> <ul> <li> <p><tt><font face=NSimsun>setw</font></tt>Q指定下一个数值或字符串的最间隔?/p> <li> <p><tt><font face=NSimsun>left</font></tt>Q左寚w输出?/p> <p><tt><font face=NSimsun>right</font></tt>Q右寚w输出。输出默认ؓ(f)叛_齐?/p> <li> <p><tt><font face=NSimsun>internal</font></tt>Q控制负值的W号位置?tt><font face=NSimsun>internal</font></tt> 左对齐符号且叛_齐|用空格填充介于其间的I间?/p> <li> <p><tt><font face=NSimsun>setfill</font></tt>Q我们能够指定填充输出时用的另一个字W。默认情况下Q值是I格?/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> 一P<tt><font face=NSimsun>setw</font></tt> 不改变输出流的内部状态,它只军_下一个输出的长度?/p> </td> </tr> </tbody> </table> </p> </div> <p>下面E序D说明了(jin)q些操纵W:(x)</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 << "i: " << setw(12) << i << "next col" << '\n'<br> << "d: " << setw(12) << d << "next col" << '\n';<br> // <span>pad first column and left-justify all columns</span><br> cout << left<br> << "i: " << setw(12) << i << "next col" << '\n'<br> << "d: " << setw(12) << d << "next col" << '\n'<br> << right; // <span>restore normal justification</span><br> // <span>pad first column and right-justify all columns</span><br> cout << right<br> << "i: " << setw(12) << i << "next col" << '\n'<br> << "d: " << setw(12) << d << "next col" << '\n';<br> // <span>pad first column but put the padding internal to the field</span><br> cout << internal<br> << "i: " << setw(12) << i << "next col" << '\n'<br> << "d: " << setw(12) << d << "next col" << '\n';<br> // <span>pad first column, using # as the pad character</span><br> cout << setfill('#')<br> << "i: " << setw(12) << i << "next col" << '\n'<br> << "d: " << setw(12) << d << "next col" << '\n'<br> << setfill(' '); // <span>restore normal pad character</span></pre> <p>执行Ӟ该程序段产生如下输出Q?/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>默认情况下,输入操作W忽略空白(I格、制表符、换行符、进U和回RQ。对下面的@环:(x)</p> <pre>while (cin >> ch)<br> cout << ch;</pre> <p>l定输入序列</p> <pre><span>a b c</span><br> <span>d</span></pre> <p>循环执行四次从字W?<tt><font face=NSimsun>a</font></tt> d <tt><font face=NSimsun>d</font></tt>Q蟩q介于其间的I格、可能的制表W和换行W。该E序D늚输出是:(x)</p> <pre><span>abcd</span></pre> <p><tt><font face=NSimsun>noskipws</font></tt> 操纵W导致输入操作符读(而不是蟩q)(j)I白。要q回默认行ؓ(f)Q应?<tt><font face=NSimsun>skipws</font></tt> 操纵W:(x)</p> <pre>cin >> noskipws; // <span>set</span> <span>cin</span> <span>so that it reads whitespace</span><br> while (cin >> ch)<br> cout << ch;<br> cin >> skipws; // <span>reset</span> <span>cin</span> <span>to default state so that it discards whitespace</span></pre> <p>l定与前面相同的输入Q该循环q行 7 ơP代,读输入中的空白以?qing)字W。该循环产生如下输出Q?/p> <pre><span>a b c</span><br> <span>d</span></pre> <h4>A.3.5. 未格式化的输入/输出操作</h4> <p>q今为止Q示例程序中只用过格式化的 IO 操作。输入和输出操作W(<tt><font face=NSimsun><<</font></tt> ?<tt><font face=NSimsun>>></font></tt>Q根据被处理数据的类型格式化所d的数据。输入操作符忽略I白Q输出操作符应用填充、精度等?/p> <p>标准库还提供?jin)丰富的支持未格式?IO 的低U操作,q些操作使我们能够将作为未解释的字节序列处理,而不是作为数据类型(?<tt><font face=NSimsun>char</font></tt>?tt><font face=NSimsun>int</font></tt>?tt><font face=NSimsun>string</font></tt> {)(j)的序列处理?/p> <img src ="http://www.shnenglu.com/dawnbreak/aggbug/85932.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/dawnbreak/" target="_blank">pear_li</a> 2009-05-27 17:53 <a href="http://www.shnenglu.com/dawnbreak/articles/85932.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Z事g解析的SAX模型分析http://www.shnenglu.com/dawnbreak/articles/85822.htmlpear_lipear_liTue, 26 May 2009 09:26:00 GMThttp://www.shnenglu.com/dawnbreak/articles/85822.htmlhttp://www.shnenglu.com/dawnbreak/comments/85822.htmlhttp://www.shnenglu.com/dawnbreak/articles/85822.html#Feedback0http://www.shnenglu.com/dawnbreak/comments/commentRss/85822.htmlhttp://www.shnenglu.com/dawnbreak/services/trackbacks/85822.html
  比如Q当你进行基于事件的~程的时候,你可以创建函数来响应用户定义的事Ӟ比如OnClick事gQ。在利用SAXq行~程的时候,需要注意的是,是解析器而不是用户生事件?br>
  比如考虑下面一个简单的文档?br>
  Q?xml version="1.0"?Q?br>  QpartsQ?br>  QpartQTurboWidgetQ?partQ?br>  Q?partsQ?br>
  当SAX2在处理这个文档的时候,它生如下的一pd的事Ӟ(x)

  StartDocument( )
  StartElement( "parts" )
  StartElement( "part" )
  Characters( "TurboWidget" )
  EndElement( "part" )
  EndElement( "parts" )
  EndDocument( )

  可以把SAX2看成是一个有拉特点(PUSHQ的解析器,SAX2产生事gQ然后你可以自己d理些事g。实际上Q当SAX2在解析一个文档的时候,SAXXMLReader读该文档q生一pd的事Ӟ你可以选择一些事件进行处理?br>
  创徏一个应用SAX的应用程序框?br>
  SAX2产生的事件包括如下的U类Q?br>
  ¨ 和XML文档内容相关的事?ISAXContentHandler)

  ¨ 和DTD相关的事?ISAXDTDHandler)

  ¨ 出现错误时发生的事g(ISAXErrorHandler)

  Z(jin)处理q些事gQ你需要实C个相关的处理c,该处理类需要包含一些方法来处理相关的事件。你必须对你惌处理的事件实现相关的处理。如果你不想处理某一个事件的话,只需要简单的忽略它就可以。在实际应用中,我们首先要承这些接口,用C++我们可以创徏一个类Q在q个cȝҎ(gu)中,我们可以告诉应用E序在接收到一个事件的时候如何进行处理。下面是建立一个基于SAX的应用的基本步骤Q?br>
  1Q?创徏头文件当使用SAX2的时候,我们需要用到动态连接库MSXML.DLL,Z(jin)使用MSXML中包含的SAX2接口Q你必须在程序的头文Ӟ一般在stdafx.h中)(j)中包含下列的代码Q?br>
  #import raw_interfaces_only


  using namespace MSXML2;

  2Q?建立具体的操作(handlerQ类QSAX2主要定义?jin)三个基本的操作c,它们分别是ISAXContentHandlerQISAXDTDHandler和ISAXErrorHandler?br>
  ISAXContentHandler是用来处理SAX2解析器对文档内容q行解析时所产生的消息的QISAXXMLReader通过Ҏ(gu)putContentHandler来注册这个实例。而ISAXDTDHandler是用来处理和DTD相关的基本的消息的,ISAXXMLReader通过Ҏ(gu)putDTDHandler来注册这个实例。ISAXErrorHandler提供?jin)对在解析过E中遇到错误时生的错误事g的处理,ISAXXMLReader通过Ҏ(gu)putErrorHandler来注册这个实?br>
  因ؓ(f)q三个类都是用来对事件进行处理的Qƈ且需要在接口ISAXXMLReader中进行注册。但是它们的基本使用Ҏ(gu)cMQ所以我们这里只详细描述Ҏ(gu)口ISAXContentHandler 的操作?br>
  ISAXContentHandler接口接收关于文档的内容变化的事gQ这是实现SAX应用所需要的最重要的接口,如果应用在遇到基本的解析事g的时候需要被通知的话QISAXXMLReader通过Ҏ(gu)putContentHandler来注册这个实例,然后ISAXXMLReader׃用这个实例来报告Z文档的事Ӟ比如元素的开始,元素的结束和相关的字W串数据{等。ISAXContentHandler 包括?jin)很多的?gu)Q比如startDocumentQendDocumentQstartElementQendElement{等。实际上它包含了(jin)好接个startXXX和endXXXҎ(gu)建立不同的信息集合的抽象。比如startDocumentҎ(gu)在文档信息开始的时候被调用Q而在startDocument以后被调用的Ҏ(gu)p认ؓ(f)是文档信息项QitemQ的子项。在文档信息内容l束的时候endDocumentp调用Q表C文档信息的l束?实际上是SAX2在解析文档的时候,当处于文档某一位置的时候,?x)激发相应的Ҏ(gu)Q比如当一个文档开始的时候,׃(x)Ȁ发startDocumentҎ(gu)Q在实际实现的时候,我们可以在我们承ISAXContentHandlercȝ实现cMQ重载该Ҏ(gu)Q实现我们自己想要的处理。我们可以把q些Ҏ(gu)看成是ISAXContentHandler接口提供l我们的。需要注意的是事件被处理的顺序和信息在文档中的位|是一致的?br>
  同时需要注意的是,如果我们需要在我们的应用中对这些消息进行处理的话,我们pl承处理q些消息的类Q比如我们只需要对文档内容q行处理Q而忽略对DTD和解析过E中错误(Error)的处理,那么我们只需要创Z个新的类Q该cȝ承ISAXContentHandler接口Q因为ISAXContentHandler中定义了(jin)很多的事件处理方法,而事实上我们只需要对我们所兛_(j)事g的处理方法进行重载,Ҏ(gu)们不兛_(j)的事件可以简单的忽略它?br>
  比如我们只关?j)startElement和endElement事gQ而且我们假设我们建立的类的名UCؓ(f)CXMLContentDealQ我们的cd可以如下面所C:(x)

  class CXMLContentDeal : public ISAXContentHandler
   {
    public:
     CXMLContentDeal();
     virtual CXMLContentDeal ();
      virtual HRESULT STDMETHODCALLTYPE startElement(
       /* [in] */ wchar_t __RPC_FAR *pwchNamespaceUri,
       /* [in] */ int cchNamespaceUri,
       /* [in] */ wchar_t __RPC_FAR *pwchLocalName,
       /* [in] */ int cchLocalName,
       /* [in] */ wchar_t __RPC_FAR *pwchRawName,
       /* [in] */ int cchRawName,
       /* [in] */ ISAXAttributes __RPC_FAR *pAttributes);
      virtual HRESULT STDMETHODCALLTYPE endElement(
       /* [in] */ wchar_t __RPC_FAR *pwchNamespaceUri,
       /* [in] */ int cchNamespaceUri,
       /* [in] */ wchar_t __RPC_FAR *pwchLocalName,
       /* [in] */ int cchLocalName,
       /* [in] */ wchar_t __RPC_FAR *pwchRawName,
       /* [in] */ int cchRawName);
    }

  然后我们可以重蝲Ҏ(gu)startElement和endElement来进行和应用相关的特D的处理?br>
  3Q?通过接口ISAXXMLReader创徏一个解析器。XMLReader是SAX应用实现的主要的接口QXMLReader的作用是q样的?首先QXML的开发h员用这个接口来注册他们对其他SAX接口的实玎ͼ比如ContentHandler,DTDHandler,ErrorHandler{等Q,另外QXMLREADER通过setFeature和setProperty两个Ҏ(gu)来配|SAX解析器的行ؓ(f)Q最后,XMLReader装?jin)解析的功能。示例代码如下:(x)

   ISAXXMLReader* pRdr = NULL;
   HRESULT hr = CoCreateInstance(
      __uuidof(SAXXMLReader),
      NULL,
      CLSCTX_ALL,
      __uuidof(ISAXXMLReader),
      (void **)&pRdr);

  4Q?创徏相应的事ӞhandlerQ处理类Q这里不妨假设我们只处理和文档内容相关的事g。示例代码如下:(x)

  CXMLContentDeal * pMc = new CXMLContentDeal();

  注意q里CXMLContentDeal是承接口ISAXContentHandler的类?br>
  5Q在解析器中注册事g处理c,CZ代码如下Q?br>
   hr = pRdr->putContentHandler(pMc);

  6Q开始进行文档的解析Q示例代码如?br>
   hr = pRdr->parseURL(URL); file://q里的URL是指一个具体XML文档的位|?br>
  7Q释放解析器对象

   pRdr->Release();

  以上是ZSAX的应用程序的框架l构Q我们可以看刎ͼ实际的事件处理是在我们的l承cCXMLContentDeal中实现的Q在我们q个CZ代码中,每当文档中一个新的元素开始的时候,都会(x)ȀzL法startElementQ每当一个元素结束的时候,都会(x)ȀzL法endElement。我们可以在startElement和endElement中写入和应用相关的特定的代码?

pear_li 2009-05-26 17:26 发表评论
]]>
STL中的排序法一览[By ACM郭老师] http://www.shnenglu.com/dawnbreak/articles/85685.htmlpear_lipear_liMon, 25 May 2009 03:51:00 GMThttp://www.shnenglu.com/dawnbreak/articles/85685.htmlhttp://www.shnenglu.com/dawnbreak/comments/85685.htmlhttp://www.shnenglu.com/dawnbreak/articles/85685.html#Feedback0http://www.shnenglu.com/dawnbreak/comments/commentRss/85685.htmlhttp://www.shnenglu.com/dawnbreak/services/trackbacks/85685.html详细的从法的效率方面来说明?jin)排序算法?br>
STL中有多种排序法Q各有各的适用范围Q下面听我一一道来Q?br>
I、完全排?/span>
sort()
首先要隆重推出的当然是最最常用的sort?jin),sort有两UŞ式,W一UŞ式有两个q代器参敎ͼ构成一个前开后闭的区_(d)按照元素?less 关系排序Q第二种形式多加一个指定排序准则的谓词。sort基本是最通用的排序函敎ͼ它用快速排序算法,q且在递归q程中,当元素数目小于一个阈|一般是16Q我的试验是24Q时Q{成直接插入排序。伟大的数学家Knuth已经证明Q在q_意义上,快速排序是最快的?jin);当然Q最坏复杂性比较差。sort要求随机q代器,因此对于很多~译器来_(d)对于前向q代器(如listQ用sort是一个编译错误?不过Q在vc2005里面Q这个错误信息实在很p糕)

sort的基本用方式如下:(x)

  1. C++:   
  2.   
  3. #include <vector>   
  4. #include <algorithm>   
  5. #include <functional>   
  6. #include <cstdlib>   
  7.     
  8. using namespace std;   
  9.     
  10. void func1()   
  11. {   
  12.     vector<int> ar;   
  13.     //向数l里面插入一些随机数   
  14.     generate_n(back_inserter(ar), 100, rand);   
  15.     //按从到大排?  
  16.     sort(ar.begin(), ar.end());   
  17. }    

l常有h问如何从大到逆排序,q个其实有很多中方式实现Q如下面的例子:(x)
  1. C++:   
  2.   
  3. void func2()   
  4. {   
  5.     vector<int> ar;   
  6.     //向数l里面插入一些随机数   
  7.     generate_n(back_inserter(ar), 100, rand);   
  8.     
  9.     //Ҏ(gu)1Q用函C?  
  10.     sort(ar.begin(), ar.end(), GreateThan);   
  11.     //Ҏ(gu)2Q用仿函数作ؓ(f)谓词   
  12.     //注意下面两种Ҏ(gu)都需要有个括P实际上是要生一个(f)时对?  
  13.     sort(ar.begin(), ar.end(), CompareInt());   
  14.     //Ҏ(gu)3Q用预定义的Adapter, 定义?nbsp;<functional> ?  
  15.     sort(ar.begin(), ar.end(), greater<int>());   
  16.     //Ҏ(gu)4Q正常排序,然后{q来   
  17.     sort(ar.begin(), ar.end());   
  18.     reverse(ar.begin(), ar.end());   
  19.     //Ҏ(gu)5Q用逆P代器   
  20.     sort(ar.rbegin(), ar.rend());   
  21. }    
  22.   

最后一U方法是我比较欣赏的Q可以不能直接对原生数组使用Q也是_(d)如果ar的定义是int ar[MAXN]Q上面其他的排序法都可以简单的Ҏ(gu)sort(ar, ar+MAXN, ...Q,但最后一个不行,要用另外一U比较丑陋的方式Q?br>
  1. C++:   
  2.   
  3. #include <iterator>   
  4. void func3(){   
  5.     int ax[5]={1,3,4,5,2};   
  6.     sort(reverse_iterator<int*>(ax+5), reverse_iterator<int*>(ax+0));   
  7. }    

stable_sort
sort优点一大堆Q一个缺点就是它不是一U稳定的排序。什么是排序的稳定性,是如果出现两个元素相等Ӟ要求排序之后他们之间保持原来的次序(比如我们先按学号排序Q然后按成W排序Q这时就希望成W相同的还是按照学L(fng)ơ序排)(j)。很可惜Q快速排序算法就不是E_的,要追求这个,只好用stable_sort?jin)?br>
在各U排序算法中Q合q排序是E_的,但一般的合ƈ排序需要额外的O(N)的存储空_(d)而这个条件不是一定能够满的Q可能是比较奢侈的)(j)。所以在stable_sort内部Q首先判断是否有_的额外空_(d)如vecotr中的cap-size()部分Q,有的话就使用普通合q函敎ͼȝ旉复杂性和快速排序一个数量Q都是O(N*logN)。如果没有额外空_(d)使用?jin)一个merge_without_buffer的关键函数进行就地合qӞ如何实现是比较有技巧的Q完全可以专门谈一谈)(j)Q这个合q过E不需要额外的存储I间Q但旉复杂度变成O(N*logN)Q这U情况下Qȝstable_sort旉复杂度是O(N*logN*logN)?br>
MQstable_sortE微慢一点儿Q但能够保证E_Q用方法和sort一栗但很多时候可以不用这U方式和q个函数Q比如上面的例子Q完全可以在排序比较准则中写入成l和学号两个条gO(jin)K?br>
  1. C++:   
  2.   
  3. class CStudent   
  4. {   
  5. public:   
  6.     CStudent();   
  7.     //注意q个比较函数中的const   
  8.     bool operator<(const CStudent& rhs) const  
  9.     {   
  10.         if (m_score != rhs.m_score)   
  11.             return (m_score <rhs.m_score);   
  12.         return m_name <rhs.m_name;   
  13.     }   
  14. protected:   
  15.     std::string m_name;   
  16.     int m_score;   
  17. };   
  18.     
  19. void func4()   
  20. {   
  21.     vector<CStudent> arStu;   
  22.     sort(arStu.begin(), arStu.end());   
  23. }    

sort_heap
堆排序也是一U快速的排序法Q复杂度也是O(N*logN)。STL中有一些和堆相关的函数Q能够构造堆Q如果在构造好的堆上每ơ取出来根节Ҏ(gu)在尾部,所有元素@环一遍,最后的l果也就有序?jin)。这是sort_heap?jin)。它的用要求区间前面已l构造成堆,如:(x)
  1. C++:   
  2.   
  3. void func5()   
  4. {   
  5.     vector<int> ar;   
  6.     generate_n(back_inserter(ar), 100, rand);   
  7.     make_heap(ar.begin(), ar.end());   
  8.     sort_heap(ar.begin(), ar.end());   
  9. }    

list.sort
对于list容器Q是不能直接使用sort的(包括stable_sortQ,从技术的角度来说是由于sort要求随机q代器;从算法的角度来说Qlistq种链表l构׃适合用快速排序。因此,list容器内部实现?jin)专门的sort法Q这个算法采用的是合q排序,应该是稳定的Q不定Q?br>
其他
优先队列Qpriority_queueQ每ơ弹出的都是max倹{实际上是heap的一个容器方式的包装?
兌式容器自w就必须是有序的Q针对keyQ,对其q代Ӟkey是递增的?
II、部分排?/span>
q些部分排序功能能够完成一D|据(而不是所有)(j)的排序,在适当的适合使用可以节省计算量。不q用的h不多?br>
partial_sort(), partial_sort_copy()
q两个函数能够将整个区间中给定数目的元素q行排序Q也是_(d)l果中只有最的M个元素是有序的。你当然也可以用sortQ区别就在于效率。如果M显著地小于NQ时间就比较短;当然M太小?jin)也不好Q那q不如挨个找最g(jin)?br>
partial_sort接受三个参数Q分别是区间的头Q中间和l尾。执行后Q将前面MQM=中间Q头Q个元素有序地放在前面,后面的元素肯定是比前面的大,但他们内部的ơ序没有保证。partial_sort_copy的区别在于把l果攑ֈ另外指定的P代器区间中:(x)
  1. C++:   
  2.   
  3. void func6()   
  4. {   
  5.     int ar[12]={69,23,80,42,17,15,26,51,19,12,35,8};   
  6.     //只排序前7个数?  
  7.     partial_sort(ar, ar+7, ar+12);   
  8.     //l果?nbsp;8 12 15 17 19 23 26 80 69 51 42 35Q后5个数据不?  
  9.     vector<int> res(7);   
  10.     //?Ҏ(gu)序后攑օres   
  11.     partial_sort_copy(ar, ar+7, res.begin(), res.end(), greater<int>() );   
  12. }    

q两个函数的实现使用的是堆的Ҏ(gu)Q先前M个元素构造成堆,然后挨个(g)查后面的元素Q看看是否小于堆的最大|是的话就彼此交换Q然后重排堆Q最后将前面已经是最的M个元素构成的堆作一ơsort_heap可以了(jin)。算法的复杂度差不多是O(N*logM)

nth_element
q个函数只真正排序出一个元素来Q就是第n个。函数有三个q代器的输入Q当然还可以加上一个谓词)(j)Q执行完毕后Q中间位|指向的元素保证和完全排序后q个位置的元素一_(d)前面区间的元素都于Q精地_(d)是不大于Q后面区间的元素?br>
熟?zhn)快速排序的马上p发现Q这实际上是一个按位置划分的算法。STL的规范中要求此函数的q_复杂度是U性的Q和快速排序一Pq种法的最坏复杂度比较差。在一般的实现Q如SGIQ中Q采用三U取1的方法寻扑ֈ分元素,最坏复杂度是O(N^N)。虽然理Z有一些算法可以保证最坏线性复杂度Q但法q于复杂QSTL一般也不采用?

III、排序辅助功?/span>
partition, stable_partition
merge, inplace_merge
IV、有序区间操?/span>

q个准备单独写一?br>
引用

让我们l期待郭老师的补充!

郭老师的boke地址 www.skywind.name/blog


pear_li 2009-05-25 11:51 发表评论
]]>
模板cȝ定义和实现要攑֜同一文g?/title><link>http://www.shnenglu.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.shnenglu.com/dawnbreak/articles/81704.html</guid><wfw:comment>http://www.shnenglu.com/dawnbreak/comments/81704.html</wfw:comment><comments>http://www.shnenglu.com/dawnbreak/articles/81704.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/dawnbreak/comments/commentRss/81704.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/dawnbreak/services/trackbacks/81704.html</trackback:ping><description><![CDATA[<p><font color=#000000>今天写程序,模板内的定义放在了(jin).h文g中,而实现放在了(jin).cpp中,</font></p> <p><font color=#000000>~译通过?jin),可连接怎么也不能成功。?/font></p> <p><font color=#000000>查了(jin)一些书才知道,模板cȝ定义和实现必L在同一文gQ?/font></p> <p><font color=#000000>《c++~程思想》中_(d)(x)模板cd义很Ҏ(gu)Q由template<...>定义的Q何东襉K意味着~译器在当时不ؓ(f)它分配内存空_(d)它一直处于等待状态,直到被一个模板实例告知,x板参数是q译器来替换的?nbsp;  <br>        Z(jin)Ҏ(gu)使用Q几乎L在头文g中放|全部的模板声明和定义。有Ӟ也可能ؓ(f)?jin)满特D需要而要在独立的cpp中放|模板的实现。但大部分现在的~译器还不支持模板类的定义和实现分开</font></p> <img src ="http://www.shnenglu.com/dawnbreak/aggbug/81704.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/dawnbreak/" target="_blank">pear_li</a> 2009-05-02 16:50 <a href="http://www.shnenglu.com/dawnbreak/articles/81704.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>帔R函数、常量引用参数、常量引用返回值[C++] http://www.shnenglu.com/dawnbreak/articles/80791.htmlpear_lipear_liWed, 22 Apr 2009 15:09:00 GMThttp://www.shnenglu.com/dawnbreak/articles/80791.htmlhttp://www.shnenglu.com/dawnbreak/comments/80791.htmlhttp://www.shnenglu.com/dawnbreak/articles/80791.html#Feedback0http://www.shnenglu.com/dawnbreak/comments/commentRss/80791.htmlhttp://www.shnenglu.com/dawnbreak/services/trackbacks/80791.html阅读全文

pear_li 2009-04-22 23:09 发表评论
]]>
最快的Hash?http://www.shnenglu.com/dawnbreak/articles/80536.htmlpear_lipear_liMon, 20 Apr 2009 06:25:00 GMThttp://www.shnenglu.com/dawnbreak/articles/80536.htmlhttp://www.shnenglu.com/dawnbreak/comments/80536.htmlhttp://www.shnenglu.com/dawnbreak/articles/80536.html#Feedback0http://www.shnenglu.com/dawnbreak/comments/commentRss/80536.htmlhttp://www.shnenglu.com/dawnbreak/services/trackbacks/80536.html一个简单的问题Q有一个庞大的字符串数l,然后l你一个单独的字符Ԍ让你从这个数l中查找是否有这个字W串q找到它Q你?x)怎么做?有一个方法最单,老?实实从头查到,一个一个比较,直到扑ֈ为止Q我惛_要学q程序设计的人都能把q样一个程序作出来Q但要是有程序员把这L(fng)E序交给用户Q我只能用无语来 评h(hun)Q或许它真的能工作,?/font>...也只能如此了(jin)?/span>

最合适的法自然是?/font>HashTableQ哈希表Q,先介l介l其中的基本知识Q所?/font>HashQ一般是一个整敎ͼ通过某种法Q可以把一个字W串"压羃" 成一个整数。当?dng)无论如何Q一?/font>32位整数是无法对应回一个字W串的,但在E序中,两个字符串计出?/font>Hash值相{的可能非常,下面看看?/font>MPQ中的Hash法Q?/font>

以下?/span>函数生成一个长度ؓ(f)0x500Q合10q制敎ͼ(x)1280Q的cryptTable[0x500]

void prepareCryptTable()

{

    unsigned long seed = 0x00100001, index1 = 0, index2 = 0, i;

 

    for( index1 = 0; index1 < 0x100; index1++ )

    {

        for( index2 = index1, i = 0; i < 5; i++, index2 += 0x100 )

        {

            unsigned long temp1, temp2;

 

            seed = (seed * 125 + 3) % 0x2AAAAB;

            temp1 = (seed & 0xFFFF) << 0x10;

 

            seed = (seed * 125 + 3) % 0x2AAAAB;

            temp2 = (seed & 0xFFFF);

 

            cryptTable[index2] = ( temp1 | temp2 );

       }

   }

}

以下函数计算lpszFileName 字符串的hash|其中dwHashType ?/span>hash的类型,在下?/span>GetHashTablePos函数里面调用本函敎ͼ其可以取的gؓ(f)0?/span>1?/span>2Q该函数q回lpszFileName 字符串的hash|

 

unsigned long HashString( char *lpszFileName, unsigned long dwHashType )

{

    unsigned char *key = (unsigned char *)lpszFileName;

unsigned long seed1 = 0x7FED7FED;

unsigned long seed2 = 0xEEEEEEEE;

    int ch;

 

    while( *key != 0 )

    {

        ch = toupper(*key++);

 

        seed1 = cryptTable[(dwHashType << 8) + ch] ^ (seed1 + seed2);

        seed2 = ch + seed1 + seed2 + (seed2 << 5) + 3;

    }

    return seed1;

}

Blizzard的这个算法是非常高效的,被称?/span>"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)。D个例子,字符?/span>"unitneutralacritter.grp"通过q个法得到的结果是0xA26067F3?/span>
  是不是把W一个算法改q一下,Ҏ(gu)逐个比较字符串的Hash值就可以?jin)呢Q答案是Q远q不够,要想得到最快的法Q就不能q行逐个的比较,通常是构造一个哈希表(Hash Table)来解决问题,哈希表是一个大数组Q这个数l的定wҎ(gu)E序的要求来定义Q例?/span>1024Q每一?/span>Hash值通过取模q算 (mod) 对应到数l中的一个位|,q样Q只要比较这个字W串的哈希值对应的位置又没有被占用Q就可以得到最后的l果?jin),xq是什么速度Q是的,是最快的O(1)Q现在仔l看看这个算法吧Q?br>

typedef struct

{

    int nHashA;

    int nHashB;

    char bExists;

   ......

} SOMESTRUCTRUE;

一U可能的l构体定义?

lpszString ?/span>hash表中查找的字W串Q?/span>lpTable 为存储字W串hash值的hash?/span>

 

int GetHashTablePos( har *lpszString, SOMESTRUCTURE *lpTable )

{

    int nHash = HashString(lpszString);

    int nHashPos = nHash % nTableSize;

 

    if ( lpTable[nHashPos].bExists && !strcmp( lpTable[nHashPos].pString, lpszString ) )

    {

        return nHashPos;

    }

    else

    {

        return -1;

    }

看到此,我想大家都在想一个很严重的问题:(x)“如果两个字符串在哈希表中对应的位|相同怎么办?”,毕竟一个数l容量是有限的,q种可能性很大。解册问题的方法很多,我首先想到的是?#8220;链表”,感谢大学里学的数据结构教?x)?jin)q个百试癄的法宝,我遇到的很多法都可以{化成链表来解冻I只要在哈希表的每个入口挂一个链表,保存所有对应的字符串就OK?jin)。事情到此似乎有?jin)完的l局Q如果是把问题独自交l我解决Q此时我可能p开始定义数据结构然后写代码?jin)。然?/span>Blizzard的程序员使用的方法则是更_֦的方法。基本原理就是:(x)他们在哈希表中不是用一个哈希D是用三个哈希值来校验字符丌Ӏ?/span>

MPQ使用文g名哈希表来跟t内部的所有文件。但是这个表的格式与正常的哈希表有一些不同。首先,它没有用哈希作Z标,把实际的文g名存储在表中用于验证Q实际上它根本就没有存储文g名。而是使用?/span>3U不同的哈希Q一个用于哈希表的下标,两个用于验证。这两个验证哈希替代?jin)实际文件名?/span>

当然?jin),q样仍然?x)出?/span>2个不同的文g名哈希到3个同L(fng)哈希。但是这U情况发生的概率q_?/span>1:18889465931478580854784Q这个概率对于Q何h来说应该都是_的。现在再回到数据l构上,Blizzard使用的哈希表没有使用链表Q而采?/span>"g"的方式来解决问题Q看看这个算法:(x)

lpszString ?/span>hash表中查找的字W串Q?/span>lpTable 为存储字W串hash值的hash表;nTableSize ?/span>hash表的长度Q?/span>

 

int GetHashTablePos( char *lpszString, MPQHASHTABLE *lpTable, int nTableSize )

{

    const int HASH_OFFSET = 0, HASH_A = 1, HASH_B = 2;

 

    int nHash = HashString( lpszString, HASH_OFFSET );

    int nHashA = HashString( lpszString, HASH_A );

    int nHashB = HashString( lpszString, HASH_B );

    int nHashStart = nHash % nTableSize;

    int nHashPos = nHashStart;

 

    while ( lpTable[nHashPos].bExists )

{

/*如果仅仅是判断在该表中时候存在这个字W串Q就比较q两?/span>hash值就可以?jin),不用?/span>

*l构体中的字W串q行比较。这样会(x)加快q行的速度Q减?/span>hash表占用的I间Q这U?/span>

*Ҏ(gu)一般应用在什么场合?*/

        if (   lpTable[nHashPos].nHashA == nHashA

&& lpTable[nHashPos].nHashB == nHashB )

{

return nHashPos;

}

          else

{

              nHashPos = (nHashPos + 1) % nTableSize;

}

 

          if (nHashPos == nHashStart)

              break;

    }

     return -1;

}

1.     计算出字W串的三个哈希|一个用来确定位|,另外两个用来校验)
2.
察看哈希表中的这个位|?/span>

3.
哈希表中q个位置为空吗?如果为空Q则肯定该字W串不存在,q回
4.
如果存在Q则(g)查其他两个哈希值是否也匚wQ如果匹配,则表C找C(jin)该字W串Q返?/font>
5.
Ud下一个位|,如果已经Ud?jin)表的末,则反l到表的开始位|vl箋查询 
6.
看看是不是又回到?jin)原来的位置Q如果是Q则q回没找?/font>
7.
回到3

补充1Q其他比较简单一些的hash函数Q?br>

/*keyZ个字W串Q?/span>nTableLength为哈希表的长?/span>

*该函数得到的hash值分布比较均匀*/

unsigned long getHashIndex( const char *key, int nTableLength )

{

    unsigned long nHash = 0;

  

    while (*key)

    {

        nHash = (nHash<<5) + nHash + *key++;

    }

       

    return ( nHash % nTableLength );

}

补充2Q?/span>

?希表的数l是定长的,如果太大Q则费Q如果太,体现不出效率。合适的数组大小是哈希表的性能的关键。哈希表的尺寸最好是一个质数。当?dng)?gu)不同的数 据量Q会(x)有不同的哈希表的大小。对于数据量时多时少的应用,最好的设计是用动态可变尺寸的哈希表,那么如果你发现哈希表寸太小?jin),比如其中的元素是?希表寸?/font>2倍时Q我们就需要扩大哈希表寸Q一般是扩大一倍。下面是哈希表尺寸大的可能取|(x)

17,           37,         79,         163,          331,

673,          1361,        2729,      471,        10949,       

21911,        43853,        87719,     175447,      350899,

701819,        1403641,     2807303,   5614657,   11229331,

22458671,      44917381,     89834777, 179669557, 359339171,

718678369,      1437356741, 2147483647

以下E序的源代码Q在linux下测试通过Q?br>


#include <stdio.h>


/*crytTable[]里面保存的是HashString函数里面会(x)用到的一些数据,在prepareCryptTable
*函数里面初始?/
unsigned long cryptTable[0x500];


/***********************************************************
*以下的函数生成一个长度ؓ(f)0x500Q合10q制敎ͼ(x)1280Q的cryptTable[0x500]
*
*
***********************************************************/
void prepareCryptTable()
{
    unsigned long seed = 0x00100001, index1 = 0, index2 = 0, i;

    for( index1 = 0; index1 < 0x100; index1++ )
    {
        for( index2 = index1, i = 0; i < 5; i++, index2 += 0x100 )
        {
            unsigned long temp1, temp2;

            seed = (seed * 125 + 3) % 0x2AAAAB;
            temp1 = (seed & 0xFFFF) << 0x10;

            seed = (seed * 125 + 3) % 0x2AAAAB;
            temp2 = (seed & 0xFFFF);

            cryptTable[index2] = ( temp1 | temp2 );
       }
   }
}


/***********************************************************
*以下函数计算lpszFileName 字符串的hash|其中dwHashType 为hash的类型,
*在下面GetHashTablePos函数里面调用本函敎ͼ其可以取的gؓ(f)0??Q该函数
*q回lpszFileName 字符串的hash|
***********************************************************/
unsigned long HashString( char *lpszFileName, unsigned long dwHashType )
{
    unsigned char *key = (unsigned char *)lpszFileName;
unsigned long seed1 = 0x7FED7FED;
unsigned long seed2 = 0xEEEEEEEE;
    int ch;

    while( *key != 0 )
    {
        ch = toupper(*key++);

        seed1 = cryptTable[(dwHashType << 8) + ch] ^ (seed1 + seed2);
        seed2 = ch + seed1 + seed2 + (seed2 << 5) + 3;
    }
    return seed1;
}

/***********************************************************
*在main中测试argv[1]的三个hash|(x)
* ./hash "arr\units.dat"
* ./hash "unit\neutral\acritter.grp"
***********************************************************/
int main( int argc, char **argv )
{
    unsigned long ulHashValue;
    int i = 0;

    if ( argc != 2 )
    {
        printf("please input two arguments\n");
        return -1;
    }

     /*初始化数l:(x)crytTable[0x500]*/
     prepareCryptTable();

     /*打印数组crytTable[0x500]里面的?/
     for ( ; i < 0x500; i++ )
     {
         if ( i % 10 == 0 )
         {
             printf("\n");
         }

         printf("%-12X", cryptTable[i] );
     }

     ulHashValue = HashString( argv[1], 0 );
     printf("\n----%X ----\n", ulHashValue );

     ulHashValue = HashString( argv[1], 1 );
     printf("----%X ----\n", ulHashValue );

     ulHashValue = HashString( argv[1], 2 );
     printf("----%X ----\n", ulHashValue );

     return 0;
}
 


pear_li 2009-04-20 14:25 发表评论
]]>
C/C++中const用法ȝhttp://www.shnenglu.com/dawnbreak/articles/79254.htmlpear_lipear_liWed, 08 Apr 2009 05:17:00 GMThttp://www.shnenglu.com/dawnbreak/articles/79254.htmlhttp://www.shnenglu.com/dawnbreak/comments/79254.htmlhttp://www.shnenglu.com/dawnbreak/articles/79254.html#Feedback0http://www.shnenglu.com/dawnbreak/comments/commentRss/79254.htmlhttp://www.shnenglu.com/dawnbreak/services/trackbacks/79254.html 

一?/span>C?/span>const的用法ȝh主要分ؓ(f)以下两种Q?/span>

       在定义变量时使用Q注意:(x)在定义变量时使用constQ一定要q行初始化操作)(j)Q?/span>

 最单的用法Q说明变量ؓ(f)一个常变量Q?/span>

 const int a=100;
  int const b=100;

说明指针为指向常数的指针Q即指针本n的值是可以改变的:(x)

 const int *a=&bQ?/span>

  int const *a=&bQ?/span>

说明指针本n的g可改变,但指向的内容可改?/span>Q?/span>
  int * const a = &bQ?/span>

说明指针为指向常数的常指?/span>,x针本w与指针指向的内定w不可改变Q?/span>

  const int * const a = &bQ?/span>

说明引用为常数引?/span>,即不能改变引用的|(x)

 const int &a=100?/span>

 

       在定义函数时使用Q?/span>

做ؓ(f)参数使用Q说明函C内是不能修改该参数的Q?/span>

   void func(const int a)Q?/span>

 void func(int const a)Q?/span>

做ؓ(f)q回g用,说明函数的返回值是不能被修改的Q?/span>

  const int func()Q?/span>

 在函C使用const,情况与定义变量的情况基本一_(d)(x) 

 int func()

{

Const int a=10Q?/span>

}

 

二?/span>C++中区别于C?/span>const用法主要分ؓ(f)以下两种

              constcL?/span>

        constcL员在对象构造期间允许被初始化ƈ且在以后不允许被改变?/span>constcL员和一般的const 变量有所不同?/span>constcL员是对应于每个对象而言Q它在对象构造期间被初始化,在这个对象的生存周期中不允许被改变?/span>
       const 成员函数

const 成员函数不允许在此函C内对此函数对应的cȝ所有成员变量进行修改,q样可以提高E序的健壮性?/span>Const一般放在函C后:(x)

void   fun()   const?/span> 



pear_li 2009-04-08 13:17 发表评论
]]>
?console mode 中?C/C++ ~译?/title><link>http://www.shnenglu.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.shnenglu.com/dawnbreak/articles/76947.html</guid><wfw:comment>http://www.shnenglu.com/dawnbreak/comments/76947.html</wfw:comment><comments>http://www.shnenglu.com/dawnbreak/articles/76947.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/dawnbreak/comments/commentRss/76947.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/dawnbreak/services/trackbacks/76947.html</trackback:ping><description><![CDATA[<li><font face=Verdana>CL.exe<br>1. cl /c [filename]   /c为只~译不链接的意思,默认cl.exe工具?x)在~译之后自动调用LINK.EXEq行链接<br>2. cl.exeq行需要指定include、lib{环境变?br>3. [filename]需要指定文件全名(包含后缀名)(j)<br>4. C/C++的编译是针对文gq行?/font> <li><font face=Verdana>lib.exe Microsoft库管理工?br>用于打包~译后的库文?obj)Q生成一个库文g(lib)</font> <br>obj文g和lib文g一P可以直接用于link.exe链接工具中,生成exe可执行文件。lib.exe的作用只是打包,多个obj打包Z? <li>link.exe 链接工具<br>link [file(s)]  /dll选项用来生成一个动态链接库(dll) <li><font face=Verdana>库文件的~写Q?br>库文件中只包含需要的函数?qing)数据即可,不需要main函数Q也不能有main函数<br></font><font face=Verdana>库文件的调用者,需要用extern关键字申明要调用的外部函?/font> <li><font face=Verdana>目前以lib后缀的库有两U,一Uؓ(f)静态链接库(Static   LibaryQ以下简U?#8220;静态库”)Q另一Uؓ(f)动态连接库(DLLQ以下简U?#8220;动态库”)的导入库(Import   LibaryQ以下简U?#8220;导入?#8221;Q?nbsp; <br>    静态库是一个或者多个obj文g的打包,所以有人干脆把从obj文g生成lib的过E称为ArchiveQ即合ƈC赗比如你链接一个静态库Q如果其中有错,它会(x)准确的找到是哪个obj有错Q即静态lib只是壛_?nbsp;<br>    动态库一般会(x)有对应的导入库,方便E序静态蝲入动态链接库Q否则你可能需要自己LoadLibary调入DLL文gQ然后再手工GetProcAddress获得对应函数?jin)。有?jin)导入库Q你只需要链接导入库后按照头文g函数接口的声明调用函数就可以?jin)?br>    导入库和静态库的区别很大,他们实质是不一L(fng)东西。静态库本n包含了(jin)实际执行代码、符可{等Q而对于导入库而言Q其实际的执行代码位于动态库中,导入库只包含?jin)地址W号表等Q确保程序找到对应函数的一些基本地址信息?/font> <li><font face=Verdana>动态链接库(dll)<br>一般ؓ(f)提高E序可读性,使用<font face=Verdana>#define DllExport __declspec(dllexport)</font>宏定义Dll中需要导出的函数Q在每个需要导出的函数前用DllExportQ例?font face=Verdana>DllExport int multiple(int x,int y)</font><br>生成一个Dll的方法:(x)1.使用cl.exe /c [filename]源文g生成库文?2.使用link.exe /dll [filename.obj]此obj文g生成dll<br>调用Dll的方法一Q?. 使用extern指定外部函数?2.在程序中直接使用要调用的Dll的函数名 3.链接时加入上一步骤中生成的库文?.lib)</font> <li>Ҏ(gu)二:(x)q行时动态加?br>C代码CZQ?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> return x*y;<br>}<br></font>//libCaller.c<br><font face=Verdana>#include "windows.h"<br>//extern  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>     </font><font face=Verdana>hf= LoadLibrary("lib.dll");<br>     if(hf!=0)<br>     {<br>          func = (LPFNDLLFUNC1)GetProcAddress(hf,"multiple");<br>          printf("Multiple:%d\r\n",func(5,12));<br>          FreeLibrary(hf);<br>    }<br>}</font> <li>Windows中的.lib文g分两U类?br>1.静态库文gQ用于编译时供其他对象引?2.动态链接库的导出文件。链接时使用Q无需使用LoadLibrary动态加载? <li><font face=Verdana>Link.exe工具在?def文g生成DLL时的两种命o(h)行语句:(x)<br>1. 如果DEF文g中没有LIBRARY语句Q需要同时指?DEF /DLL选项<br>2. 如果DEF文g中有LIBRARY语句Q只需指定/DEF选项卛_生成DLL</font> <li><font face=Verdana>Microsoft Visult Studio开发工具中CL.exe工具CL的意思及(qing)Compile和Link</font> <li><font face=Verdana>生成DLL的方式(listed in recommended order of useQ:(x)<br>1. 在源文g中用VC++自定义关键字__declspec(dllexport)<br>2. 使用包含EXPORTS语句?def文g<br>3. 使用Link.exe工具?EXPORT选项Q定义导出函敎ͼ(j)</font> <font face=Verdana> <li>//DEF源文件Sample<br>EXPORTS<br>add <li>//DLL源文件Sample<br>int add(int x,int y)<br>{<br> return x + y;<br>} <li>int substract(int x,int y)<br>{<br> return x-y;<br>} </li> </font> <p> </p> <p> </p> <p> </p> <p>我L鼓励 C/C++ 的学?fn)者,在刚接触q个E式语言的时候,先以 console modeQDOS-likeQ程式ؓ(f)目标。换a之,不要一开始就惛_ GUI E式、想开视窗、想有眩目亮丽的画面 -- 那只是未走先飞,揠苗助长|了(jin)?br>所?console E式Q就是文字模式的E式Q我们可以在其中好好?C/C++ 的语a根基l好Q而不?x)分心(j)於其他暂无必要?GUI 枝节上?br>我一直以为,q是理所当然的事情,却也一直发玎ͼ有不大专院校的大一 C/C++ 评Q同学们必须写个作家、小d、小盘┅做为期中或期末作业?br>果然世界不能大同Q各人看法殊?:)<br>我不但认?C/C++ E式开发对象初期要?console mode ZQ我也认为,C/C++ 的程式开发环境,初期也要?console mode Z。换a之,不要一开始就q入整合环境QIDEQ。整合环境中那麽多视H、那麽多功能、那麽多预设|?x)让E式新手D撩ؕQ无法掌握程式编译过E中一些有价值的知识与经验?br>{我们对~译E序有了(jin)L(fng)的了(jin)解,再来使用整合环境Q我认ؓ(f)q才最好?br>所以不论在 <深入出 MFC> ?<多型与虚?gt; 书籍中,我都?x)简qconsole mode 下的作业方式?lt;深入出 MFC> ?p.224 列出Q?lt;多型与虚?gt; ?p.233 列出?br>但仍然偶而会(x)收到|友Q不论是否上两本书的读者)(j)的询问,询问console mode 的编译方式,或询问他们所遭遇的问题?br>我再ơ整理这个题目。再有类似问题,我就可以整篇 mail l发问者了(jin)?br>★★ 注意Q以下适合 PC 环境 ★★<br>●C/C++ ~译器需要的环境变数讑֮<br>古早以来QPC 上的 C ~译器,需要两个环境变敎ͼ(x)<br>LIBQ这个环境变数告诉编译器_(d)必要?libraries 在哪里(哪个碟目录下)(j)<br>INCLUDEQ告诉编译器_(d)必要?header files 在哪里(哪个碟目录下)(j)<br>另外Qؓ(f)?jin)让我们能够在Q?working directory 都叫得到~译器,当然我们必须讑֮ PATH?br>从古早以来,一直到现在QC/C++ ~译器都需要这三个环境变数?br>●以 Visual C++ Z<br>?Visual C++ ZQ如果安装後的档案布局如下Q?br>C:\MSDEV\VC98\BIN : q里放有~译?CL.EXE<br>C:\MSDEV\VC98\INCLUDE : q里放有 C/C++ header files<br>C:\MSDEV\VC98\LIB : q里放有 C/C++ standard libraries<br>那麽你可以写一个批ơ档如下Q?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\BINQ是因ؓ(f)~译?CL.EXE 执行旉?MSPDB60.DLLQ而它被安装於 C:\MSDEV\COMMON\MSDEV98\BIN 之中?br>如果你写的程式不只是单纯?C/C++ E式Q还用到?MFCQ一样可以在 console mode 下编译,q时候你的环境变数应该如此设定:(x)<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>多指定了(jin) MFC\INCLUDE ?MFC\LIBQ就可以让编译器和联l器扑ֈ MFC ?header files ?libraries。如果你q需要用?ATLQ就得在 INCLUDE 环境变数中再加上 C:\MSDEV\VC98\ATL\INCLUDE?br>●以 Borland C++Builder Z<br>?Borland C++Builder ZQ如果安装後的档案布局如下Q?br>C:\BORLAND\CBuilder3\BIN : q里放有~译?BCC32.EXE<br>C:\BORLAND\CBuilder3\INCLUDE : q里放有 C/C++ header files<br>C:\BORLAND\CBuilder3\LIB : q里放有 C/C++ standard libraries<br>那麽你可以写一个批ơ档如下Q?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++ E式<br>首先Q开启一?DOS BoxQDOS Prompt, DOS VMQ,然後在该 DOS box 中执行上q写好的Ҏ(gu)档,完成环境变数的设定。你可以再在 DOS 提示号下键入 set 命o(h)Q看看环境变数的讑֮内容正确与否?br>然後可以直接在 DOS 提示号下键入~译器名Uͼ开始编译了(jin)。如果你使用 Visual C++Q就q麽做:(x)<br><small>C:\> CL test.cpp <enter></small><br>如果你?C++BuilderQ就q麽做:(x)<br><small>C:\> BCC32 test.cpp <enter></small><br>xҎ(gu)情况下需要什麽特D的 optionsQ就必须自己查一下啦。只要执?CL /? ?BCC32Q其後不加Q何引敎ͼ(j)Q便可看到所有的 compile options?br>●编译器与联l器的关p?br>早期的编译过E与联结q程是分开的。换句话说我们必d两个动作Q?br><small>C:\> Cl test.cpp<br>C:\> LINK test.obj xxx Qxxx 代表各个必要?librariesQ?/small><br>或是Q?br><small>C:\> BCC32 test.cpp<br>C:\> TLINK32 test.obj xxx Qxxx 代表各个必要?librariesQ?br></small><br>如今的编译过E与联结q程当然q是分开的,但是我们的动作只需一个:(x)<br><small>C:\> CL test.cpp</small><br>或是Q?br><small>C:\> BCC32 test.cpp</small><br>q是因ؓ(f)~译器变聪明?jin),除非你指?/c optionQ表C只~译不联l)(j)Q否则它便自动ؓ(f)你呼叫联l器q行联结动作。过M来颇?programmer?ch)恼的「该使用哪些 libraries」的问题Q编译器也有?jin)聪明的解决?gu)Q它?yu)程式中用到?library functions 记录hQ同时也录下它们所属的library 名称Q於是联l器可以从q个表格中知道要联结哪些 libraries ?jin)?br>●环境变C DOS VMQVirtual MachineQ的关系<br>你可以同时开起多?DOS BoxQ但是你不能够在某个 DOS Box 中执行上q批ơ档而在另一?DOS VM 中n受其环境讑֮?br>q是因ؓ(f)每个 DOS Box 都是一?Virtual MachineQ彼此谁也看不到谁,互不相干?br>除非你在 autoexec.bat 中就讑֮好上q那些环境变数。这麽一来,M一个新开启的 DOS VM 便会(x)因ؓ(f)l承最原始?DOS VM 环境Q而承了(jin)那些变数讑֮?br>●环境空_(d)environment spaceQ不?br>最易造成大家困扰的,是环境I间Qenvironment spaceQ不的问题?br>当你安装?Visual C++Q会(x)在其 BIN 子目录中发现一个名?VCVARS32.BAT 的档案。这个档案其实就是做上述的环境变数设定动作(q在 Visual C++ 安装q程的最後一个步骤有说明。哎Q有多少人安装Y体不看说明!Q。所以,你可以在M DOS Box 中执行此档,取代前述我们自己的批ơ档?br>但是通常大家都有p|的经验,得到 "<strong>Out of environment space</strong>" 的错误讯息。这是因?VCVARS32.BAT 使用以下句法Q?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%Q再附加其他讑֮Qƈ把LIB 的原始设定(%LIB%Q再附加其他讑֮。如果原始设定已l很长,多来q麽几次Q便 "Out of environment space" 啦!<br>做法之一是调高环境空间的大小。请?c:\config.sys 档中加上q行Q?br><small>shell=C:\COMMAND.COM C:\ /E:1024 /P</small><br>其中 /E:1024 便是表示环境空间调?1024 bytes。(不够Q再调)(j)<br>做法之二是不要?VCVARS32.BAT 的那U「附加」句型,改用前述我们自己的批ơ档。要知道Q我们可能有好几个编译器环境QVC、BCB、G++ ┅)(j)Q需要轮番测试我们的E式Q如果用「附加」句型,多来几次Q再大的环境I间也会(x)消磨D尽?br>Ҏ(gu)一和方法二要双齐下唷?br>●有M规模上的限制吗?<br>使用 console 模式Q或U?command line 模式Q来~译联结E式Q程式的大小可否有Q何规模上的限Ӟ{案是没有!<br>它的~点是没有工具帮你管理档案、没有预讑րD你少打几个字、没有分析工具帮你整?objectsQ让你浏?objects、symbols┅。所以一旦你基本功学?x)?jin)Q要开始中大型E式的设计,当然以整合环境(IDEQؓ(f)佟?br>●不要误?br>我这不是开倒RQ要大家回到Ҏ(gu)饮血的时代,都回头去做山洞人。而是我觉得,Ҏ(gu)一?C/C++ 初学者,整合环境QIDEQ的q用恐怕带来一头雾_(d)不如先在 console mode 下作业。一斚w多认识一些环境设定方面的常识Q满好的Q一斚w比较方便好用Q也不必写个 1000 行的小l习(fn)q得启动 五五加农炮,一斚w求知的力量可以全部放在语a的练?fn)上头?br>{有?jin)一定的E度Q再使用整合环境Q就不会(x)如坠五里雾了(jin)?/p> <img src ="http://www.shnenglu.com/dawnbreak/aggbug/76947.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/dawnbreak/" target="_blank">pear_li</a> 2009-03-18 00:00 <a href="http://www.shnenglu.com/dawnbreak/articles/76947.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Windows?POSIX U程~程http://www.shnenglu.com/dawnbreak/articles/76946.htmlpear_lipear_liTue, 17 Mar 2009 15:58:00 GMThttp://www.shnenglu.com/dawnbreak/articles/76946.htmlhttp://www.shnenglu.com/dawnbreak/comments/76946.htmlhttp://www.shnenglu.com/dawnbreak/articles/76946.html#Feedback0http://www.shnenglu.com/dawnbreak/comments/commentRss/76946.htmlhttp://www.shnenglu.com/dawnbreak/services/trackbacks/76946.html1. POSIX 标准

        POSIX是Portable Operating System Interface of Unix的羃写。由IEEEQInstitute of Electrical and Electronic EngineeringQ开发,由ANSI和ISO标准化?br>        POSIX的诞生和Unix的发展是密不可分的,Unix?0q代诞生于Bell labQƈ?0q代向美各大高校分发V7版的源码以做研究。UC Berkeley在V7的基上开发了(jin)BSD Unix。后来很多商业厂家意识到Unix的h(hun)gUL(fng)以Bell Lab的System V或BSD为基来开发自qUnixQ较著名的有Sun OSQAIXQVMS。由于各厂家对Unix的开发各自ؓ(f)政,造成?jin)Unix的版本相当؜乱,lY件的可移植性带来很大困难,对Unix的发展极Z利。ؓ(f)l束q种局面,IEEE开发了(jin)POSIXQPOSIX在源代码U别上定义了(jin)一l最的Unix(cUnix)操作pȝ接口?br>        POSIX 表示可移植操作系l接口(Portable Operating System Interface Q羃写ؓ(f) POSIX 是ؓ(f)?jin)读x?UNIXQ。电(sh)气和?sh)子工程师协会(x)(Institute of Electrical and Electronics EngineersQIEEEQ最初开?POSIX 标准Q是Z(jin)提高 UNIX 环境下应用程序的可移植性。然而,POSIX q不局限于 UNIX。许多其它的操作pȝQ例?DEC OpenVMS ?Microsoft Windows NTQ都支持 POSIX 标准Q尤其是 IEEE Std. 1003.1-1990Q?995 q修订)(j)?POSIX.1QPOSIX.1 提供?jin)源代码U别?C 语言应用~程接口QAPIQ给操作pȝ的服务程序,例如d文g。POSIX.1 已经被国际标准化l织QInternational Standards OrganizationQISOQ所接受Q被命名?ISO/IEC 9945-1:1990 标准?
        POSIX 现在已经发展成ؓ(f)一个非常庞大的标准族,某些部分正处在开发过E中。表 1-1 l出?POSIX 标准的几个重要组成部分。POSIX ?IEEE 1003 ?2003 家族的标准是可互换的。除 1003.1 之外Q?003 ?2003 家族也包括在表中?

?Q标准的重要l成部分
1003.0
理 POSIX 开攑ּpȝ环境QOSEQ。IEEE ?1995 q通过?jin)这?gu)准?ISO 的版本是 ISO/IEC 14252:1996?
1003.1
被广泛接受、用于源代码U别的可UL性标准?003.1 提供一个操作系l的 C 语言应用~程接口QAPIQ。IEEE ?ISO 已经?1990 q通过?jin)这个标准,IEEE ?1995 q重C订了(jin)该标准?
1003.1b
一个用于实时编E的标准Q以前的 P1003.4 ?POSIX.4Q。这个标准在 1993 q被 IEEE 通过Q被合ƈq?ISO/IEC 9945-1?
1003.1c
一个用于线E(在一个程序中当前被执行的代码D)(j)的标准。以前是 P1993.4 ?POSIX.4 的一部分Q这个标准已l在 1995 q被 IEEE 通过Q归?ISO/IEC 9945-1:1996?
1003.1g
一个关于协议独立接口的标准Q该接口可以使一个应用程序通过|络与另一个应用程序通讯?1996 q_(d)IEEE 通过?jin)这个标准?
1003.2
一个应用于 shell ?工具软g的标准,它们分别是操作系l所必须提供的命令处理器和工L(fng)序?1992 q?IEEE 通过?jin)这个标准。ISO 也已l通过?jin)这个标准(ISO/IEC 9945-2:1993Q?
1003.2d
改进?1003.2 标准?
1003.5
一个相当于 1003.1 ?Ada 语言?API。在 1992 q_(d)IEEE 通过?jin)这个标准。ƈ?1997 q对其进行了(jin)修订。ISO 也通过?jin)该标准?
1003.5b
一个相当于 1003.1bQ实时扩展)(j)?Ada 语言?API。IEEE ?ISO 都已l通过?jin)这个标准。ISO 的标准是 ISO/IEC 14519:1999?
1003.5c
一个相当于 1003.1qQ协议独立接口)(j)?Ada 语言?API。在 1998 q_(d) IEEE 通过?jin)这个标准。ISO 也通过?jin)这个标准?
1003.9
一个相当于 1003.1 ?FORTRAN 语言?API。在 1992 q_(d)IEEE 通过?jin)这个标准,q于 1997 q对其再ơ确认。ISO 也已l通过?jin)这个标准?
1003.10
一个应用于计算应用环境框架QApplication Environment ProfileQAEPQ的标准。在 1995 q_(d)IEEE 通过?jin)这个标准?
1003.13
一个关于应用环境框架的标准Q主要针对?POSIX 接口的实时应用程序。在 1998 q_(d)IEEE 通过?jin)这个标准?
1003.22
一个针?POSIX 的关于安全性框架的指南?
1003.23
一个针对用L(fng)l的指南Q主要是Z(jin)指导用户开发和使用支持操作需求的开攑ּpȝ环境QOSEQ框?
2003
针对指定和用是否符?POSIX 标准的测试方法,有关其定义、一般需求和指导斚w的一个标准。在 1997 q_(d)IEEE 通过?jin)这个标准?
2003.1
q个标准规定?jin)针?1003.1 ?POSIX 试Ҏ(gu)的提供商要提供的一些条件。在 1992 q_(d)IEEE 通过?jin)这个标准?
2003.2
一个定义了(jin)被用来检查与 IEEE 1003.2Qshell ?工具 APIQ是否符合的试Ҏ(gu)的标准。在 1996 q_(d)IEEE 通过?jin)这个标准?

       除了(jin) 1003 ?2003 家族以外Q还有几个其它的 IEEE 标准Q例?1224 ?1228Q它们也提供开发可UL应用E序?API。要惛_到关?IEEE 标准的最C息,可以讉K IEEE 标准的主,|址?http://standards.ieee.org/。有?POSIX 标准的概qC息,误?Web 站点 http://standards.ieee.org/reading/ieee/stad_public/description/posix/?/p>

2. Liniux下的U程~程

        Linuxpȝ下的多线E遵循POSIXU程接口Q称为pthread。从上面的描qC隄道,POSIXU程接口是POSIX众多标准中的一个(POSIX 1003.1-2001Q?/strong>?/p>

        ~写Linux下的多线E程序,需要用头文gpthread.hQ连接时需要用库libpthread.a。顺便说一下,Linux下pthread的实现是通过pȝ调用 clone() 来实现的。clone() 是Linux所Ҏ(gu)的系l调用,它的使用方式cMforkQ关?clone() 的详l情况,有兴的读者可以去查看有关文档说明?/p>

下面是一?POSIX U程的简单示例程?thread1.c)Q?

#include <pthread.h>
#include 
<stdlib.h>
#include 
<unistd.h>
 
void *thread_function(void *arg) {
  
int i;
  
for ( i=0; i<20; i++{
    printf(
"Thread says hi! ");
    sleep(
1);
  }

  
return NULL;
}

int main(void{
  pthread_t mythread;
  
  
if ( pthread_create( &mythread, NULL, thread_function, NULL) ) {
    printf(
"error creating thread.");
    abort();
  }

  
if ( pthread_join ( mythread, NULL ) ) {
    printf(
"error joining thread.");
    abort();
  }

  exit(
0);
}

 要编译这个程序,只需先将E序存ؓ(f) thread1.cQ然后输入:(x)

$ gcc thread1.c -o thread1 -lpthread

q行则输入:(x)

$ ./thread1

3. Windows下POSIXU程~程

       Windows本n没有提供对POSIX的支持。但有一个叫 POSIX Threads for Win32 的开源项目给Z(jin)一个功能比较完善的Windows下pthreads API的实现。目前的最新版本是Pthreads-w32 release 2.8.0 (2006-12-22)?/p>

       我没有测试过q个最新版本,q里只给?.7.0版的链接Q?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">ftp://sources.redhat.com/pub/pthreads-win32/pthreads-w32-2-7-0-release.exe?/p>

       关于该开源项目的详细介绍见:(x)http://sources.redhat.com/pthreads-win32/?/p>

3.1 单?/strong>

       上面的exe文g是一个自解压文gQ解压后QPre-built.2目录中有~译所需要的头文Ӟinclude子目录)(j)和库文gQlib子目录)(j)?/p>

       一个简单的试E序(main.cpp)Q?/p>

#include <stdio.h>
#include 
<pthread.h>
#include 
<assert.h>

void* Function_t(void* Param)
{
    printf(
"I am a thread!  ");
    pthread_t myid 
= pthread_self();
    printf(
"thread ID=%d ", myid);
    
return NULL;
}


int main()
{
    pthread_t pid;
    pthread_attr_t attr;
    pthread_attr_init(
&attr);
    pthread_attr_setscope(
&attr, PTHREAD_SCOPE_PROCESS);
    pthread_attr_setdetachstate(
&attr, PTHREAD_CREATE_DETACHED);
    pthread_create(
&pid, &attr, Function_t, NULL);
    printf(
"======================================== ");
    getchar();
    pthread_attr_destroy(
&attr);
    
return 1;
}
 

使用 cl.exe ~译Q不熟?zhn)?cl.exe 的请参考:(x)http://blog.csdn.net/liuyongjin1984/archive/2008/01/07/2029405.aspx 或者参见下?.2部分Q:(x)

?span style="COLOR: #000000">rem cl.bat

》cl.exe main.cpp  //I"c:pthreads-w32-2-7-0-releasePre-built.2include"

》link.exe 
/out:main_cl.exe  main.obj  /LIBPATH:"c:pthreads-w32-2-7-0-releasePre-built.2lib"   pthreadVC2.lib

 3.2 使用VC++ 6.0或Visual Studio 2005来运行上面的E序

关键有两点:(x)

1. 是将头文Ӟinclude子目录)(j)和库文gQlib子目录)(j)中的内容d到VC++ 6.0或Visual Studio 2005开发环境对应的include和lib目录下?/p>

具体来说Q?strong>以添加include目录ZQ添加lib目录cMQ:(x)

?QVC++ 6.0Q添加include目录Q工?-》选项--》目录)(j)

 

 

?QVisual Studio 2005(dinclude目录Qtools--》options)

 

 

2. 指定link时要q接的库的名UͼpthreadVC2.libQ?/strong>

?QVC++ 6.0Q工E?-》设|?-》连接)(j)

?QVisual Studio 2005(project-->* property pages)

 

4. 书籍推荐
<< POSIX Multithread Programming Primer >>: http://download.csdn.net/source/237125



pear_li 2009-03-17 23:58 发表评论
]]>
Reversing MS VC++ Part II: Classes, Methods and RTTIhttp://www.shnenglu.com/dawnbreak/articles/76231.htmlpear_lipear_liWed, 11 Mar 2009 07:37:00 GMThttp://www.shnenglu.com/dawnbreak/articles/76231.htmlhttp://www.shnenglu.com/dawnbreak/comments/76231.htmlhttp://www.shnenglu.com/dawnbreak/articles/76231.html#Feedback0http://www.shnenglu.com/dawnbreak/comments/commentRss/76231.htmlhttp://www.shnenglu.com/dawnbreak/services/trackbacks/76231.html阅读全文

pear_li 2009-03-11 15:37 发表评论
]]>
C++对象内存模型http://www.shnenglu.com/dawnbreak/articles/76230.htmlpear_lipear_liWed, 11 Mar 2009 07:36:00 GMThttp://www.shnenglu.com/dawnbreak/articles/76230.htmlhttp://www.shnenglu.com/dawnbreak/comments/76230.htmlhttp://www.shnenglu.com/dawnbreak/articles/76230.html#Feedback0http://www.shnenglu.com/dawnbreak/comments/commentRss/76230.htmlhttp://www.shnenglu.com/dawnbreak/services/trackbacks/76230.html/*
 *C++中非多态类对象的内存映像模?br> *
 *
 *
 */
#include <iostream>

using namespace std;

class rectangle
{
    public:
  rectangle():m_length(1), m_width(1){}
  ~rectangle(){}
  float GetLength() const
  {
      return m_length;
  }
  void SetLength(float length)
  {
      m_length = length;
  }
  float GetWidth() const
  {
      return m_width;
  }
  void SetWidth(float width)
  {
      m_width = width;
  }
  void Draw() const 
  {
      //...
      //...
      //...
  }
  static unsigned int GetCount()
  {
      return m_count;
  }

    protected:
  rectangle(const rectangle & copy)
  {
      //...
      //...
      //...
  }
  rectangle& operator=(const rectangle & assign)
  {
      //...
      //...
      //...
  }

    private:
  float m_length;
  float m_width;
  static unsigned int m_count;
};
int main()
{
    rectangle rect;
    rect.SetWidth(10);
    cout<<rect.GetWidth()<<endl;
    return 0;
}
/*
 *有如下规则:(x)
非静态数据成员被攑֜每一个对象体内作为对象专有的数据成员
静态数据成员被提取出来攑֜E序的静态数据区内,cL有对象共享,因此只存在一份?br>静态和非静态成员函数最l都被提取出来放在程序的代码D中qؓ(f)该类所有对象共享,因此每一个成员函C只能存在一份代码实体?br>因此Q构成对象本w的只有数据QQ何成员函数都不隶属于M一个对象,非静态成员函C对象的关pd是绑定,l定的中介就是this指针?br>成员函数cL有对象共享,不仅是处于简化语a实现、节省存储的目的Q而且是ؓ(f)?jin)同类对象有一致的行ؓ(f)。同cd象的行ؓ(f)虽然一_(d)但是操作不同的数据成员?br> */



pear_li 2009-03-11 15:36 发表评论
]]>通过重蝲new和delete实现单的对象?http://www.shnenglu.com/dawnbreak/articles/76229.htmlpear_lipear_liWed, 11 Mar 2009 07:33:00 GMThttp://www.shnenglu.com/dawnbreak/articles/76229.htmlhttp://www.shnenglu.com/dawnbreak/comments/76229.htmlhttp://www.shnenglu.com/dawnbreak/articles/76229.html#Feedback1http://www.shnenglu.com/dawnbreak/comments/commentRss/76229.htmlhttp://www.shnenglu.com/dawnbreak/services/trackbacks/76229.html通过重蝲new和delete实现单的对象?

对象池的用途在q里׃介绍?jin),本例中只是其一个简单的实现?

#include <list>

#include <iostream>
using namespace std;

template<class T>
class object_pool
{
    list<void *> data_list;
public:
    void* malloc_data()
    {
        if(!data_list.empty())
        {
            list<void *>::iterator it = data_list.begin();
            void *p = *it;
            data_list.pop_front();
            return p;
        }
        else
        {
            void* p = malloc(sizeof(T));
            return p;
        }
    }

    void free_data(void* p)
    {
        data_list.push_back(p);
    }
};

class A
{
public:
    int value;
    A(int a):value(a){cout<<"A("<<a<<") called"<<endl;}
    ~A() {cout<<"~A("<<value<<") called"<<endl;}

    static object_pool<A> pool_;
    void* operator new (size_t size)
    {
        return pool_.malloc_data();
    }

    void operator delete(void* p)
    {
        pool_.free_data(p);
    }

};

object_pool<A> A::pool_;

void main()
{
    
    A* a1=new A(1);
    delete a1;
    
    A* a2=new A(2);
    delete a2;
}



pear_li 2009-03-11 15:33 发表评论
]]>
C++对象内存模型http://www.shnenglu.com/dawnbreak/articles/76224.htmlpear_lipear_liWed, 11 Mar 2009 06:36:00 GMThttp://www.shnenglu.com/dawnbreak/articles/76224.htmlhttp://www.shnenglu.com/dawnbreak/comments/76224.htmlhttp://www.shnenglu.com/dawnbreak/articles/76224.html#Feedback0http://www.shnenglu.com/dawnbreak/comments/commentRss/76224.htmlhttp://www.shnenglu.com/dawnbreak/services/trackbacks/76224.html

׃U种原因Q我们无法保证所有通过new在heap中申L(fng)内存资源都安全地得到释放Q例如:(x)

class base{...};
void f()
{
   base* ptr = new base;
   ...
   delete ptr;
}

q样一pd操作中,?..."中可能会(x)抛出异常Q最l导致delete无法得到执行Q于是就造成?jin)内存泄霌Ӏ尽我们可以设法避免在"..."中抛出异常,但是Q对于后来维护和修改q段代码?jin)h员来_(d)很可能加入一些控制流Q从而过早地从函C中返回,内存泄露再次发生?/p>

一U解决之道就是,资源封装在一个类中,利用cȝ析构函数来释放该资源Q这样一来,无论是何U方式退出f()函数的作用域Q类的析构函数L?x)被调用Q从而有效地避免?jin)资源泄霌Ӏ?/p>

auto_ptr是q种Ҏ(gu)的一U典型应用。它被称为智能指针,是一个模板类。声明一个只能指针的Ҏ(gu)是:(x)auto_ptr ptr(new T);Z(jin)说明auto_ptr的用法,下面是一个具体的例子Q?/p>

#include <iostream>
#include <memory>      //Z(jin)使用auto_ptr你必d含头文g:#include <memory>
using namespace std;

class demo
{
public:
  demo ()
  {
    cout << "demo()" << endl;
  }
   ~demo ()
  {
    cout << "~demo()" << endl;
  }
};
void
test_org ()
{
  cout << "test_org():" << endl;
  demo *ptr = new demo;
  return;
}

void
test_auto ()
{
  cout << "test_auto():" << endl;
  auto_ptr < demo > ptr (new demo);
  return;
}

int
main (int narg, char **arg)
{
  test_org ();
  test_auto ();
  return 0;
}

linux下,g++~译器的输出l果如下Q?br>~$ g++ test.cpp -o test
~$ ./test
test_org():
demo()
test_auto():
demo()
~demo()



pear_li 2009-03-11 14:36 发表评论
]]>
如何(g)内存泄漏——重载new和deletehttp://www.shnenglu.com/dawnbreak/articles/76223.htmlpear_lipear_liWed, 11 Mar 2009 06:33:00 GMThttp://www.shnenglu.com/dawnbreak/articles/76223.htmlhttp://www.shnenglu.com/dawnbreak/comments/76223.htmlhttp://www.shnenglu.com/dawnbreak/articles/76223.html#Feedback0http://www.shnenglu.com/dawnbreak/comments/commentRss/76223.htmlhttp://www.shnenglu.com/dawnbreak/services/trackbacks/76223.html本文可以被自p{载,但是必须遵@如下版权U定Q?
1、保留本U定Qƈ保留在文章的开头部分?
2、不能Q意修Ҏ(gu)章内容,或者删节,增加。如果认为本文内Ҏ(gu)不当之处需要修改,?
与作者联pR?
3、不能摘抄本文的内容Q必d文发表或者引用?
4、必M留作者v名、注明文章出处。(本文授权lwww.linuxaid.com.cnQ?
5、如不遵守本规定Q则无权转蝲本文?nbsp;
作?
ariesram
?sh)子邮g地址
ariesram@linuxaid.com.cn, ?nbsp;ariesram@may10.ca
本文?qing)本人所有文章均攉在bambi.may10.ca/~ariesram/articles/中?
本文授权lwww.linuxaid.com.cn?

正文Q?
我曾l参与过一个比较大的项目,在这个项目里面,我们没有一个完全确定的设计文档Q所以程序的实现常常变动。虽然我们有一个比较灵zȝ框架Q但是从E序的角度来Ԍ它我们的程序非常的混ؕ。直到发布的日期临近Q我们还没有一个稳定的可以用来做alpha试的版本。所以我们必d快的删除掉无用的代码Q让q个版本_的稳定。但是,在这个没有够规范的软g公司Q我们没有时间也没有_的精力来做边界测试之cȝ工作。所以我们只能采用变通的办法。在软g中最大的问题是内存泄漏。因为往往出现q样的情况,我们在一D代码中分配?jin)内存,但是却没有释攑֮。这造成?jin)很大的问题。我们需要一个简单的解决Ҏ(gu)Q能够简单的~译q这个项目,在运行的时候,它能够生一个没有被释放的内存的列表Q用q个列表Q我们能够改正程序的错误。这是我们UC为内存跟t的Ҏ(gu)。首先,我们需要一U代码,能够被加入到源代码中去,而且q种代码能够被重用。代码重用是一U很重要的特性,能够节省大量的时间和金钱以及(qing)E序员的力_。另外,我们的这U代码必ȝ单,因ؓ(f)我们当时已经没有那么多的旉和精力去完全重看一遍所有的代码来重新编写以?qing)改正错误从而内存跟踪能够起作用?

好在Q我们总能够找到解决的办法。首先,我们(g)查了(jin)代码Q发现所有的代码都是用new来分配内存,用delete来释攑ֆ存。那么,我们能够用一个全E替换,来替换掉所有的new和delete操作W吗Q不能。因Z码的规模太大?jin),那样做除了(jin)浪?gu)间没有别的Q何好处。好在我们的源代码是用C++来写成的Q所以,q意味着没有必要替换掉所有的new和delete,而只用重载这两个操作W。对?jin),值用重蝲q两个操作符Q我们就能在分配和释攑ֆ存之前做点什么。这是一个绝对的好消息。我们也知道该如何去做。因为,MFC也是q么做的。我们需要做的是Q跟t所有的内存分配和交互引用以?qing)内存释放。我们的源代码用Visual C++写成Q当然这U解x法也可以很轻杄使用在别的C++代码里面。要做的W一件事情是重蝲new和delete操作W,它们会(x)在所有的代码中被使用到。我们在stdafx.h中,加入Q?
#ifdef _DEBUG
inline void * __cdecl operator new(unsigned int size, 
const char *file, int line)
{
};

inline void __cdecl operator delete(void *p)
{
};
#endif
q样Q我们就重蝲?jin)new和delete操作W。我们用$ifdef?endif来包住这两个重蝲操作W,q样Q这两个操作W就不会(x)在发布版本中出现。看一看这D代码,?x)发玎ͼnew操作W有三个参数Q它们是Q分配的内存大小Q出现的文g名,和行受这对于L内存泄漏是必需的和重要的。否则,׃(x)需要很多时间去L它们出现的确切地斏V加入了(jin)q段代码Q我们的调用new()的代码仍然是指向只接受一个参数的new操作W,而不是这个接受三个参数的操作W。另外,我们也不惌录所有的new操作W的语句d含__FILE__和__LINE__参数。我们需要做的是自动的让所有的接受一个参数的new操作W调用接受三个参数的new操作W。这一点可以用一点点的技巧去做,例如下面的这一D宏定义Q?
#ifdef _DEBUG
#define DEBUG_NEW new(__FILE__, __LINE__)
#else
#define DEBUG_NEW new
#endif
#define new DEBUG_NEW
现在我们所有的接受一个参数的new操作W都成ؓ(f)?jin)接受三个参数的new操作W号Q__FILE__和__LINE__被预~译器自动的插入到其中了(jin)。然后,是作实际的跟踪?jin)。我们需要加入一些例E到我们的重载的函数中去Q让它们能够完成分配内存和释攑ֆ存的工作。这h做, #ifdef _DEBUG
inline void * __cdecl operator new(unsigned int size,
const char *file, int line)
{
void *ptr = (void *)malloc(size);
AddTrack((DWORD)ptr, size, file, line);
return(ptr);
};
inline void __cdecl operator delete(void *p)
{
RemoveTrack((DWORD)p);
free(p);
};
#endif
另外Q还需要用相同的方法来重蝲new[]和delete[]操作W。这里就省略掉它们了(jin)?
最后,我们需要提供一套函数AddTrack()和RemoveTrack()。我用STL来维护存储内存分配记录的q接表?
q两个函数如下:(x)
typedef struct {
DWORD address;
DWORD size;
char file[64];
DWORD line;
} ALLOC_INFO;

typedef list<ALLOC_INFO*> AllocList;

AllocList *allocList;

void AddTrack(DWORD addr, DWORD asize, const char *fname, DWORD lnum)
{
ALLOC_INFO *info;

if(!allocList) {
allocList = new(AllocList);
}

info = new(ALLOC_INFO);
info->address = addr;
strncpy(info->file, fname, 63);
info->line = lnum;
info->size = asize;
allocList->insert(allocList->begin(), info);
};

void RemoveTrack(DWORD addr)
{
AllocList::iterator i;

if(!allocList)
return;
for(i = allocList->begin(); i != allocList->end(); i++)
{
if((*i)->address == addr)
{
allocList->remove((*i));
break;
}
}
};
现在Q在我们的程序退Z前,allocList存储?jin)没有被释放的内存分配。ؓ(f)?jin)看到它们是什么和在哪里被分配的,我们需要打印出allocList中的数据。我使用?jin)Visual C++中的OutputH口来做qg事情?
void DumpUnfreed()
{
AllocList::iterator i;
DWORD totalSize = 0;
char buf[1024];

if(!allocList)
return;

for(i = allocList->begin(); i != allocList->end(); i++) {
sprintf(buf, "%-50s: LINE %d, ADDRESS %d %d unfreed ",
(*i)->file, (*i)->line, (*i)->address, (*i)->size);
OutputDebugString(buf);
totalSize += (*i)->size;
}
sprintf(buf, "----------------------------------------------------------- ");
OutputDebugString(buf);
sprintf(buf, "Total Unfreed: %d bytes ", totalSize);
OutputDebugString(buf);
};
现在我们有?jin)一个可以重用的代码Q用来监跟t所有的内存泄漏?jin)。这D代码可以用来加入到所有的目中去。虽然它不会(x)让你的程序看h更好Q但是v码它能够帮助你检查错误,让程序更加的E_?

pear_li 2009-03-11 14:33 发表评论
]]>
C++的类型{换符Qstatic_cast、dynamic_cast、reinterpret_cast、和const_cast 很全?/title><link>http://www.shnenglu.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.shnenglu.com/dawnbreak/articles/68122.html</guid><wfw:comment>http://www.shnenglu.com/dawnbreak/comments/68122.html</wfw:comment><comments>http://www.shnenglu.com/dawnbreak/articles/68122.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/dawnbreak/comments/commentRss/68122.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/dawnbreak/services/trackbacks/68122.html</trackback:ping><description><![CDATA[<div id="ooga0ig" class="postText"> <p style="font-family: 新宋?"><font size="2">使用标准C++的类型{换符Qstatic_cast、dynamic_cast、reinterpret_cast、和const_cast?/font></p> <p style="font-family: 新宋?"><font size="2">3.1 static_cast<br>用法Qstatic_cast < type-id > ( expression )      <br>该运符把expression转换为type-idcdQ但没有q行时类型检查来保证转换的安全性。它主要有如下几U用法:(x)<br>①用于类层次l构中基cd子类之间指针或引用的转换?br>  q行上行转换Q把子类的指针或引用转换成基c表C)(j)是安全的Q?br>  q行下行转换Q把基类指针或引用{换成子类表示Q时Q由于没有动态类型检查,所以是不安全的?br>②用于基本数据类型之间的转换Q如把int转换成charQ把int转换成enum。这U{换的安全性也要开发h员来保证?br>③把I指针{换成目标cd的空指针?br>④把Mcd的表辑ּ转换成voidcd?/font></p> <p style="font-family: 新宋?"><font size="2">注意Qstatic_cast不能转换掉expression的const、volitale、或者__unaligned属性?/font></p> <p style="font-family: 新宋?"><br><font size="2">3.2 dynamic_cast<br>用法Qdynamic_cast < type-id > ( expression )<br>该运符把expression转换成type-idcd的对象。Type-id必须是类的指针、类的引用或者void *Q?br>如果type-id是类指针cdQ那么expression也必L一个指针,如果type-id是一个引用,那么expression也必L一个引用?/font></p> <p style="font-family: 新宋?"><font size="2">dynamic_cast主要用于cdơ间的上行{换和下行转换Q还可以用于cM间的交叉转换?br>在类层次间进行上行{换时Qdynamic_cast和static_cast的效果是一L(fng)Q?br>在进行下行{换时Qdynamic_casthcd(g)查的功能Q比static_cast更安全?br>class B{<br>public:<br>       int m_iNum;<br>       virtual void foo();<br>};</font></p> <p style="font-family: 新宋?"><font size="2">class D:public B{<br>    public:<br>       char *m_szName[100];<br>};</font></p> <p style="font-family: 新宋?"><font size="2">void func(B *pb){<br>    D *pd1 = static_cast<D *>(pb);<br>    D *pd2 = dynamic_cast<D *>(pb);<br>}</font></p> <p style="font-family: 新宋?"><font size="2">在上面的代码D中Q如果pb指向一个Dcd的对象,pd1和pd2是一L(fng)Qƈ且对q两个指针执行Dcd的Q何操作都是安全的Q?br>但是Q如果pb指向的是一个Bcd的对象,那么pd1是一个指向该对象的指针,对它q行Dcd的操作将是不安全的(如访问m_szNameQ,<br>而pd2是一个空指针?/font></p> <p style="font-family: 新宋?"><font size="2">另外要注意:(x)B要有虚函敎ͼ否则?x)编译出错;static_cast则没有这个限制?br>q是׃q行时类型检查需要运行时cd信息Q而这个信息存储在cȝ虚函数表Q?br>关于虚函数表的概念,详细可见<Inside c++ object model>Q中Q只有定义了(jin)虚函数的cL有虚函数表,<br>没有定义虚函数的cL没有虚函数表的?/font></p> <p style="font-family: 新宋?"><font size="2">另外Qdynamic_castq支持交叉{换(cross castQ。如下代码所C?br>class A{<br>public:<br>        int m_iNum;<br>        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>    B *pb = new B;<br>    pb->m_iNum = 100;</font></p> <p style="font-family: 新宋?"><font size="2">    D *pd1 = static_cast<D *>(pb);    //compile error<br>    D *pd2 = dynamic_cast<D *>(pb);  //pd2 is NULL<br>    delete pb;<br>}</font></p> <p style="font-family: 新宋?"><font size="2">在函数foo中,使用static_castq行转换是不被允许的Q将在编译时出错Q而?dynamic_cast的{换则是允许的Q结果是I指针?/font></p> <p style="font-family: 新宋?"><br><font size="2">3.3 reinpreter_cast<br>用法Qreinpreter_cast<type-id> (expression)<br>type-id必须是一个指针、引用、算术类型、函数指针或者成员指针?br>它可以把一个指针{换成一个整敎ͼ也可以把一个整数{换成一个指针(先把一个指针{换成一个整敎ͼ<br>在把该整数{换成原类型的指针Q还可以得到原先的指针|(j)?/font></p> <p style="font-family: 新宋?"><font size="2">该运符的用法比较多?/font></p> <p style="font-family: 新宋?"><font size="2">3.4 const_cast <br>用法Qconst_cast<type_id> (expression)<br>该运符用来修改cd的const或volatile属性。除?jin)const 或volatile修饰之外Q?type_id和expression的类型是一L(fng)?br>帔R指针被{化成非常量指针,q且仍然指向原来的对象;<br>帔R引用被{换成非常量引用,q且仍然指向原来的对象;帔R对象被{换成非常量对象?/font></p> <p style="font-family: 新宋?"><font size="2">Voiatile和constc试。D如下一例:(x)<br>class B{<br>public:<br>     int m_iNum;<br>}<br>void foo(){<br> const B b1;<br> b1.m_iNum = 100;            //comile error<br> B b2 = const_cast<B>(b1);<br> b2. m_iNum = 200;           //fine<br>}<br>上面的代码编译时?x)报错,因?f)b1是一个常量对象,不能对它q行改变Q?br>使用const_cast把它转换成一个常量对象,可以对它的数据成员L改变。注意:(x)b1和b2是两个不同的对象?/font></p> </div><img src ="http://www.shnenglu.com/dawnbreak/aggbug/68122.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/dawnbreak/" target="_blank">pear_li</a> 2008-11-29 01:53 <a href="http://www.shnenglu.com/dawnbreak/articles/68122.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss> <footer> <div class="friendship-link"> <p>лǵվܻԴȤ</p> <a href="http://www.shnenglu.com/" title="精品视频久久久久">精品视频久久久久</a> <div class="friend-links"> </div> </div> </footer> <a href="http://www.baipe.cn" target="_blank">þ91Ʒ91</a>| <a href="http://www.0731dy.cn" target="_blank">99þɫĻ</a>| <a href="http://www.cn-yb.cn" target="_blank">ٸþĻһ </a>| <a href="http://www.jhitezpt.cn" target="_blank">þþƷ99þ޶</a>| <a href="http://www.ouseshi.cn" target="_blank">ٸþþþþþþþ</a>| <a href="http://www.tjywmc.cn" target="_blank">ƷþĻ</a>| <a href="http://www.lnsmgg.cn" target="_blank">99þþþ</a>| <a href="http://www.shangbi.com.cn" target="_blank">91鶹Ʒ91þþ</a>| <a href="http://www.mirbase.cn" target="_blank">þþƷɧ</a>| <a href="http://www.0546bbs.cn" target="_blank">ƷѾþþþþþþ</a>| <a href="http://www.840ww.cn" target="_blank">ۺϾþþƷ</a>| <a href="http://www.9kgat.cn" target="_blank">þþþҹҹҹƷ</a>| <a href="http://www.dgchengxin.cn" target="_blank">޹˾ƷŮ˾þþ </a>| <a href="http://www.ggg13.cn" target="_blank">ɫۺϾžþ</a>| <a href="http://www.akeyu.cn" target="_blank">þþƷ鶹ҹҹ</a>| <a href="http://www.gvyf.cn" target="_blank">þ99þóѲ</a>| <a href="http://www.fundmm.cn" target="_blank">þˬˬAV </a>| <a href="http://www.higo0310.cn" target="_blank">þþþó˾ƷĻ</a>| <a href="http://www.510dpw.cn" target="_blank">ɫۺϺϾþۺӿ</a>| <a href="http://www.7708.com.cn" target="_blank">þ99ֻоƷ66</a>| <a href="http://www.setocaster.cn" target="_blank">þ99һ</a>| <a href="http://www.jzyuan.cn" target="_blank">þþù޾Ʒ</a>| <a href="http://www.7788mm.cn" target="_blank">Ʒŷ޺ձþ </a>| <a href="http://www.bpdb.com.cn" target="_blank">Ʒþ» </a>| <a href="http://www.eksn.cn" target="_blank">ľþþƷ</a>| <a href="http://www.0731dy.cn" target="_blank">ŷƷ˿þþĻ</a>| <a href="http://www.sdxingying.com.cn" target="_blank">þþѾƷre6</a>| <a href="http://www.ak14.cn" target="_blank">þþþѾƷ</a>| <a href="http://www.timng.cn" target="_blank">޹ƷۺϾþ </a>| <a href="http://www.wowokk.cn" target="_blank">þþƷֻоƷ66 </a>| <a href="http://www.aofa-dsj.cn" target="_blank">ݹƷþ</a>| <a href="http://www.024cg.cn" target="_blank">þҹɫƷAV</a>| <a href="http://www.46test.cn" target="_blank">99ƷѾþþþþ</a>| <a href="http://www.wushuichulisb.cn" target="_blank">ۺۺϾþ69</a>| <a href="http://www.kucunshuo.cn" target="_blank">Ʒ99þþþþլ</a>| <a href="http://www.dfyxw.cn" target="_blank">ŮþþƷ㽶69</a>| <a href="http://www.amazinghall.com.cn" target="_blank">aѹۿþav</a>| <a href="http://www.gg4493.cn" target="_blank">ľƷþþþþò</a>| <a href="http://www.99605.com.cn" target="_blank">պݺݾþ͵͵ɫۺ96</a>| <a href="http://www.pzjc.net.cn" target="_blank">ƷƵþþþ</a>| <a href="http://www.ntysjx.cn" target="_blank">ɫۺϾþ</a>| <script> (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })(); </script> </body>