• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            chaosuper85

            C++博客 首頁 新隨筆 聯(lián)系 聚合 管理
              118 Posts :: 0 Stories :: 3 Comments :: 0 Trackbacks

            #

                 摘要: Beej網(wǎng)絡(luò)socket編程指南 -------------------------------------------------------------------------------- 介紹   Socket 編程讓你沮喪嗎?從man pages中很難得到有用的信息嗎?你想跟上時代去編Internet相關(guān)的程序,但是為你在調(diào)用 connect() ...  閱讀全文
            posted @ 2009-08-04 00:59 chaosuper 閱讀(200) | 評論 (0)編輯 收藏

            在C++中,內(nèi)存分成5個區(qū),他們分別是堆、棧、自由存儲區(qū)、全局/靜態(tài)存儲區(qū)和常量存儲區(qū)。

              棧,就是那些由編譯器在需要的時候分配,在不需要的時候自動清楚的變量的存儲區(qū)。里面的變量通常是局部變量、函數(shù)參數(shù)等。

              堆,就是那些由new分配的內(nèi)存塊,他們的釋放編譯器不去管,由我們的應(yīng)用程序去控制,一般一個new就要對應(yīng)一個delete。如果程序員沒有釋放掉,那么在程序結(jié)束后,操作系統(tǒng)會自動回收。

              自由存儲區(qū),就是那些由malloc等分配的內(nèi)存塊,他和堆是十分相似的,不過它是用free來結(jié)束自己的生命的。

              全局/靜態(tài)存儲區(qū),全局變量和靜態(tài)變量被分配到同一塊內(nèi)存中,在以前的C語言中,全局變量又分為初始化的和未初始化的,在C++里面沒有這個區(qū)分了,他們共同占用同一塊內(nèi)存區(qū)。

              常量存儲區(qū),這是一塊比較特殊的存儲區(qū),他們里面存放的是常量,不允許修改(當(dāng)然,你要通過非正當(dāng)手段也可以修改,而且方法很多,在《const的思考》一文中,我給出了6種方法)

              明確區(qū)分堆與棧

              在bbs上,堆與棧的區(qū)分問題,似乎是一個永恒的話題,由此可見,初學(xué)者對此往往是混淆不清的,所以我決定拿他第一個開刀。

              首先,我們舉一個例子:

            void f() { int* p=new int[5]; }

              這條短短的一句話就包含了堆與棧,看到new,我們首先就應(yīng)該想到,我們分配了一塊堆內(nèi)存,那么指針p呢?他分配的是一塊棧內(nèi)存,所以這句話的意思就是:在棧內(nèi)存中存放了一個指向一塊堆內(nèi)存的指針p。在程序會先確定在堆中分配內(nèi)存的大小,然后調(diào)用operator new分配內(nèi)存,然后返回這塊內(nèi)存的首地址,放入棧中,他在VC6下的匯編代碼如下:

            00401028 push 14h
            0040102A call operator new (00401060)
            0040102F add esp,4
            00401032 mov dword ptr [ebp-8],eax
            00401035 mov eax,dword ptr [ebp-8]
            00401038 mov dword ptr [ebp-4],eax

              這里,我們?yōu)榱撕唵尾]有釋放內(nèi)存,那么該怎么去釋放呢?是delete p么?澳,錯了,應(yīng)該是delete []p,這是為了告訴編譯器:我刪除的是一個數(shù)組,VC6就會根據(jù)相應(yīng)的Cookie信息去進(jìn)行釋放內(nèi)存的工作。

              好了,我們回到我們的主題:堆和棧究竟有什么區(qū)別?

              主要的區(qū)別由以下幾點(diǎn):

              1、管理方式不同;

              2、空間大小不同;

              3、能否產(chǎn)生碎片不同;

              4、生長方向不同;

              5、分配方式不同;

              6、分配效率不同;

              管理方式:對于棧來講,是由編譯器自動管理,無需我們手工控制;對于堆來說,釋放工作由程序員控制,容易產(chǎn)生memory leak。

              空間大小:一般來講在32位系統(tǒng)下,堆內(nèi)存可以達(dá)到4G的空間,從這個角度來看堆內(nèi)存幾乎是沒有什么限制的。但是對于棧來講,一般都是有一定的空間大小的,例如,在VC6下面,默認(rèn)的棧空間大小是1M(好像是,記不清楚了)。當(dāng)然,我們可以修改:

              打開工程,依次操作菜單如下:Project->Setting->Link,在Category 中選中Output,然后在Reserve中設(shè)定堆棧的最大值和commit。

              注意:reserve最小值為4Byte;commit是保留在虛擬內(nèi)存的頁文件里面,它設(shè)置的較大會使棧開辟較大的值,可能增加內(nèi)存的開銷和啟動時間。

              碎片問題:對于堆來講,頻繁的new/delete勢必會造成內(nèi)存空間的不連續(xù),從而造成大量的碎片,使程序效率降低。對于棧來講,則不會存在這個問題,因?yàn)闂J窍冗M(jìn)后出的隊(duì)列,他們是如此的一一對應(yīng),以至于永遠(yuǎn)都不可能有一個內(nèi)存塊從棧中間彈出,在他彈出之前,在他上面的后進(jìn)的棧內(nèi)容已經(jīng)被彈出,詳細(xì)的可以參考數(shù)據(jù)結(jié)構(gòu),這里我們就不再一一討論了。

              生長方向:對于堆來講,生長方向是向上的,也就是向著內(nèi)存地址增加的方向;對于棧來講,它的生長方向是向下的,是向著內(nèi)存地址減小的方向增長。

              分配方式:堆都是動態(tài)分配的,沒有靜態(tài)分配的堆。棧有2種分配方式:靜態(tài)分配和動態(tài)分配。靜態(tài)分配是編譯器完成的,比如局部變量的分配。動態(tài)分配由alloca函數(shù)進(jìn)行分配,但是棧的動態(tài)分配和堆是不同的,他的動態(tài)分配是由編譯器進(jìn)行釋放,無需我們手工實(shí)現(xiàn)。

              分配效率:棧是機(jī)器系統(tǒng)提供的數(shù)據(jù)結(jié)構(gòu),計(jì)算機(jī)會在底層對棧提供支持:分配專門的寄存器存放棧的地址,壓棧出棧都有專門的指令執(zhí)行,這就決定了棧的效率比較高。堆則是C/C++函數(shù)庫提供的,它的機(jī)制是很復(fù)雜的,例如為了分配一塊內(nèi)存,庫函數(shù)會按照一定的算法(具體的算法可以參考數(shù)據(jù)結(jié)構(gòu)/操作系統(tǒng))在堆內(nèi)存中搜索可用的足夠大小的空間,如果沒有足夠大小的空間(可能是由于內(nèi)存碎片太多),就有可能調(diào)用系統(tǒng)功能去增加程序數(shù)據(jù)段的內(nèi)存空間,這樣就有機(jī)會分到足夠大小的內(nèi)存,然后進(jìn)行返回。顯然,堆的效率比棧要低得多。

              從這里我們可以看到,堆和棧相比,由于大量new/delete的使用,容易造成大量的內(nèi)存碎片;由于沒有專門的系統(tǒng)支持,效率很低;由于可能引發(fā)用戶態(tài)和核心態(tài)的切換,內(nèi)存的申請,代價變得更加昂貴。所以棧在程序中是應(yīng)用最廣泛的,就算是函數(shù)的調(diào)用也利用棧去完成,函數(shù)調(diào)用過程中的參數(shù),返回地址,EBP和局部變量都采用棧的方式存放。所以,我們推薦大家盡量用棧,而不是用堆。

              雖然棧有如此眾多的好處,但是由于和堆相比不是那么靈活,有時候分配大量的內(nèi)存空間,還是用堆好一些。

              無論是堆還是棧,都要防止越界現(xiàn)象的發(fā)生(除非你是故意使其越界),因?yàn)樵浇绲慕Y(jié)果要么是程序崩潰,要么是摧毀程序的堆、棧結(jié)構(gòu),產(chǎn)生以想不到的結(jié)果,就算是在你的程序運(yùn)行過程中,沒有發(fā)生上面的問題,你還是要小心,說不定什么時候就崩掉,那時候debug可是相當(dāng)困難的:)
            posted @ 2009-08-03 17:55 chaosuper 閱讀(145) | 評論 (0)編輯 收藏

             

            cisco研發(fā)中心面試試題
            說點(diǎn)關(guān)于cisco的面世吧:就算是給大家參考參考,可能對以后有幫助
            過程:4輪面試。兩個印度人,一個愛爾蘭人,一個臺灣人。每人面半個小時。首先是自我
            介紹了,這個相信大家誰都有準(zhǔn)備。最好是4分鐘之內(nèi),突出重點(diǎn):C/C++Linux/UnixVoIP,工作經(jīng)驗(yàn)或是其它。
            然后就是問問題。首先我認(rèn)為大家一定要準(zhǔn)備好自己的英語,因?yàn)槊嬖嚬贂鶕?jù)自己的心情說快說慢,特別是印度工程師,聽起來比較費(fèi)勁。下面說幾點(diǎn)技術(shù)相干問題:
            1.
            如果你對C++virtual function不是很懂,那么你很難突破。無論你到哪里面試,虛函數(shù)是一定要吃透的,我不知道現(xiàn)在方不方便說出具體的問題,但是大家應(yīng)該對虛函數(shù)100%的了解,否則我認(rèn)為很難拿到ciscooffer。轉(zhuǎn)摘請標(biāo)明:http://www.pghome.net/art.html 志遠(yuǎn)工作室
            2.
            關(guān)于線程:semaphoreshare memorycirtical sectionpthreadprocessmutex
            dead lock這些概念
            你應(yīng)該很懂,隨便他怎么問你都能很好的回答出來,否則也懸。
            3. C++
            的零碎:對const100%理解;一個class基本的幾個元素:default constructordestructorcopy constructor
            overload assignmentpass by #118aluepass by reference的區(qū)別;這些你要熟到非常
            細(xì)微的地步,比如哪里的參數(shù)是
            const &
            ,返回值類型是reference還是&#118alue,要不要const。這些都是C++精髓,大家要是
            不是徹底弄懂,很難過cisco面試官
            的眼睛,因?yàn)閱栴}都是圍繞這個來的。大家看自己是否面試好,只要問問自己對上面這些懂多少就可以了。
            4.
            對于二叉樹,起碼大家也應(yīng)該知道是怎么回事,比如給你前序,中序你能畫出二叉樹,
            遞歸非遞歸算法怎么寫,如果刪除葉
            結(jié)點(diǎn)怎么寫......當(dāng)然cisco沒問這么多,只是問了這里面的一個問題,我認(rèn)為如果大家要
            面試,二叉樹一定要吃透,比如演變的
            二叉搜索樹,堆排序,這些都要記住,否則臨場很難發(fā)揮出來。當(dāng)然cisco沒有問這么多,
            具體哪個我就不說,可能也應(yīng)人而異,
            但是這些諸位都應(yīng)該知道,如果二叉樹是什么都不知道,那么我覺得沒戲。
            5.
            現(xiàn)場編程。這個題目一般都很簡單,就要看心理素質(zhì)怎樣。cisco出的題目不知道現(xiàn)在方不方便說,不過大概就是圍繞遞歸和非遞歸作文章。大家數(shù)據(jù)結(jié)構(gòu)里面都學(xué)過,關(guān)鍵在于能否在非常短的時間之內(nèi)準(zhǔn)確的表達(dá)出自己的意思。這個我認(rèn)為關(guān)鍵在于心理
            素質(zhì)。如果編程你沒過,那么我就不知道怎樣了,但是你過了,而且很輕松的過,那么對于
            拿到offer是很有好處的。
            6.
            關(guān)于VoIP,這個主要集中在H.323SIP上。首先如果你對這兩個咚咚一竅不通,那么我
            個人認(rèn)為很難通過。起碼,什么是什么應(yīng)該知道,區(qū)別應(yīng)該知道,兩個協(xié)議的相關(guān)協(xié)議完成的功能應(yīng)該知道(當(dāng)然SIP是依靠其它協(xié)議),拓?fù)鋺?yīng)該知道,關(guān)鍵元素應(yīng)該知道比如H.323里面的epgkbilling至少應(yīng)該聽過,SIP中的proxyuaredirectregiste
            r
            應(yīng)該知道什么咚咚,這些知道代表你知道這兩個是什么,如果前門這些不是全部知道(當(dāng)然知道一項(xiàng)我想也沒問題,還有就是我只針對H.323SIP,其它這里不說),接下來cisco感興趣的是比如H.323的對話建立過程,比如ARQ-->ACF什么的,或者SIPinvite等等
            ,如果你能完美的畫出這個交互過程,那么你的VoIP100%滿足了cisco的要求了。不過個人認(rèn)為除非你是開發(fā)過VoIP相關(guān)協(xié)議,否則這么清楚還是少的。當(dāng)然只是個人見解。
            7.
            關(guān)于Linux:這個也是被問到的問題:基本操作:ifconfigtopps -ef|grep **,這
            些你用過Linux就知道。總的來說Linux基本上是針對基本的操作。就看你有沒有用過了。TCP相關(guān)編程,clientserver端的socket編程
            ,這個反正cisco是有高手,他會根據(jù)你知道多少問多少,起碼也應(yīng)該知道一點(diǎn)內(nèi)容吧。
            8. Why cisco
            。這是個主觀問題,可能也是考官打發(fā)剩于面試時間用的。呵呵,還是很重要。
            以上就是我對cisco面試的總接,目的很簡單,給大家分享,可能以后或者其它面試有幫助
            ,而不只是回憶cisco面試成功與否,這個已經(jīng)不取決
            于我們,總結(jié)一下:轉(zhuǎn)摘請注明:www.pghome.net
            a.
            英語個人認(rèn)為只要表達(dá)對方能夠聽懂就可以,不必要多么流利,因?yàn)橛《裙こ處煹挠⒄Z
            實(shí)在是不敢恭維。當(dāng)然你的聽力必須出色,專業(yè)名詞必須了如指掌
            b. C++
            應(yīng)該至少熟悉:virtual function的所有方面;overload assignmentcopy constr
            uctor
            destructor
            c. VoIP
            :鑒于cisco招人的工作性質(zhì),你越懂這個你的砝碼越重
            d. Linux
            :和VoIP一樣
            e.
            編程素養(yǎng):對數(shù)據(jù)結(jié)構(gòu)的認(rèn)識和臨場編程能力。進(jìn)程之間通信和線程編程,網(wǎng)絡(luò)編程。
            f.
            你給考官的映象
            大家不妨參照上面的給自己分析分析,如果你每項(xiàng)都很牛,表現(xiàn)很好,那么我覺得你拿offer沒問題。
            以上純屬個人總結(jié),目的在于分享和給面試其他公司或者是明年面試的人一些參考和建議,沒有任何泄露cisco面試考題的意思。

            posted @ 2009-08-03 14:45 chaosuper 閱讀(208) | 評論 (0)編輯 收藏

            http://linux.sheup.com/
            posted @ 2009-08-03 14:36 chaosuper 閱讀(107) | 評論 (0)編輯 收藏

            GCC 5

            Section: GNU Tools (1)
            Updated: 2003/12/05
            Sponsor: GCC Casino Winning Content

            下面的`-m'選項(xiàng)用于HPPA族計(jì)算機(jī):

            -mpa-risc-1-0
            生成PA 1.0處理器的目標(biāo)碼.
            -mpa-risc-1-1
            生成PA 1.1處理器的目標(biāo)碼.

             

            -mkernel
            生成適用于內(nèi)核的目標(biāo)碼.特別要避免add指令,它有一個參數(shù)是DP寄存器;addil 代替add指令.這樣可以避免HP-UX連接器的某個嚴(yán)重bug.

             

            -mshared-libs
            生成能夠連接HP-UX共享庫的目標(biāo)碼.該選項(xiàng)還沒有實(shí)現(xiàn)全部功能,PA目標(biāo)默認(rèn)為關(guān)閉.使用這個選項(xiàng)會導(dǎo)致 編譯器生成錯誤的目標(biāo)碼.

             

            -mno-shared-libs
            不生成連接HP-UX共享庫的目標(biāo)碼.這是PA目標(biāo)的默認(rèn)選項(xiàng).

             

            -mlong-calls
            生成的目標(biāo)碼允許同一個源文件中的函數(shù)調(diào)用,調(diào)用點(diǎn)和被調(diào)函數(shù)的距離可以超過256K之遠(yuǎn).不需要打開這個開關(guān)選項(xiàng), 除非連接器給出``branch out of range errors``這樣的錯誤.

             

            -mdisable-fpregs
            防止任何情況下使用浮點(diǎn)寄存器.編譯內(nèi)核需要這個選項(xiàng),內(nèi)核切換浮點(diǎn)寄存器的執(zhí)行環(huán)境速度非常緩慢.如果打開了這個 開關(guān)選項(xiàng)同時試圖浮點(diǎn)操作,編譯將失敗.

             

            -mdisable-indexing
            防止編譯器使用索引地址模式(indexing address mode).這樣在MACH上編譯MIG生成的代碼時,可以 避免一些非常晦澀的問題.

             

            -mtrailing-colon
            在標(biāo)記定義(label definition)的末尾添加一個冒號(用于ELF匯編器).

             

            下面的`-m'選項(xiàng)用于Intel 80960族計(jì)算機(jī):

            -mcpu-type
            默認(rèn)機(jī)器類型為cpu-type ,使編譯器產(chǎn)生對應(yīng)的指令,地址模式和內(nèi)存對齊.默認(rèn)的 cpu-typekb;其他選擇有ka, mc, ca, cf, sa,sb.

             

            -mnumerics
            -msoft-float
            -mnumerics開關(guān)選項(xiàng)指出處理器不支持浮點(diǎn)指令. -msoft-float開關(guān)選項(xiàng)指出不應(yīng)該認(rèn)為 機(jī)器支持浮點(diǎn)操作.

             

            -mleaf-procedures
            -mno-leaf-procedures
            企圖(或防止)改變?nèi)~過程(leaf procedure),使其可被bal指令以及call指令 調(diào)用.對于直接函數(shù)調(diào)用,如果bal指令能夠被匯編器或連接器替換,這可以產(chǎn)生更有效的代碼,但是其他情況下 產(chǎn)生較低效的代碼,例如通過函數(shù)指針調(diào)用函數(shù),或使用了不支持這種優(yōu)化的連接器.

             

            -mtail-call
            -mno-tail-call
            執(zhí)行(或不執(zhí)行)更多的嘗試(除過編譯器那些機(jī)器無關(guān)部分),優(yōu)化進(jìn)入分支的尾遞歸(tail-recursive)調(diào)用.你 可能不需要這個,因?yàn)闄z測什么地方無效沒有全部完成.默認(rèn)開關(guān)是-mno-tail-call.

             

            -mcomplex-addr
            -mno-complex-addr
            認(rèn)為(或不認(rèn)為)在當(dāng)前的i960設(shè)備上,值得使用復(fù)合地址模式(complex addressing mode).復(fù)合地址模式 可能不值得用到K系列,但是一定值得用在C系列.目前除了CBCC處理器,其他處理器上 -mcomplex-addr是默認(rèn)選項(xiàng).

             

            -mcode-align
            -mno-code-align
            把目標(biāo)碼對齊到8字節(jié)邊界上(或者不必),這樣讀取會快一些.目前只對C系列默認(rèn)打開.

             

            -mic-compat
            -mic2.0-compat
            -mic3.0-compat
            兼容iC960 v2.0v3.0.

             

            -masm-compat
            -mintel-asm
            兼容iC960匯編器.

             

            -mstrict-align
            -mno-strict-align
            不允許(或允許)邊界不對齊的訪問.

             

            -mold-align
            使結(jié)構(gòu)對齊(structure-alignment)兼容Intelgcc發(fā)行版本1.3 (基于gcc 1.37).目前 這個選項(xiàng)有點(diǎn)問題,因?yàn)?/font>#pragma align 1總是作同樣的設(shè)定,而且無法關(guān)掉.

             

            下面的`-m'選項(xiàng)用于DEC Alpha設(shè)備:

            -mno-soft-float
            -msoft-float
            使用(或不使用)硬件浮點(diǎn)指令進(jìn)行浮點(diǎn)運(yùn)算.打開-msoft-float,將使用 `libgcc1.c'中的函數(shù)執(zhí)行浮點(diǎn)運(yùn)算.除非它們被仿真浮點(diǎn)操作的例程替換,或者類似,它們被編譯為調(diào)用 仿真例程,這些例程將發(fā)出浮點(diǎn)操作.如果你為不帶浮點(diǎn)操作的Alpha編譯程序,你必須確保建立了這個庫,以便不調(diào)用 仿真例程.

            注意,不帶浮點(diǎn)操作的Alpha也要求擁有浮點(diǎn)寄存器.

             

            -mfp-reg
            -mno-fp-regs
            生成使用(或不使用)浮點(diǎn)寄存器群的目標(biāo)代碼. -mno-fp-regs包含有-msoft-float 開關(guān)選項(xiàng).如果不使用浮點(diǎn)寄存器,浮點(diǎn)操作數(shù)就象整數(shù)一樣通過整數(shù)寄存器傳送,浮點(diǎn)運(yùn)算結(jié)果放到$0而不是$f0.這是非標(biāo)準(zhǔn) 調(diào)用,因此任何帶有浮點(diǎn)參數(shù)或返回值的函數(shù),如果被-mno-fp-regs開關(guān)編譯過的目標(biāo)碼調(diào)用,它也必須 用這個選項(xiàng)編譯.

            這個選項(xiàng)的典型用法是建立內(nèi)核,內(nèi)核不使用任何浮點(diǎn)寄存器,因此沒必要保存和恢復(fù)這些寄存器.

             

            下面附加的選項(xiàng)出現(xiàn)在System V第四版中,用于兼容這些系統(tǒng)中的其他編譯器:

            -G
            SVr4系統(tǒng)中, gcc出于兼容接受了`-G'選項(xiàng)(然后傳遞給連接器).可是我們建議使用 `-symbolic'`-shared'選項(xiàng),而不在gcc命令行上出現(xiàn)連接選項(xiàng).

             

            -Qy
            驗(yàn)證編譯器用的工具的版本,輸出到.ident匯編指令.

             

            -Qn
            制止輸出端的.ident指令(默認(rèn)選項(xiàng)).

             

            -YP,dirs
            對于`-l'指定的庫文件,只搜索dirs.你可以在dirs中用冒號隔開各個 目錄項(xiàng).

             

            -Ym,dir
            dir目錄中尋找M4預(yù)處理器.匯編器使用這個選項(xiàng).

             

            代碼生成選項(xiàng)(CODE GENERATION OPTION)

            下面的選項(xiàng)和平臺無關(guān),用于控制目標(biāo)碼生成的接口約定.

            大部分選項(xiàng)以`-f'開始.這些選項(xiàng)擁有確定和否定兩種格式; `-ffoo'的否定格式是 `-fno-foo'.后面的描述將只列舉其中的一個格式---非默認(rèn)的格式.你可以通過添加或去掉 `no-'推測出另一個格式.

            -fnonnull-objects
            假設(shè)通過引用(reference)取得的對象不為null (C++).

            一般說來, GNU C++對通過引用取得的對象作保守假設(shè).例如,編譯器一定會檢查下似代碼中的a不為 null:

            obj &a = g (); a.f (2);

            檢查類似的引用需要額外的代碼,然而對于很多程序是不必要的.如果你的程序不要求這種檢查,你可以用 `-fnonnull-objects'選項(xiàng)忽略它.

             

            -fpcc-struct-return
            函數(shù)返回structunion值時,采用和本地編譯器相同的參數(shù)約定.對于較小的結(jié)構(gòu), 這種約定的效率偏低,而且很多機(jī)器上不能重入;它的優(yōu)點(diǎn)是允許GCC編譯的目標(biāo)碼和PCC編譯的目標(biāo)碼互相調(diào)用.

             

            -freg-struct-return
            一有可能就通過寄存器返回structunion函數(shù)值.對于較小的結(jié)構(gòu),它比 -fpcc-struct-return更有效率.

            如果既沒有指定-fpcc-struct-return ,也沒有指定-freg-struct-return, GNU CC默認(rèn)使用目標(biāo)機(jī)的標(biāo)準(zhǔn)約定.如果沒有標(biāo)準(zhǔn)約定, GNU CC默認(rèn)采用-fpcc-struct-return.

             

            -fshort-enums
            enum類型只分配它聲明的值域范圍的字節(jié)數(shù).就是說, enum類型等于大小足夠的 最小整數(shù)類型.

             

            -fshort-double
            使double類型的大小和float一樣.

             

            -fshared-data
            要求編譯結(jié)果的數(shù)據(jù)和非const變量是共享數(shù)據(jù),而不是私有數(shù)據(jù).這種差別僅在某些操作系統(tǒng)上面有意義, 那里的共享數(shù)據(jù)在同一個程序的若干進(jìn)程間共享,而私有數(shù)據(jù)在每個進(jìn)程內(nèi)都有副件.

             

            -fno-common
            即使未初始化的全局變量也分配在目標(biāo)文件的bss,而不是把它們當(dāng)做普通塊(common block)建立.這樣的 結(jié)果是,如果在兩個不同的編譯結(jié)果中聲明了同一個變量(沒使用extern ),連接它們時會產(chǎn)生錯誤. 這個選項(xiàng)可能有用的唯一情況是,你希望確認(rèn)程序能在其他系統(tǒng)上運(yùn)行,而其他系統(tǒng)總是這么做.

             

            -fno-ident
            忽略`#ident'指令.

             

            -fno-gnu-linker
            不要把全局初始化部件(C++的構(gòu)造子和解構(gòu)子)輸出為GNU連接器使用的格式(GNU連接器是標(biāo)準(zhǔn)方法的系統(tǒng) 上).當(dāng)你打算使用非GNU連接器的時候可以用這個選項(xiàng),GNU連接器也需要collect2程序確保系統(tǒng)連接器 放入構(gòu)造子(constructor)和解構(gòu)子(destructor). (GNU CC的發(fā)布包中包含有collect2 程序.)對于必須使用collect2的系統(tǒng),編譯器驅(qū)動程序gcc自動配置為這么做.

             

            -finhibit-size-directive
            不要輸出.size匯編指令,或其他類似指令,當(dāng)某個函數(shù)一分為二,兩部分在內(nèi)存中距離很遠(yuǎn)時會引起問題. 當(dāng)編譯`crtstuff.c'時需要這個選項(xiàng);其他情況下都不應(yīng)該使用.

             

            -fverbose-asm
            輸出匯編代碼時放些額外的注釋信息.這個選項(xiàng)僅用于確實(shí)需要閱讀匯編輸出的時候(可能調(diào)試編譯器自己的時候).

             

            -fvolatile
            使編譯器認(rèn)為所有通過指針訪問的內(nèi)存是易變內(nèi)存(volatile).

             

            -fvolatile-global
            使編譯器認(rèn)為所有的外部和全局變量是易變內(nèi)存.

             

            -fpic
            如果支持這種目標(biāo)機(jī),編譯器就生成位置無關(guān)目標(biāo)碼.適用于共享庫(shared library).

             

            -fPIC
            如果支持這種目標(biāo)機(jī),編譯器就輸出位置無關(guān)目標(biāo)碼.適用于動態(tài)連接(dynamic linking),即使分支需要大范圍 轉(zhuǎn)移.

             

            -ffixed-reg
            把名為reg的寄存器按固定寄存器看待(fixed register);生成的目標(biāo)碼不應(yīng)該引用它(除了或許 用作棧指針,幀指針,或其他固定的角色).

            reg必須是寄存器的名字.寄存器名字取決于機(jī)器,用機(jī)器描述宏文件的REGISTER_NAMES宏 定義.

            這個選項(xiàng)沒有否定格式,因?yàn)樗谐鋈愤x擇.

             

            -fcall-used-reg
            把名為reg的寄存器按可分配寄存器看待,不能在函數(shù)調(diào)用間使用.可以臨時使用或當(dāng)做變量使用,生存期 不超過一個函數(shù).這樣編譯的函數(shù)無需保存和恢復(fù)reg寄存器.

            如果在可執(zhí)行模塊中,把這個選項(xiàng)說明的寄存器用作固定角色將會產(chǎn)生災(zāi)難性結(jié)果,如棧指針或幀指針.

            這個選項(xiàng)沒有否定格式,因?yàn)樗谐鋈愤x擇.

             

            -fcall-saved-reg
            把名為reg的寄存器按函數(shù)保護(hù)的可分配寄存器看待.可以臨時使用或當(dāng)做變量使用,它甚至能在函數(shù)間 生存.這樣編譯的函數(shù)會保存和恢復(fù)使用中的reg寄存器.

            如果在可執(zhí)行模塊中,把這個選項(xiàng)說明的寄存器用作固定角色將會產(chǎn)生災(zāi)難性結(jié)果,如棧指針或幀指針.

            另一種災(zāi)難是用這個選項(xiàng)說明的寄存器返回函數(shù)值.

            這個選項(xiàng)沒有否定格式,因?yàn)樗谐鋈愤x擇.

             

            PRAGMAS

            GNU C++支持兩條`#pragma'指令使同一個頭文件有兩個用途:對象類的接口定義, 對象類完整的內(nèi)容定義.

            #pragma interface
            (僅對C++)在定義對象類的頭文件中,使用這個指令可以節(jié)省大部分采用該類的目標(biāo)文件的大小.一般說來,某些信息 (內(nèi)嵌成員函數(shù)的備份副件,調(diào)試信息,實(shí)現(xiàn)虛函數(shù)的內(nèi)部表格等)的本地副件必須保存在包含類定義的各個目標(biāo)文件中.使用這個 pragma指令能夠避免這樣的復(fù)制.當(dāng)編譯中引用包含`#pragma interface'指令的頭文件時,就 不會產(chǎn)生這些輔助信息(除非輸入的主文件使用了`#pragma implementation'指令).作為替代,目標(biāo)文件 將包含可被連接時解析的引用(reference).

             

            #pragma implementation
            #pragma implementation "objects.h"
            (僅對C++)如果要求從頭文件產(chǎn)生完整的輸出(并且全局可見),你應(yīng)該在主輸入文件中使用這條pragma.頭文件 中應(yīng)該依次使用`#pragma interface'指令.implementation文件中將產(chǎn)生全部內(nèi)嵌成員函數(shù) 的備份,調(diào)試信息,實(shí)現(xiàn)虛函數(shù)的內(nèi)部表格等.

            如果`#pragma implementation'不帶參數(shù),它指的是和源文件有相同基本名的包含文件;例如, `allclass.cc', `#pragma implementation'等于`#pragma implementation allclass.h'.如果某個implementation文件需要從多個頭文件引入代碼,就應(yīng)該 使用這個字符串參數(shù).

            不可能把一個頭文件里面的內(nèi)容分割到多個implementation文件中.

             

            文件(FILE)

            file.c             C源文件
            file.h             C頭文件(預(yù)處理文件)
            file.i            預(yù)處理后的C源文件
            file.C             C++源文件
            file.cc            C++源文件
            file.cxx           C++源文件
            file.m             Objective-C源文件
            file.s            匯編語言文件
            file.o            目標(biāo)文件
            a.out             連接的輸出文件
            TMPDIR/cc*        臨時文件
            LIBDIR/cpp        預(yù)處理器
            LIBDIR/cc1         C編譯器
            LIBDIR/cc1plus     C++編譯器
            LIBDIR/collect    某些機(jī)器需要的連接器前端(front end)程序
            LIBDIR/libgcc.a    GCC子例程(subroutine)/lib/crt[01n].o   啟動例程(start-up)
            LIBDIR/ccrt0       C++的附加啟動例程
            /lib/libc.a       標(biāo)準(zhǔn)C,另見intro (3)
            /usr/include       #include文件的標(biāo)準(zhǔn)目錄
            LIBDIR/include     #include文件的標(biāo)準(zhǔn)gcc目錄
            LIBDIR/g++-include #include文件的附加g++目錄
            
            LIBDIR通常為/usr/local/lib/machine/version.
            TMPDIR
            來自環(huán)境變量TMPDIR (如果存在,缺省為/usr/tmp ,否則為 /tmp).

             

            BUGS

            關(guān)于報(bào)告差錯的指導(dǎo)請查閱GCC手冊.

            版權(quán)(COPYING)

            Copyright 1991, 1992, 1993 Free Software Foundation, Inc.

            Permission is granted to make and distribute verbatim copies of this manual provided the copyright notice and this permission notice are preserved on all copies.

            Permission is granted to copy and distribute modified versions of this manual under the conditions for verbatim copying, provided that the entire resulting derived work is distributed under the terms of a permission notice identical to this one.

            Permission is granted to copy and distribute translations of this manual into another language, under the above conditions for modified versions, except that this permission notice may be included in translations approved by the Free Software Foundation instead of in the original English.

            作者(AUTHORS)

            關(guān)于GNU CC的奉獻(xiàn)者請查閱GUN CC手冊.
            posted @ 2009-08-03 14:32 chaosuper 閱讀(646) | 評論 (0)編輯 收藏

                 摘要: GCC 3 Section: GNU Tools (1)Updated: 2003/12/05   調(diào)試選項(xiàng)(DEBUGGING OPTION) GNU CC擁有許多特別選項(xiàng),既可以調(diào)試用戶的程序,也可以對GCC排錯: -g 以操作系統(tǒng)的本地格式(stabs, COFF, XCOFF,或DWARF).產(chǎn)生調(diào)試信息. GDB能夠使用這些調(diào)試信息. 在大多...  閱讀全文
            posted @ 2009-08-03 14:31 chaosuper 閱讀(188) | 評論 (0)編輯 收藏

                 摘要: GCC 4 Section: GNU Tools (1)Updated: 2003/12/05 機(jī)器相關(guān)選項(xiàng)(MACHINE DEPENDENT OPTION) 每一種目標(biāo)機(jī)型都有自己的特別選項(xiàng),這些選項(xiàng)用`-m '開關(guān)引導(dǎo),選擇不同的硬件型號或配置---例如, 68010還是68020,有沒有浮點(diǎn)協(xié)處理器.通過指定選項(xiàng),安裝 編譯器的一個版本能夠?yàn)樗械男吞柣蚺渲眠M(jìn)行編譯. 此外...  閱讀全文
            posted @ 2009-08-03 14:31 chaosuper 閱讀(304) | 評論 (0)編輯 收藏

            GCC 1

            Section: GNU Tools (1)
            Updated: 2003/12/05
            ÆäËûÁ¬½Ó: GNU ¹«¹²ÊÚȨ

            NAME

            gcc,g++-GNU工程的CC++編譯器(egcs-1.1.2)

            總覽(SYNOPSIS)

            gcc[option|filename ]...
            g++[option|filename ]...

            警告(WARNING)

            本手冊頁內(nèi)容摘自GNU C編譯器的完整文檔,僅限于解釋選項(xiàng)的含義.

            除非有人自愿維護(hù),否則本手冊頁不再更新.如果發(fā)現(xiàn)手冊頁和軟件之間有所矛盾,請查對Info文件, Info文件是權(quán)威文檔.

            如果我們發(fā)覺本手冊頁的內(nèi)容由于過時而導(dǎo)致明顯的混亂和抱怨時,我們就停止發(fā)布它.不可能有其他選擇,象更新Info文件同時更新man手冊,因?yàn)槠渌S護(hù)GNU CC的工作沒有留給我們時間做這個. GNU工程認(rèn)為man手冊是過時產(chǎn)物,應(yīng)該把時間用到別的地方.

            如果需要完整和最新的文檔,請查閱Info文件`gcc'Using and Porting GNU CC (for version 2.0) (使用和移植GNU CC 2.0) 手冊.二者均來自Texinfo原文件 gcc.texinfo.

            描述(DESCRIPTION)

            CC++編譯器是集成的.他們都要用四個步驟中的一個或多個處理輸入文件: 預(yù)處理(preprocessing),編譯(compilation),匯編(assembly)和連接(linking).源文件后綴名標(biāo)識源文件的 語言,但是對編譯器來說,后綴名控制著缺省設(shè)定:
            gcc
            認(rèn)為預(yù)處理后的文件(.i)C文件,并且設(shè)定C形式的連接.
            g++
            認(rèn)為預(yù)處理后的文件(.i)C++文件,并且設(shè)定C++形式的連接.

            源文件后綴名指出語言種類以及后期的操作:

            .c      C源程序;預(yù)處理,編譯,匯編
            .C      C++源程序;預(yù)處理,編譯,匯編
            .cc     C++源程序;預(yù)處理,編譯,匯編
            .cxx    C++源程序;預(yù)處理,編譯,匯編
            .m      Objective-C源程序;預(yù)處理,編譯,匯編
            .i     預(yù)處理后的C文件;編譯,匯編
            .ii    預(yù)處理后的C++文件;編譯,匯編
            .s     匯編語言源程序;匯編
            .S     匯編語言源程序;預(yù)處理,匯編
            .h     預(yù)處理器文件;通常不出現(xiàn)在命令行上
            
            其他后綴名的文件被傳遞給連接器(linker).通常包括:

            .o     目標(biāo)文件(Object file)
            .a     歸檔庫文件(Archive file)
            

            除非使用了-c, -S,-E選項(xiàng)(或者編譯錯誤阻止了完整的過程),否則連接總是 最后的步驟.在連接階段中,所有對應(yīng)于源程序的.o文件, -l庫文件,無法識別的文件名(包括指定的 .o目標(biāo)文件和.a庫文件)按命令行中的順序傳遞給連接器.

            選項(xiàng)(OPTIONS)

            選項(xiàng)必須分立給出: `-dr'完全不同于`-d -r '.

            大多數(shù)`-f'`-W'選項(xiàng)有兩個相反的格式: -fname-fno-name (-Wname-Wno-name).這里 只列舉不是默認(rèn)選項(xiàng)的格式.

            下面是所有選項(xiàng)的摘要,按類型分組,解釋放在后面的章節(jié)中.

            總體選項(xiàng)(Overall Option)

            -c -S -E -o file -pipe -v -x language
            語言選項(xiàng)(Language Option)
            -ansi -fall-virtual -fcond-mismatch -fdollars-in-identifiers -fenum-int-equiv -fexternal-templates -fno-asm -fno-builtin -fhosted -fno-hosted -ffreestanding -fno-freestanding -fno-strict-prototype -fsigned-bitfields -fsigned-char -fthis-is-variable -funsigned-bitfields -funsigned-char -fwritable-strings -traditional -traditional-cpp -trigraphs
            警告選項(xiàng)(Warning Option)
            -fsyntax-only -pedantic -pedantic-errors -w -W -Wall -Waggregate-return -Wcast-align -Wcast-qual -Wchar-subscript -Wcomment -Wconversion -Wenum-clash -Werror -Wformat -Wid-clash-len -Wimplicit -Wimplicit-int -Wimplicit-function-declaration -Winline -Wlong-long -Wmain -Wmissing-prototypes -Wmissing-declarations -Wnested-externs -Wno-import -Wparentheses -Wpointer-arith -Wredundant-decls -Wreturn-type -Wshadow -Wstrict-prototypes -Wswitch -Wtemplate-debugging -Wtraditional -Wtrigraphs -Wuninitialized -Wunused -Wwrite-strings
            調(diào)試選項(xiàng)(Debugging Option)
            -a -dletters -fpretend-float -g -glevel -gcoff -gxcoff -gxcoff+ -gdwarf -gdwarf+ -gstabs -gstabs+ -ggdb -p -pg -save-temps -print-file-name=library -print-libgcc-file-name -print-prog-name=program
            優(yōu)化選項(xiàng)(Optimization Option)
            -fcaller-saves -fcse-follow-jumps -fcse-skip-blocks -fdelayed-branch -felide-constructors -fexpensive-optimizations -ffast-math -ffloat-store -fforce-addr -fforce-mem -finline-functions -fkeep-inline-functions -fmemoize-lookups -fno-default-inline -fno-defer-pop -fno-function-cse -fno-inline -fno-peephole -fomit-frame-pointer -frerun-cse-after-loop -fschedule-insns -fschedule-insns2 -fstrength-reduce -fthread-jumps -funroll-all-loops -funroll-loops -O -O2 -O3
            預(yù)處理器選項(xiàng)(Preprocessor Option)
            -Aassertion -C -dD -dM -dN -Dmacro[=defn] -E -H -idirafter dir -include file -imacros file -iprefix file -iwithprefix dir -M -MD -MM -MMD -nostdinc -P -Umacro -undef
            匯編器選項(xiàng)(Assembler Option)
            -Wa,option
            連接器選項(xiàng)(Linker Option)
            -llibrary -nostartfiles -nostdlib -static -shared -symbolic -Xlinker option -Wl,option -u symbol
            目錄選項(xiàng)(Directory Option)
            -Bprefix -Idir -I- -Ldir
            目標(biāo)機(jī)選項(xiàng)(Target Option)
            -b machine -V version
            配置相關(guān)選項(xiàng)(Configuration Dependent Option)
            M680x0 選項(xiàng)
            -m68000 -m68020 -m68020-40 -m68030 -m68040 -m68881 -mbitfield -mc68000 -mc68020 -mfpa -mnobitfield -mrtd -mshort -msoft-float

            VAX選項(xiàng)
            -mg -mgnu -munix

            SPARC選項(xiàng)
            -mepilogue -mfpu -mhard-float -mno-fpu -mno-epilogue -msoft-float -msparclite -mv8 -msupersparc -mcypress

            Convex選項(xiàng)
            -margcount -mc1 -mc2 -mnoargcount

            AMD29K選項(xiàng)
            -m29000 -m29050 -mbw -mdw -mkernel-registers -mlarge -mnbw -mnodw -msmall -mstack-check -muser-registers

            M88K選項(xiàng)
            -m88000 -m88100 -m88110 -mbig-pic -mcheck-zero-division -mhandle-large-shift -midentify-revision -mno-check-zero-division -mno-ocs-debug-info -mno-ocs-frame-position -mno-optimize-arg-area -mno-serialize-volatile -mno-underscores -mocs-debug-info -mocs-frame-position -moptimize-arg-area -mserialize-volatile -mshort-data-num -msvr3 -msvr4 -mtrap-large-shift -muse-div-instruction -mversion-03.00 -mwarn-passed-structs

            RS6000選項(xiàng)
            -mfp-in-toc -mno-fop-in-toc

            RT選項(xiàng)
            -mcall-lib-mul -mfp-arg-in-fpregs -mfp-arg-in-gregs -mfull-fp-blocks -mhc-struct-return -min-line-mul -mminimum-fp-blocks -mnohc-struct-return

            MIPS選項(xiàng)
            -mcpu=cpu type -mips2 -mips3 -mint64 -mlong64 -mmips-as -mgas -mrnames -mno-rnames -mgpopt -mno-gpopt -mstats -mno-stats -mmemcpy -mno-memcpy -mno-mips-tfile -mmips-tfile -msoft-float -mhard-float -mabicalls -mno-abicalls -mhalf-pic -mno-half-pic -G num -nocpp

            i386選項(xiàng)
            -m486 -mno-486 -msoft-float -mno-fp-ret-in-387

            HPPA選項(xiàng)
            -mpa-risc-1-0 -mpa-risc-1-1 -mkernel -mshared-libs -mno-shared-libs -mlong-calls -mdisable-fpregs -mdisable-indexing -mtrailing-colon

            i960選項(xiàng)
            -mcpu-type -mnumerics -msoft-float -mleaf-procedures -mno-leaf-procedures -mtail-call -mno-tail-call -mcomplex-addr -mno-complex-addr -mcode-align -mno-code-align -mic-compat -mic2.0-compat -mic3.0-compat -masm-compat -mintel-asm -mstrict-align -mno-strict-align -mold-align -mno-old-align

            DEC Alpha選項(xiàng)
            -mfp-regs -mno-fp-regs -mno-soft-float -msoft-float

            System V選項(xiàng)
            -G -Qy -Qn -YP,paths -Ym,dir

            代碼生成選項(xiàng)(Code Generation Option)
            -fcall-saved-reg -fcall-used-reg -ffixed-reg -finhibit-size-directive -fnonnull-objects -fno-common -fno-ident -fno-gnu-linker -fpcc-struct-return -fpic -fPIC -freg-struct-return -fshared-data -fshort-enums -fshort-double -fvolatile -fvolatile-global -fverbose-asm

             

            總體選項(xiàng)(Overall Option)

            -x language
            明確指出后面輸入文件的語言為language (而不是從文件名后綴得到的默認(rèn)選擇).這個選項(xiàng)應(yīng)用于后面 所有的輸入文件,直到遇著下一個`-x'選項(xiàng). language的可選值有`c', `objective-c', `c-header', `c++', `cpp-output', `assembler',`assembler-with-cpp'.
            -x none
            關(guān)閉任何對語種的明確說明,因此依據(jù)文件名后綴處理后面的文件(就象是從未使用過`-x'選項(xiàng)).

            如果只操作四個階段(預(yù)處理,編譯,匯編,連接)中的一部分,可以使用`-x'選項(xiàng)(或文件名后綴)告訴 gcc從哪里開始,`-c', `-S',`-E'選項(xiàng)告訴gcc到 哪里結(jié)束.注意,某些選項(xiàng)組合(例如, `-x cpp-output -E')使gcc不作任何事情.

            -c
            編譯或匯編源文件,但是不作連接.編譯器輸出對應(yīng)于源文件的目標(biāo)文件.

            缺省情況下, GCC通過用`.o'替換源文件名后綴`.c', `.i', `.s',等等,產(chǎn)生目標(biāo)文件名.可以使用-o選項(xiàng)選擇其他名字.

            GCC忽略-c選項(xiàng)后面任何無法識別的輸入文件(他們不需要編譯或匯編).

            -S
            編譯后即停止,不進(jìn)行匯編.對于每個輸入的非匯編語言文件,輸出文件是匯編語言文件.

            缺省情況下, GCC通過用`.o'替換源文件名后綴`.c', `.i',等等,產(chǎn)生 目標(biāo)文件名.可以使用-o選項(xiàng)選擇其他名字.

            GCC忽略任何不需要編譯的輸入文件.

            -E
            預(yù)處理后即停止,不進(jìn)行編譯.預(yù)處理后的代碼送往標(biāo)準(zhǔn)輸出.

            GCC忽略任何不需要預(yù)處理的輸入文件.

            -o file
            指定輸出文件為file.該選項(xiàng)不在乎GCC產(chǎn)生什么輸出,無論是可執(zhí)行文件,目標(biāo)文件,匯編文件還是 預(yù)處理后的C代碼.

            由于只能指定一個輸出文件,因此編譯多個輸入文件時,使用`-o'選項(xiàng)沒有意義,除非輸出一個可執(zhí)行文件.

            如果沒有使用`-o'選項(xiàng),默認(rèn)的輸出結(jié)果是:可執(zhí)行文件為`a.out', `source.suffix '的目標(biāo)文件是`source.o',匯編文件是 `source.s',而預(yù)處理后的C源代碼送往標(biāo)準(zhǔn)輸出.

            -v
            (在標(biāo)準(zhǔn)錯誤)顯示執(zhí)行編譯階段的命令.同時顯示編譯器驅(qū)動程序,預(yù)處理器,編譯器的版本號.
            -pipe
            在編譯過程的不同階段間使用管道而非臨時文件進(jìn)行通信.這個選項(xiàng)在某些系統(tǒng)上無法工作,因?yàn)槟切┫到y(tǒng)的 匯編器不能從管道讀取數(shù)據(jù). GNU的匯編器沒有這個問題.

            語言選項(xiàng)(LANGUAGE OPTIONS)

            下列選項(xiàng)控制編譯器能夠接受的C "方言":
            -ansi
            支持符合ANSI標(biāo)準(zhǔn)的C程序.

            這樣就會關(guān)閉GNU C中某些不兼容ANSI C的特性,例如asm, inlinetypeof關(guān)鍵字,以及諸如unixvax這些表明當(dāng)前系統(tǒng)類型的預(yù)定義宏.同時開啟 不受歡迎和極少使用的ANSI trigraph特性,以及禁止`$'成為標(biāo)識符的一部分.

            盡管使用了`-ansi'選項(xiàng),下面這些可選的關(guān)鍵字, __asm__, __extension__, __inline____typeof__仍然有效.你當(dāng)然不會把 他們用在ANSI C程序中,但可以把他們放在頭文件里,因?yàn)榫幾g包含這些頭文件的程序時,可能會指定 `-ansi'選項(xiàng).另外一些預(yù)定義宏,__unix____vax__,無論有沒有使用 `-ansi'選項(xiàng),始終有效.

            使用`-ansi'選項(xiàng)不會自動拒絕編譯非ANSI程序,除非增加`-pedantic'選項(xiàng)作為 `-ansi'選項(xiàng)的補(bǔ)充.

            使用`-ansi'選項(xiàng)的時候,預(yù)處理器會預(yù)定義一個__STRICT_ANSI__.有些頭文件 關(guān)注此宏,以避免聲明某些函數(shù),或者避免定義某些宏,這些函數(shù)和宏不被ANSI標(biāo)準(zhǔn)調(diào)用;這樣就不會干擾在其他地方 使用這些名字的程序了.

             

            -fno-asm
            不把asm, inlinetypeof當(dāng)作關(guān)鍵字,因此這些詞可以用做標(biāo)識符.__asm__, __inline____typeof__能夠替代他們. `-ansi' 隱含聲明了`-fno-asm'.
            -fno-builtin
            不接受不是兩個下劃線開頭的內(nèi)建函數(shù)(built-in function).目前受影響的函數(shù)有_exit, abort, abs, alloca, cos, exit, fabs, labs, memcmp, memcpy, sin, sqrt, strcmp, strcpy,strlen.

            `-ansi'選項(xiàng)能夠阻止alloca_exit成為內(nèi)建函數(shù).

             

            -fhosted
            按宿主環(huán)境編譯;他隱含聲明了`-fbuiltin'選項(xiàng),而且警告不正確的main函數(shù)聲明.
            -ffreestanding
            按獨(dú)立環(huán)境編譯;他隱含聲明了`-fno-builtin'選項(xiàng),而且對main函數(shù)沒有特別要求.

            (譯注:宿主環(huán)境(hosted environment)下所有的標(biāo)準(zhǔn)庫可用, main函數(shù)返回一個int,典型例子是除了 內(nèi)核以外幾乎所有的程序.對應(yīng)的獨(dú)立環(huán)境(freestanding environment)不存在標(biāo)準(zhǔn)庫,程序入口也不一定是 main,最明顯的例子就是操作系統(tǒng)內(nèi)核.詳情參考gcc網(wǎng)站最近的資料)

             

            -fno-strict-prototype
            對于沒有參數(shù)的函數(shù)聲明,例如`int foo ();',C風(fēng)格處理---即不說明參數(shù)個數(shù)或類型. (僅針對C++).正常情況下,這樣的函數(shù)fooC++中意味著參數(shù)為空.

             

            -trigraphs
            支持ANSI C trigraphs. `-ansi'選項(xiàng)隱含聲明了`-trigraphs'.

             

            -traditional
            試圖支持傳統(tǒng)C編譯器的某些方面.詳見GNU C手冊,我們已經(jīng)把細(xì)節(jié)清單從這里刪除,這樣當(dāng)內(nèi)容過時后,人們也不會 埋怨我們.

            除了一件事:對于C++程序(不是C), `-traditional'選項(xiàng)帶來一個附加效應(yīng),允許對 this賦值.他和`-fthis-is-variable'選項(xiàng)的效果一樣.

             

            -traditional-cpp
            試圖支持傳統(tǒng)C預(yù)處理器的某些方面.特別是上面提到有關(guān)預(yù)處理器的內(nèi)容,但是不包括 `-traditional'選項(xiàng)的其他效應(yīng).

             

            -fdollars-in-identifiers
            允許在標(biāo)識符(identifier)中使用`$'字符(僅針對C++).你可以指定 `-fno-dollars-in-identifiers'選項(xiàng)顯明禁止使用`$'. (GNU C++在某些 目標(biāo)系統(tǒng)缺省允許`$',但不是所有系統(tǒng).)

             

            -fenum-int-equiv
            允許int類型到枚舉類型(enumeration)的隱式轉(zhuǎn)換(僅限于C++).正常情況下GNU C++允許從 enumint的轉(zhuǎn)換,反之則不行.

             

            -fexternal-templates
            為模板聲明(template declaration)產(chǎn)生較小的代碼(僅限于C++),方法是對于每個模板函數(shù) (template function),只在定義他們的地方生成一個副本.想要成功使用這個選項(xiàng),你必須在所有使用模板的 文件中,標(biāo)記`#pragma implementation' (定義)`#pragma interface' (聲明).

            當(dāng)程序用`-fexternal-templates'編譯時,模板實(shí)例(template instantiation) 全部是外部類型.你必須讓需要的實(shí)例在實(shí)現(xiàn)文件中出現(xiàn).可以通過typedef實(shí)現(xiàn)這一點(diǎn),他引用所需的每個 實(shí)例.相對應(yīng)的,如果編譯時使用缺省選項(xiàng)`-fno-external-templates',所有模板實(shí)例明確的設(shè)為內(nèi)置.

             

            -fall-virtual
            所有可能的成員函數(shù)默認(rèn)為虛函數(shù).所有的成員函數(shù)(除了構(gòu)造子函數(shù)和newdelete 成員操作符)視為所在類的虛函數(shù).

            這不表明每次調(diào)用成員函數(shù)都將通過內(nèi)部虛函數(shù)表.有些情況下,編譯器能夠判斷出可以直接調(diào)用某個虛函數(shù);這時就 直接調(diào)用.

             

            -fcond-mismatch
            允許條件表達(dá)式的第二和第三個參數(shù)的類型不匹配.這種表達(dá)式的值是void.

             

            -fthis-is-variable
            允許對this賦值(僅對C++).合并用戶自定義的自由存儲管理機(jī)制到C++,使可賦值的 `this'顯得不合時宜.因此,默認(rèn)情況下,類成員函數(shù)內(nèi)部對this賦值是無效操作.然而為了 向后兼容,你可以通過`-fthis-is-variable'選項(xiàng)使這種操作有效.

             

            -funsigned-char
            char定義為無符號類型,如同unsigned char.

            各種機(jī)器都有自己缺省的char類型.既可能是unsigned char也可能是signed char .

            理想情況下,當(dāng)依賴于數(shù)據(jù)的符號性時,一個可移植程序總是應(yīng)該使用signed charunsigned char.但是許多程序已經(jīng)寫成只用簡單的char,并且期待這是有符號數(shù)(或者無符號數(shù),具體情況取決于 編寫程序的目標(biāo)機(jī)器).這個選項(xiàng),和它的反義選項(xiàng),使那樣的程序工作在對應(yīng)的默認(rèn)值上.

            char的類型始終應(yīng)該明確定義為signed charunsigned char,即使 它表現(xiàn)的和其中之一完全一樣.

             

            -fsigned-char
            char定義為有符號類型,如同signed char.

            這個選項(xiàng)等同于`-fno-unsigned-char',他是the negative form of `-funsigned-char'的相反選項(xiàng).同樣, `-fno-signed-char'等價于 `-funsigned-char'.

             

            -fsigned-bitfields
            -funsigned-bitfields
            -fno-signed-bitfields
            -fno-unsigned-bitfields
            如果沒有明確聲明`signed'`unsigned'修飾符,這些選項(xiàng)用來定義有符號位域 (bitfield)或無符號位域.缺省情況下,位域是有符號的,因?yàn)樗麄兝^承的基本整數(shù)類型,int,是 有符號數(shù).

            然而,如果指定了`-traditional'選項(xiàng),位域永遠(yuǎn)是無符號數(shù).

             

            -fwritable-strings
            把字符串常量存儲到可寫數(shù)據(jù)段,而且不做特別對待.這是為了兼容一些老程序,他們假設(shè)字符串常量是可寫的. `-traditional'選項(xiàng)也有相同效果.

            篡改字符串常量是一個非常糟糕的想法; ``常量''就應(yīng)該是常量.

            posted @ 2009-08-03 14:30 chaosuper 閱讀(199) | 評論 (0)編輯 收藏

                 摘要: GCC 2 Section: GNU Tools (1)Updated: 2003/12/05 預(yù)處理器選項(xiàng)(Preprocessor Option) 下列選項(xiàng)針對C預(yù)處理器,預(yù)處理器用在正式編譯以前,對C 源文件進(jìn)行某種處理. 如果指定了`-E'選項(xiàng), GCC只進(jìn)行預(yù)處理工作.下面的某些選項(xiàng)必須和`-E'選項(xiàng)一起才 有意義,因?yàn)樗麄兊妮敵鼋Y(jié)果不能用于編譯. -includ...  閱讀全文
            posted @ 2009-08-03 14:30 chaosuper 閱讀(197) | 評論 (0)編輯 收藏

            0 Makefile概述


            什么是makefile?或許很多Winodws的程序員都不知道這個東西,因?yàn)槟切¦indows的IDE都為你做了這個工作,但我覺得要作一個好的和professional的程序員,makefile還是要懂。這就好像現(xiàn)在有這么多的HTML的編輯器,但如果你想成為一個專業(yè)人士,你還是要了解HTML的標(biāo)識的含義。特別在Unix下的軟件編譯,你就不能不自己寫makefile了,會不會寫makefile,從一個側(cè)面說明了一個人是否具備完成大型工程的能力。

            因?yàn)椋琺akefile關(guān)系到了整個工程的編譯規(guī)則。一個工程中的源文件不計(jì)數(shù),其按類型、功能、模塊分別放在若干個目錄中,makefile定義了一系列的規(guī)則來指定,哪些文件需要先編譯,哪些文件需要后編譯,哪些文件需要重新編譯,甚至于進(jìn)行更復(fù)雜的功能操作,因?yàn)閙akefile就像一個Shell腳本一樣,其中也可以執(zhí)行操作系統(tǒng)的命令。

            makefile帶來的好處就是——“自動化編譯”,一旦寫好,只需要一個make命令,整個工程完全自動編譯,極大的提高了軟件開發(fā)的效率。make是一個命令工具,是一個解釋makefile中指令的命令工具,一般來說,大多數(shù)的IDE都有這個命令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。可見,makefile都成為了一種在工程方面的編譯方法。

            現(xiàn)在講述如何寫makefile的文章比較少,這是我想寫這篇文章的原因。當(dāng)然,不同產(chǎn)商的make各不相同,也有不同的語法,但其本質(zhì)都是在“文件依賴性”上做文章,這里,我僅對GNU的make進(jìn)行講述,我的環(huán)境是RedHat Linux 8.0,make的版本是3.80。必竟,這個make是應(yīng)用最為廣泛的,也是用得最多的。而且其還是最遵循于IEEE 1003.2-1992 標(biāo)準(zhǔn)的(POSIX.2)。

            在這篇文檔中,將以C/C++的源碼作為我們基礎(chǔ),所以必然涉及一些關(guān)于C/C++的編譯的知識,相關(guān)于這方面的內(nèi)容,還請各位查看相關(guān)的編譯器的文檔。這里所默認(rèn)的編譯器是UNIX下的GCC和CC。

            0.1 關(guān)于程序的編譯和鏈接

            在此,我想多說關(guān)于程序編譯的一些規(guī)范和方法,一般來說,無論是C、C++、還是pas,首先要把源文件編譯成中間代碼文件,在Windows下也就是 .obj 文件,UNIX下是 .o 文件,即 Object File,這個動作叫做編譯(compile)。然后再把大量的Object File合成執(zhí)行文件,這個動作叫作鏈接(link)。

            編譯時,編譯器需要的是語法的正確,函數(shù)與變量的聲明的正確。對于后者,通常是你需要告訴編譯器頭文件的所在位置(頭文件中應(yīng)該只是聲明,而定義應(yīng)該放在C/C++文件中),只要所有的語法正確,編譯器就可以編譯出中間目標(biāo)文件。一般來說,每個源文件都應(yīng)該對應(yīng)于一個中間目標(biāo)文件(O文件或是OBJ文件)。

            鏈接時,主要是鏈接函數(shù)和全局變量,所以,我們可以使用這些中間目標(biāo)文件(O文件或是OBJ文件)來鏈接我們的應(yīng)用程序。鏈接器并不管函數(shù)所在的源文件,只管函數(shù)的中間目標(biāo)文件(Object File),在大多數(shù)時候,由于源文件太多,編譯生成的中間目標(biāo)文件太多,而在鏈接時需要明顯地指出中間目標(biāo)文件名,這對于編譯很不方便,所以,我們要給中間目標(biāo)文件打個包,在Windows下這種包叫“庫文件”(Library File),也就是 .lib 文件,在UNIX下,是Archive File,也就是 .a 文件。

            總結(jié)一下,源文件首先會生成中間目標(biāo)文件,再由中間目標(biāo)文件生成執(zhí)行文件。在編譯時,編譯器只檢測程序語法,和函數(shù)、變量是否被聲明。如果函數(shù)未被聲明,編譯器會給出一個警告,但可以生成Object File。而在鏈接程序時,鏈接器會在所有的Object File中找尋函數(shù)的實(shí)現(xiàn),如果找不到,那到就會報(bào)鏈接錯誤碼(Linker Error),在VC下,這種錯誤一般是:Link 2001錯誤,意思說是說,鏈接器未能找到函數(shù)的實(shí)現(xiàn)。你需要指定函數(shù)的Object File.

            好,言歸正傳,GNU的make有許多的內(nèi)容,閑言少敘,還是讓我們開始吧。

            1 Makefile 介紹


            make命令執(zhí)行時,需要一個 Makefile 文件,以告訴make命令需要怎么樣的去編譯和鏈接程序。

            首先,我們用一個示例來說明Makefile的書寫規(guī)則。以便給大家一個感興認(rèn)識。這個示例來源于GNU的make使用手冊,在這個示例中,我們的工程有8個C文件,和3個頭文件,我們要寫一個Makefile來告訴make命令如何編譯和鏈接這幾個文件。我們的規(guī)則是:

            1. 如果這個工程沒有編譯過,那么我們的所有C文件都要編譯并被鏈接。
            2. 如果這個工程的某幾個C文件被修改,那么我們只編譯被修改的C文件,并鏈接目標(biāo)程序。
            3. 如果這個工程的頭文件被改變了,那么我們需要編譯引用了這幾個頭文件的C文件,并鏈接目標(biāo)程序。

            只要我們的Makefile寫得夠好,所有的這一切,我們只用一個make命令就可以完成,make命令會自動智能地根據(jù)當(dāng)前的文件修改的情況來確定哪些文件需要重編譯,從而自己編譯所需要的文件和鏈接目標(biāo)程序。

            1.1 Makefile的規(guī)則

            在講述這個Makefile之前,還是讓我們先來粗略地看一看Makefile的規(guī)則。
            target ... : prerequisites ...
            command
            ...
            ...
            
            target也就是一個目標(biāo)文件,可以是Object File,也可以是執(zhí)行文件。還可以是一個標(biāo)簽(Label),對于標(biāo)簽這種特性,在后續(xù)的“偽目標(biāo)”章節(jié)中會有敘述。

            prerequisites就是,要生成那個target所需要的文件或是目標(biāo)。

            command也就是make需要執(zhí)行的命令。(任意的Shell命令)

            這是一個文件的依賴關(guān)系,也就是說,target這一個或多個的目標(biāo)文件依賴于prerequisites中的文件,其生成規(guī)則定義在command中。說白一點(diǎn)就是說,prerequisites中如果有一個以上的文件比target文件要新的話,command所定義的命令就會被執(zhí)行。這就是Makefile的規(guī)則。也就是Makefile中最核心的內(nèi)容。

            說到底,Makefile的東西就是這樣一點(diǎn),好像我的這篇文檔也該結(jié)束了。呵呵。還不盡然,這是Makefile的主線和核心,但要寫好一個Makefile還不夠,我會以后面一點(diǎn)一點(diǎn)地結(jié)合我的工作經(jīng)驗(yàn)給你慢慢到來。內(nèi)容還多著呢。:)

            1.2 一個示例

            正如前面所說的,如果一個工程有3個頭文件,和8個C文件,我們?yōu)榱送瓿汕懊嫠龅哪侨齻€規(guī)則,我們的Makefile應(yīng)該是下面的這個樣子的。
                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
            
            反斜杠(\)是換行符的意思。這樣比較便于Makefile的易讀。我們可以把這個內(nèi)容保存在文件為“Makefile”或“makefile”的文件中,然后在該目錄下直接輸入命令“make”就可以生成執(zhí)行文件edit。如果要刪除執(zhí)行文件和所有的中間目標(biāo)文件,那么,只要簡單地執(zhí)行一下“make clean”就可以了。

            在這個makefile中,目標(biāo)文件(target)包含:執(zhí)行文件edit和中間目標(biāo)文件(*.o),依賴文件(prerequisites)就是冒號后面的那些 .c 文件和 .h文件。每一個 .o 文件都有一組依賴文件,而這些 .o 文件又是執(zhí)行文件 edit 的依賴文件。依賴關(guān)系的實(shí)質(zhì)上就是說明了目標(biāo)文件是由哪些文件生成的,換言之,目標(biāo)文件是哪些文件更新的。

            在定義好依賴關(guān)系后,后續(xù)的那一行定義了如何生成目標(biāo)文件的操作系統(tǒng)命令,一定要以一個Tab鍵作為開頭。記住,make并不管命令是怎么工作的,他只管執(zhí)行所定義的命令。make會比較targets文件和prerequisites文件的修改日期,如果prerequisites文件的日期要比targets文件的日期要新,或者target不存在的話,那么,make就會執(zhí)行后續(xù)定義的命令。

            這里要說明一點(diǎn)的是,clean不是一個文件,它只不過是一個動作名字,有點(diǎn)像C語言中的lable一樣,其冒號后什么也沒有,那么,make就不會自動去找文件的依賴性,也就不會自動執(zhí)行其后所定義的命令。要執(zhí)行其后的命令,就要在make命令后明顯得指出這個lable的名字。這樣的方法非常有用,我們可以在一個makefile中定義不用的編譯或是和編譯無關(guān)的命令,比如程序的打包,程序的備份,等等。

            1.3 make是如何工作的

            在默認(rèn)的方式下,也就是我們只輸入make命令。那么,

            1. make會在當(dāng)前目錄下找名字叫“Makefile”或“makefile”的文件。
            2. 如果找到,它會找文件中的第一個目標(biāo)文件(target),在上面的例子中,他會找到“edit”這個文件,并把這個文件作為最終的目標(biāo)文件。
            3. 如果edit文件不存在,或是edit所依賴的后面的 .o 文件的文件修改時間要比edit這個文件新,那么,他就會執(zhí)行后面所定義的命令來生成edit這個文件。
            4. 如果edit所依賴的.o文件也存在,那么make會在當(dāng)前文件中找目標(biāo)為.o文件的依賴性,如果找到則再根據(jù)那一個規(guī)則生成.o文件。(這有點(diǎn)像一個堆棧的過程)
            5. 當(dāng)然,你的C文件和H文件是存在的啦,于是make會生成 .o 文件,然后再用 .o 文件生命make的終極任務(wù),也就是執(zhí)行文件edit了。

            這就是整個make的依賴性,make會一層又一層地去找文件的依賴關(guān)系,直到最終編譯出第一個目標(biāo)文件。在找尋的過程中,如果出現(xiàn)錯誤,比如最后被依賴的文件找不到,那么make就會直接退出,并報(bào)錯,而對于所定義的命令的錯誤,或是編譯不成功,make根本不理。make只管文件的依賴性,即,如果在我找了依賴關(guān)系之后,冒號后面的文件還是不在,那么對不起,我就不工作啦。

            通過上述分析,我們知道,像clean這種,沒有被第一個目標(biāo)文件直接或間接關(guān)聯(lián),那么它后面所定義的命令將不會被自動執(zhí)行,不過,我們可以顯示要make執(zhí)行。即命令——“make clean”,以此來清除所有的目標(biāo)文件,以便重編譯。

            于是在我們編程中,如果這個工程已被編譯過了,當(dāng)我們修改了其中一個源文件,比如file.c,那么根據(jù)我們的依賴性,我們的目標(biāo)file.o會被重編譯(也就是在這個依性關(guān)系后面所定義的命令),于是file.o的文件也是最新的啦,于是file.o的文件修改時間要比edit要新,所以edit也會被重新鏈接了(詳見edit目標(biāo)文件后定義的命令)。

            而如果我們改變了“command.h”,那么,kdb.o、command.o和files.o都會被重編譯,并且,edit會被重鏈接。

            1.4 makefile中使用變量

            在上面的例子中,先讓我們看看edit的規(guī)則:
                  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]文件的字符串被重復(fù)了兩次,如果我們的工程需要加入一個新的[.o]文件,那么我們需要在兩個地方加(應(yīng)該是三個地方,還有一個地方在clean中)。當(dāng)然,我們的makefile并不復(fù)雜,所以在兩個地方加也不累,但如果makefile變得復(fù)雜,那么我們就有可能會忘掉一個需要加入的地方,而導(dǎo)致編譯失敗。所以,為了makefile的易維護(hù),在makefile中我們可以使用變量。makefile的變量也就是一個字符串,理解成C語言中的宏可能會更好。

            比如,我們聲明一個變量,叫objects, OBJECTS, objs, OBJS, obj, 或是 OBJ,反正不管什么啦,只要能夠表示obj文件就行了。我們在makefile一開始就這樣定義:

                 objects = main.o kbd.o command.o display.o \
            insert.o search.o files.o utils.o
            
            于是,我們就可以很方便地在我們的makefile中以“$(objects)”的方式來使用這個變量了,于是我們的改良版makefile就變成下面這個樣子:
                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 文件加入,我們只需簡單地修改一下 objects 變量就可以了。

            關(guān)于變量更多的話題,我會在后續(xù)給你一一道來。

            1.5 讓make自動推導(dǎo)

            GNU的make很強(qiáng)大,它可以自動推導(dǎo)文件以及文件依賴關(guān)系后面的命令,于是我們就沒必要去在每一個[.o]文件后都寫上類似的命令,因?yàn)椋覀兊膍ake會自動識別,并自己推導(dǎo)命令。

            只要make看到一個[.o]文件,它就會自動的把[.c]文件加在依賴關(guān)系中,如果make找到一個whatever.o,那么whatever.c,就會是whatever.o的依賴文件。并且 cc -c whatever.c 也會被推導(dǎo)出來,于是,我們的makefile再也不用寫得這么復(fù)雜。我們的是新的makefile又出爐了。

                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)
            
            這種方法,也就是make的“隱晦規(guī)則”。上面文件內(nèi)容中,“.PHONY”表示,clean是個偽目標(biāo)文件。

            關(guān)于更為詳細(xì)的“隱晦規(guī)則”和“偽目標(biāo)文件”,我會在后續(xù)給你一一道來。

            1.6 另類風(fēng)格的makefile

            即然我們的make可以自動推導(dǎo)命令,那么我看到那堆[.o]和[.h]的依賴就有點(diǎn)不爽,那么多的重復(fù)的[.h],能不能把其收攏起來,好吧,沒有問題,這個對于make來說很容易,誰叫它提供了自動推導(dǎo)命令和文件的功能呢?來看看最新風(fēng)格的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)
            
            這種風(fēng)格,讓我們的makefile變得很簡單,但我們的文件依賴關(guān)系就顯得有點(diǎn)凌亂了。魚和熊掌不可兼得。還看你的喜好了。我是不喜歡這種風(fēng)格的,一是文件的依賴關(guān)系看不清楚,二是如果文件一多,要加入幾個新的.o文件,那就理不清楚了。

            1.7 清空目標(biāo)文件的規(guī)則

            每個Makefile中都應(yīng)該寫一個清空目標(biāo)文件(.o和執(zhí)行文件)的規(guī)則,這不僅便于重編譯,也很利于保持文件的清潔。這是一個“修養(yǎng)”(呵呵,還記得我的《編程修養(yǎng)》嗎)。一般的風(fēng)格都是:
                    clean:
            rm edit $(objects)
            
            更為穩(wěn)健的做法是:
                    .PHONY : clean
            clean :
            -rm edit $(objects)
            
            前面說過,.PHONY意思表示clean是一個“偽目標(biāo)”,。而在rm命令前面加了一個小減號的意思就是,也許某些文件出現(xiàn)問題,但不要管,繼續(xù)做后面的事。當(dāng)然,clean的規(guī)則不要放在文件的開頭,不然,這就會變成make的默認(rèn)目標(biāo),相信誰也不愿意這樣。不成文的規(guī)矩是——“clean從來都是放在文件的最后”。

            上面就是一個makefile的概貌,也是makefile的基礎(chǔ),下面還有很多makefile的相關(guān)細(xì)節(jié),準(zhǔn)備好了嗎?準(zhǔn)備好了就來。


            2 Makefile 總述

            2.1 Makefile里有什么?

            Makefile里主要包含了五個東西:顯式規(guī)則、隱晦規(guī)則、變量定義、文件指示和注釋。

            1. 顯式規(guī)則。顯式規(guī)則說明了,如何生成一個或多的的目標(biāo)文件。這是由Makefile的書寫者明顯指出,要生成的文件,文件的依賴文件,生成的命令。
            2. 隱晦規(guī)則。由于我們的make有自動推導(dǎo)的功能,所以隱晦的規(guī)則可以讓我們比較粗糙地簡略地書寫Makefile,這是由make所支持的。
            3. 變量的定義。在Makefile中我們要定義一系列的變量,變量一般都是字符串,這個有點(diǎn)你C語言中的宏,當(dāng)Makefile被執(zhí)行時,其中的變量都會被擴(kuò)展到相應(yīng)的引用位置上。
            4. 文件指示。其包括了三個部分,一個是在一個Makefile中引用另一個Makefile,就像C語言中的include一樣;另一個是指根據(jù)某些情況指定Makefile中的有效部分,就像C語言中的預(yù)編譯#if一樣;還有就是定義一個多行的命令。有關(guān)這一部分的內(nèi)容,我會在后續(xù)的部分中講述。
            5. 注釋。Makefile中只有行注釋,和UNIX的Shell腳本一樣,其注釋是用“#”字符,這個就像C/C++中的“//”一樣。如果你要在你的Makefile中使用“#”字符,可以用反斜框進(jìn)行轉(zhuǎn)義,如:“\#”。

            最后,還值得一提的是,在Makefile中的命令,必須要以[Tab]鍵開始。

            2.2Makefile的文件名

            默認(rèn)的情況下,make命令會在當(dāng)前目錄下按順序找尋文件名為“GNUmakefile”、“makefile”、“Makefile”的文件,找到了解釋這個文件。在這三個文件名中,最好使用“Makefile”這個文件名,因?yàn)椋@個文件名第一個字符為大寫,這樣有一種顯目的感覺。最好不要用“GNUmakefile”,這個文件是GNU的make識別的。有另外一些make只對全小寫的“makefile”文件名敏感,但是基本上來說,大多數(shù)的make都支持“makefile”和“Makefile”這兩種默認(rèn)文件名。

            當(dāng)然,你可以使用別的文件名來書寫Makefile,比如:“Make.Linux”,“Make.Solaris”,“Make.AIX”等,如果要指定特定的Makefile,你可以使用make的“-f”和“--file”參數(shù),如:make -f Make.Linux或make --file Make.AIX。

            2.3 引用其它的Makefile

            在Makefile使用include關(guān)鍵字可以把別的Makefile包含進(jìn)來,這很像C語言的#include,被包含的文件會原模原樣的放在當(dāng)前文件的包含位置。include的語法是:
            include <filename>
            filename可以是當(dāng)前操作系統(tǒng)Shell的文件模式(可以保含路徑和通配符)

            在include前面可以有一些空字符,但是絕不能是[Tab]鍵開始。include和可以用一個或多個空格隔開。舉個例子,你有這樣幾個Makefile:a.mk、b.mk、c.mk,還有一個文件叫foo.make,以及一個變量$(bar),其包含了e.mk和f.mk,那么,下面的語句:

                include foo.make *.mk $(bar)
            
            等價于:
                include foo.make a.mk b.mk c.mk e.mk f.mk
            
            make命令開始時,會把找尋include所指出的其它Makefile,并把其內(nèi)容安置在當(dāng)前的位置。就好像C/C++的#include指令一樣。如果文件都沒有指定絕對路徑或是相對路徑的話,make會在當(dāng)前目錄下首先尋找,如果當(dāng)前目錄下沒有找到,那么,make還會在下面的幾個目錄下找:

            1. 如果make執(zhí)行時,有“-I”或“--include-dir”參數(shù),那么make就會在這個參數(shù)所指定的目錄下去尋找。
            2. 如果目錄 /include(一般是:/usr/local/bin或/usr/include)存在的話,make也會去找。

            如果有文件沒有找到的話,make會生成一條警告信息,但不會馬上出現(xiàn)致命錯誤。它會繼續(xù)載入其它的文件,一旦完成makefile的讀取,make會再重試這些沒有找到,或是不能讀取的文件,如果還是不行,make才會出現(xiàn)一條致命信息。如果你想讓make不理那些無法讀取的文件,而繼續(xù)執(zhí)行,你可以在include前加一個減號“-”。如:

            -include <filename>
            其表示,無論include過程中出現(xiàn)什么錯誤,都不要報(bào)錯繼續(xù)執(zhí)行。和其它版本make兼容的相關(guān)命令是sinclude,其作用和這一個是一樣的。

            2.4 環(huán)境變量 MAKEFILES

            如果你的當(dāng)前環(huán)境中定義了環(huán)境變量MAKEFILES,那么,make會把這個變量中的值做一個類似于include的動作。這個變量中的值是其它的Makefile,用空格分隔。只是,它和include不同的是,從這個環(huán)境變中引入的Makefile的“目標(biāo)”不會起作用,如果環(huán)境變量中定義的文件發(fā)現(xiàn)錯誤,make也會不理。

            但是在這里我還是建議不要使用這個環(huán)境變量,因?yàn)橹灰@個變量一被定義,那么當(dāng)你使用make時,所有的Makefile都會受到它的影響,這絕不是你想看到的。在這里提這個事,只是為了告訴大家,也許有時候你的Makefile出現(xiàn)了怪事,那么你可以看看當(dāng)前環(huán)境中有沒有定義這個變量。

            2.5 make的工作方式

            GNU的make工作時的執(zhí)行步驟入下:(想來其它的make也是類似)

            1. 讀入所有的Makefile。
            2. 讀入被include的其它Makefile。
            3. 初始化文件中的變量。
            4. 推導(dǎo)隱晦規(guī)則,并分析所有規(guī)則。
            5. 為所有的目標(biāo)文件創(chuàng)建依賴關(guān)系鏈。
            6. 根據(jù)依賴關(guān)系,決定哪些目標(biāo)要重新生成。
            7. 執(zhí)行生成命令。

            1-5步為第一個階段,6-7為第二個階段。第一個階段中,如果定義的變量被使用了,那么,make會把其展開在使用的位置。但make并不會完全馬上展開,make使用的是拖延戰(zhàn)術(shù),如果變量出現(xiàn)在依賴關(guān)系的規(guī)則中,那么僅當(dāng)這條依賴被決定要使用了,變量才會在其內(nèi)部展開。

            當(dāng)然,這個工作方式你不一定要清楚,但是知道這個方式你也會對make更為熟悉。有了這個基礎(chǔ),后續(xù)部分也就容易看懂了。

            3 Makefile書寫規(guī)則


            規(guī)則包含兩個部分,一個是依賴關(guān)系,一個是生成目標(biāo)的方法。

            在Makefile中,規(guī)則的順序是很重要的,因?yàn)椋琈akefile中只應(yīng)該有一個最終目標(biāo),其它的目標(biāo)都是被這個目標(biāo)所連帶出來的,所以一定要讓make知道你的最終目標(biāo)是什么。一般來說,定義在Makefile中的目標(biāo)可能會有很多,但是第一條規(guī)則中的目標(biāo)將被確立為最終的目標(biāo)。如果第一條規(guī)則中的目標(biāo)有很多個,那么,第一個目標(biāo)會成為最終的目標(biāo)。make所完成的也就是這個目標(biāo)。

            好了,還是讓我們來看一看如何書寫規(guī)則。

            3.1 規(guī)則舉例

             foo.o : foo.c defs.h       # foo模塊
            cc -c -g foo.c
            
            看到這個例子,各位應(yīng)該不是很陌生了,前面也已說過,foo.o是我們的目標(biāo),foo.c和defs.h是目標(biāo)所依賴的源文件,而只有一個命令“cc -c -g foo.c”(以Tab鍵開頭)。這個規(guī)則告訴我們兩件事:

            1. 文件的依賴關(guān)系,foo.o依賴于foo.c和defs.h的文件,如果foo.c和defs.h的文件日期要比foo.o文件日期要新,或是foo.o不存在,那么依賴關(guān)系發(fā)生。
            2. 如果生成(或更新)foo.o文件。也就是那個cc命令,其說明了,如何生成foo.o這個文件。(當(dāng)然foo.c文件include了defs.h文件)

            3.2 規(guī)則的語法

                  targets : prerequisites
            command
            ...
            
            或是這樣:
                  targets : prerequisites ; command
            command
            ...
            
            targets是文件名,以空格分開,可以使用通配符。一般來說,我們的目標(biāo)基本上是一個文件,但也有可能是多個文件。

            command是命令行,如果其不與“target:prerequisites”在一行,那么,必須以[Tab鍵]開頭,如果和prerequisites在一行,那么可以用分號做為分隔。(見上)

            prerequisites也就是目標(biāo)所依賴的文件(或依賴目標(biāo))。如果其中的某個文件要比目標(biāo)文件要新,那么,目標(biāo)就被認(rèn)為是“過時的”,被認(rèn)為是需要重生成的。這個在前面已經(jīng)講過了。

            如果命令太長,你可以使用反斜框(‘\’)作為換行符。make對一行上有多少個字符沒有限制。規(guī)則告訴make兩件事,文件的依賴關(guān)系和如何成成目標(biāo)文件。

            一般來說,make會以UNIX的標(biāo)準(zhǔn)Shell,也就是/bin/sh來執(zhí)行命令。

            3.3 在規(guī)則中使用通配符

            如果我們想定義一系列比較類似的文件,我們很自然地就想起使用通配符。make支持三各通配符:“*”,“?”和“[...]”。這是和Unix的B-Shell是相同的。

            波浪號(“~”)字符在文件名中也有比較特殊的用途。如果是“~/test”,這就表示當(dāng)前用戶的$HOME目錄下的test目錄。而“~hchen/test”則表示用戶hchen的宿主目錄下的test目錄。(這些都是Unix下的小知識了,make也支持)而在Windows或是MS-DOS下,用戶沒有宿主目錄,那么波浪號所指的目錄則根據(jù)環(huán)境變量“HOME”而定。

            通配符代替了你一系列的文件,如“*.c”表示所以后綴為c的文件。一個需要我們注意的是,如果我們的文件名中有通配符,如:“*”,那么可以用轉(zhuǎn)義字符“\”,如“\*”來表示真實(shí)的“*”字符,而不是任意長度的字符串。

            好吧,還是先來看幾個例子吧:

                clean:
            rm -f *.o
            
            上面這個例子我不不多說了,這是操作系統(tǒng)Shell所支持的通配符。這是在命令中的通配符。
                print: *.c
            lpr -p $?
            touch print
            
            上面這個例子說明了通配符也可以在我們的規(guī)則中,目標(biāo)print依賴于所有的[.c]文件。其中的“$?”是一個自動化變量,我會在后面給你講述。
                objects = *.o
            
            上面這個例子,表示了,通符同樣可以用在變量中。并不是說[*.o]會展開,不!objects的值就是“*.o”。Makefile中的變量其實(shí)就是C/C++中的宏。如果你要讓通配符在變量中展開,也就是讓objects的值是所有[.o]的文件名的集合,那么,你可以這樣:
                objects := $(wildcard *.o)
            
            這種用法由關(guān)鍵字“wildcard”指出,關(guān)于Makefile的關(guān)鍵字,我們將在后面討論。

            3.4 文件搜尋

            在一些大的工程中,有大量的源文件,我們通常的做法是把這許多的源文件分類,并存放在不同的目錄中。所以,當(dāng)make需要去找尋文件的依賴關(guān)系時,你可以在文件前加上路徑,但最好的方法是把一個路徑告訴make,讓make在自動去找。

            Makefile文件中的特殊變量“VPATH”就是完成這個功能的,如果沒有指明這個變量,make只會在當(dāng)前的目錄中去找尋依賴文件和目標(biāo)文件。如果定義了這個變量,那么,make就會在當(dāng)當(dāng)前目錄找不到的情況下,到所指定的目錄中去找尋文件了。

                VPATH = src:../headers
            
            上面的的定義指定兩個目錄,“src”和“../headers”,make會按照這個順序進(jìn)行搜索。目錄由“冒號”分隔。(當(dāng)然,當(dāng)前目錄永遠(yuǎn)是最高優(yōu)先搜索的地方)

            另一個設(shè)置文件搜索路徑的方法是使用make的“vpath”關(guān)鍵字(注意,它是全小寫的),這不是變量,這是一個make的關(guān)鍵字,這和上面提到的那個VPATH變量很類似,但是它更為靈活。它可以指定不同的文件在不同的搜索目錄中。這是一個很靈活的功能。它的使用方法有三種:

            1. vpath < pattern> < directories>
              為符合模式< pattern>的文件指定搜索目錄< directories>。
            2. vpath < pattern>
              清除符合模式< pattern>的文件的搜索目錄。
            3. vpath
              清除所有已被設(shè)置好了的文件搜索目錄。

            vapth使用方法中的< pattern>需要包含“%”字符。“%”的意思是匹配零或若干字符,例如,“%.h”表示所有以“.h”結(jié)尾的文件。< pattern>指定了要搜索的文件集,而< directories>則指定了 的文件集的搜索的目錄。例如:

                vpath %.h ../headers
            
            該語句表示,要求make在“../headers”目錄下搜索所有以“.h”結(jié)尾的文件。(如果某文件在當(dāng)前目錄沒有找到的話)

            我們可以連續(xù)地使用vpath語句,以指定不同搜索策略。如果連續(xù)的vpath語句中出現(xiàn)了相同的< pattern>,或是被重復(fù)了的< pattern>,那么,make會按照vpath語句的先后順序來執(zhí)行搜索。如:

                vpath %.c foo
            vpath %   blish
            vpath %.c bar
            
            其表示“.c”結(jié)尾的文件,先在“foo”目錄,然后是“blish”,最后是“bar”目錄。
                vpath %.c foo:bar
            vpath %   blish
            
            而上面的語句則表示“.c”結(jié)尾的文件,先在“foo”目錄,然后是“bar”目錄,最后才是“blish”目錄。

            3.5 偽目標(biāo)

            最早先的一個例子中,我們提到過一個“clean”的目標(biāo),這是一個“偽目標(biāo)”,

                clean:
            rm *.o temp
            
            正像我們前面例子中的“clean”一樣,即然我們生成了許多文件編譯文件,我們也應(yīng)該提供一個清除它們的“目標(biāo)”以備完整地重編譯而用。 (以“make clean”來使用該目標(biāo))

            因?yàn)椋覀儾⒉簧?#8220;clean”這個文件。“偽目標(biāo)”并不是一個文件,只是一個標(biāo)簽,由于“偽目標(biāo)”不是文件,所以make無法生成它的依賴關(guān)系和決定它是否要執(zhí)行。我們只有通過顯示地指明這個“目標(biāo)”才能讓其生效。當(dāng)然,“偽目標(biāo)”的取名不能和文件名重名,不然其就失去了“偽目標(biāo)”的意義了。

            當(dāng)然,為了避免和文件重名的這種情況,我們可以使用一個特殊的標(biāo)記“.PHONY”來顯示地指明一個目標(biāo)是“偽目標(biāo)”,向make說明,不管是否有這個文件,這個目標(biāo)就是“偽目標(biāo)”。

                .PHONY : clean
            
            只要有這個聲明,不管是否有“clean”文件,要運(yùn)行“clean”這個目標(biāo),只有“make clean”這樣。于是整個過程可以這樣寫:
                 .PHONY: clean
            clean:
            rm *.o temp
            
            偽目標(biāo)一般沒有依賴的文件。但是,我們也可以為偽目標(biāo)指定所依賴的文件。偽目標(biāo)同樣可以作為“默認(rèn)目標(biāo)”,只要將其放在第一個。一個示例就是,如果你的Makefile需要一口氣生成若干個可執(zhí)行文件,但你只想簡單地敲一個make完事,并且,所有的目標(biāo)文件都寫在一個Makefile中,那么你可以使用“偽目標(biāo)”這個特性:
                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
            
            我們知道,Makefile中的第一個目標(biāo)會被作為其默認(rèn)目標(biāo)。我們聲明了一個“all”的偽目標(biāo),其依賴于其它三個目標(biāo)。由于偽目標(biāo)的特性是,總是被執(zhí)行的,所以其依賴的那三個目標(biāo)就總是不如“all”這個目標(biāo)新。所以,其它三個目標(biāo)的規(guī)則總是會被決議。也就達(dá)到了我們一口氣生成多個目標(biāo)的目的。“.PHONY : all”聲明了“all”這個目標(biāo)為“偽目標(biāo)”。

            隨便提一句,從上面的例子我們可以看出,目標(biāo)也可以成為依賴。所以,偽目標(biāo)同樣也可成為依賴。看下面的例子:

                .PHONY: cleanall cleanobj cleandiff
            cleanall : cleanobj cleandiff
            rm program
            cleanobj :
            rm *.o
            cleandiff :
            rm *.diff
            
            “make clean”將清除所有要被清除的文件。“cleanobj”和“cleandiff”這兩個偽目標(biāo)有點(diǎn)像“子程序”的意思。我們可以輸入“make cleanall”和“make cleanobj”和“make cleandiff”命令來達(dá)到清除不同種類文件的目的

            3.6 多目標(biāo)

            Makefile的規(guī)則中的目標(biāo)可以不止一個,其支持多目標(biāo),有可能我們的多個目標(biāo)同時依賴于一個文件,并且其生成的命令大體類似。于是我們就能把其合并起來。當(dāng)然,多個目標(biāo)的生成規(guī)則的執(zhí)行命令是同一個,這可能會可我們帶來麻煩,不過好在我們的可以使用一個自動化變量“$@”(關(guān)于自動化變量,將在后面講述),這個變量表示著目前規(guī)則中所有的目標(biāo)的集合,這樣說可能很抽象,還是看一個例子吧。

                bigoutput littleoutput : text.g
            generate text.g -$(subst output,,$@) > $@
            
                上述規(guī)則等價于:
            
            bigoutput : text.g generate text.g -big > bigoutput littleoutput : text.g generate text.g -little > littleoutput
            其中,-$(subst output,,$@)中的“$”表示執(zhí)行一個Makefile的函數(shù),函數(shù)名為subst,后面的為參數(shù)。關(guān)于函數(shù),將在后面講述。這里的這個函數(shù)是截取字符串的意思,“$@”表示目標(biāo)的集合,就像一個數(shù)組,“$@”依次取出目標(biāo),并執(zhí)于命令。

            3.7 靜態(tài)模式

            靜態(tài)模式可以更加容易地定義多目標(biāo)的規(guī)則,可以讓我們的規(guī)則變得更加的有彈性和靈活。我們還是先來看一下語法:

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

            targets定義了一系列的目標(biāo)文件,可以有通配符。是目標(biāo)的一個集合。

            target-parrtern是指明了targets的模式,也就是的目標(biāo)集模式。

            prereq-parrterns是目標(biāo)的依賴模式,它對target-parrtern形成的模式再進(jìn)行一次依賴目標(biāo)的定義。

            這樣描述這三個東西,可能還是沒有說清楚,還是舉個例子來說明一下吧。如果我們的<target-parrtern>定義成“%.o”,意思是我們的集合中都是以“.o”結(jié)尾的,而如果我們的<prereq-parrterns>定義成“%.c”,意思是對<target-parrtern>所形成的目標(biāo)集進(jìn)行二次定義,其計(jì)算方法是,取<target-parrtern>模式中的“%”(也就是去掉了[.o]這個結(jié)尾),并為其加上[.c]這個結(jié)尾,形成的新集合。

            所以,我們的“目標(biāo)模式”或是“依賴模式”中都應(yīng)該有“%”這個字符,如果你的文件名中有“%”那么你可以使用反斜杠“\”進(jìn)行轉(zhuǎn)義,來標(biāo)明真實(shí)的“%”字符。

            看一個例子:

                objects = foo.o bar.o
            all: $(objects)
            $(objects): %.o: %.c
            $(CC) -c $(CFLAGS) $< -o $@
            
            上面的例子中,指明了我們的目標(biāo)從$object中獲取,“%.o”表明要所有以“.o”結(jié)尾的目標(biāo),也就是“foo.o bar.o”,也就是變量$object集合的模式,而依賴模式“%.c”則取模式“%.o”的“%”,也就是“foo bar”,并為其加下“.c”的后綴,于是,我們的依賴目標(biāo)就是“foo.c bar.c”。而命令中的“$<”和“$@”則是自動化變量,“$<”表示所有的依賴目標(biāo)集(也就是“foo.c bar.c”),“$@”表示目標(biāo)集(也褪恰癴oo.o bar.o”)。于是,上面的規(guī)則展開后等價于下面的規(guī)則:
                foo.o : foo.c
            $(CC) -c $(CFLAGS) foo.c -o foo.o
            bar.o : bar.c
            $(CC) -c $(CFLAGS) bar.c -o bar.o
            
            試想,如果我們的“%.o”有幾百個,那種我們只要用這種很簡單的“靜態(tài)模式規(guī)則”就可以寫完一堆規(guī)則,實(shí)在是太有效率了。“靜態(tài)模式規(guī)則”的用法很靈活,如果用得好,那會一個很強(qiáng)大的功能。再看一個例子:
                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))表示調(diào)用Makefile的filter函數(shù),過濾“$filter”集,只要其中模式為“%.o”的內(nèi)容。其的它內(nèi)容,我就不用多說了吧。這個例字展示了Makefile中更大的彈性。

            3.8 自動生成依賴性

            在Makefile中,我們的依賴關(guān)系可能會需要包含一系列的頭文件,比如,如果我們的main.c中有一句“#include "defs.h"”,那么我們的依賴關(guān)系應(yīng)該是:

                main.o : main.c defs.h
            
            但是,如果是一個比較大型的工程,你必需清楚哪些C文件包含了哪些頭文件,并且,你在加入或刪除頭文件時,也需要小心地修改Makefile,這是一個很沒有維護(hù)性的工作。為了避免這種繁重而又容易出錯的事情,我們可以使用C/C++編譯的一個功能。大多數(shù)的C/C++編譯器都支持一個“-M”的選項(xiàng),即自動找尋源文件中包含的頭文件,并生成一個依賴關(guān)系。例如,如果我們執(zhí)行下面的命令:
                cc -M main.c
            
            其輸出是:
                main.o : main.c defs.h
            
            于是由編譯器自動生成的依賴關(guān)系,這樣一來,你就不必再手動書寫若干文件的依賴關(guān)系,而由編譯器自動生成了。需要提醒一句的是,如果你使用GNU的C/C++編譯器,你得用“-MM”參數(shù),不然,“-M”參數(shù)會把一些標(biāo)準(zhǔn)庫的頭文件也包含進(jìn)來。

            gcc -M main.c的輸出是:

                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的輸出則是:
                main.o: main.c defs.h
            
            那么,編譯器的這個功能如何與我們的Makefile聯(lián)系在一起呢。因?yàn)檫@樣一來,我們的Makefile也要根據(jù)這些源文件重新生成,讓Makefile自已依賴于源文件?這個功能并不現(xiàn)實(shí),不過我們可以有其它手段來迂回地實(shí)現(xiàn)這一功能。GNU組織建議把編譯器為每一個源文件的自動生成的依賴關(guān)系放到一個文件中,為每一個“name.c”的文件都生成一個“name.d”的Makefile文件,[.d]文件中就存放對應(yīng)[.c]文件的依賴關(guān)系。

            于是,我們可以寫出[.c]文件和[.d]文件的依賴關(guān)系,并讓make自動更新或自成[.d]文件,并把其包含在我們的主Makefile中,這樣,我們就可以自動化地生成每個文件的依賴關(guān)系了。

            這里,我們給出了一個模式規(guī)則來產(chǎn)生[.d]文件:

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

            這個規(guī)則的意思是,所有的[.d]文件依賴于[.c]文件,“rm -f $@”的意思是刪除所有的目標(biāo),也就是[.d]文件,第二行的意思是,為每個依賴文件“$<”,也就是[.c]文件生成依賴文件,“$@”表示模式“%.d”文件,如果有一個C文件是name.c,那么“%”就是“name”,“$$$$”意為一個隨機(jī)編號,第二行生成的文件有可能是“name.d.12345”,第三行使用sed命令做了一個替換,關(guān)于sed命令的用法請參看相關(guān)的使用文檔。第四行就是刪除臨時文件。

            總而言之,這個模式要做的事就是在編譯器生成的依賴關(guān)系中加入[.d]文件的依賴,即把依賴關(guān)系:

                main.o : main.c defs.h
            
            轉(zhuǎn)成:
                main.o main.d : main.c defs.h
            
            于是,我們的[.d]文件也會自動更新了,并會自動生成了,當(dāng)然,你還可以在這個[.d]文件中加入的不只是依賴關(guān)系,包括生成的命令也可一并加入,讓每個[.d]文件都包含一個完賴的規(guī)則。一旦我們完成這個工作,接下來,我們就要把這些自動生成的規(guī)則放進(jìn)我們的主Makefile中。我們可以使用Makefile的“include”命令,來引入別的Makefile文件(前面講過),例如:
                sources = foo.c bar.c
            include $(sources:.c=.d)
            
            上述語句中的“$(sources:.c=.d)”中的“.c=.d”的意思是做一個替換,把變量$(sources)所有[.c]的字串都替換成[.d],關(guān)于這個“替換”的內(nèi)容,在后面我會有更為詳細(xì)的講述。當(dāng)然,你得注意次序,因?yàn)閕nclude是按次來載入文件,最先載入的[.d]文件中的目標(biāo)會成為默認(rèn)目標(biāo)

            4 Makefile 書寫命令


            每條規(guī)則中的命令和操作系統(tǒng)Shell的命令行是一致的。make會一按順序一條一條的執(zhí)行命令,每條命令的開頭必須以[Tab]鍵開頭,除非,命令是緊跟在依賴規(guī)則后面的分號后的。在命令行之間中的空格或是空行會被忽略,但是如果該空格或空行是以Tab鍵開頭的,那么make會認(rèn)為其是一個空命令。

            我們在UNIX下可能會使用不同的Shell,但是make的命令默認(rèn)是被“/bin/sh”——UNIX的標(biāo)準(zhǔn)Shell解釋執(zhí)行的。除非你特別指定一個其它的Shell。Makefile中,“#”是注釋符,很像C/C++中的“//”,其后的本行字符都被注釋。

            4.1 顯示命令

            通常,make會把其要執(zhí)行的命令行在命令執(zhí)行前輸出到屏幕上。當(dāng)我們用“@”字符在命令行前,那么,這個命令將不被make顯示出來,最具代表性的例子是,我們用這個功能來像屏幕顯示一些信息。如:

                @echo 正在編譯XXX模塊......
            
            當(dāng)make執(zhí)行時,會輸出“正在編譯XXX模塊......”字串,但不會輸出命令,如果沒有“@”,那么,make將輸出:
                echo 正在編譯XXX模塊......
            正在編譯XXX模塊......
            
            如果make執(zhí)行時,帶入make參數(shù)“-n”或“--just-print”,那么其只是顯示命令,但不會執(zhí)行命令,這個功能很有利于我們調(diào)試我們的Makefile,看看我們書寫的命令是執(zhí)行起來是什么樣子的或是什么順序的。

            而make參數(shù)“-s”或“--slient”則是全面禁止命令的顯示。

            4.2 命令執(zhí)行

            當(dāng)依賴目標(biāo)新于目標(biāo)時,也就是當(dāng)規(guī)則的目標(biāo)需要被更新時,make會一條一條的執(zhí)行其后的命令。需要注意的是,如果你要讓上一條命令的結(jié)果應(yīng)用在下一條命令時,你應(yīng)該使用分號分隔這兩條命令。比如你的第一條命令是cd命令,你希望第二條命令得在cd之后的基礎(chǔ)上運(yùn)行,那么你就不能把這兩條命令寫在兩行上,而應(yīng)該把這兩條命令寫在一行上,用分號分隔。如:

                示例一:
            exec:
            cd /home/hchen
            pwd
            示例二:
            exec:
            cd /home/hchen; pwd
            

            當(dāng)我們執(zhí)行“make exec”時,第一個例子中的cd沒有作用,pwd會打印出當(dāng)前的Makefile目錄,而第二個例子中,cd就起作用了,pwd會打印出“/home/hchen”。

            make一般是使用環(huán)境變量SHELL中所定義的系統(tǒng)Shell來執(zhí)行命令,默認(rèn)情況下使用UNIX的標(biāo)準(zhǔn)Shell——/bin/sh來執(zhí)行命令。但在MS-DOS下有點(diǎn)特殊,因?yàn)镸S-DOS下沒有SHELL環(huán)境變量,當(dāng)然你也可以指定。如果你指定了UNIX風(fēng)格的目錄形式,首先,make會在SHELL所指定的路徑中找尋命令解釋器,如果找不到,其會在當(dāng)前盤符中的當(dāng)前目錄中尋找,如果再找不到,其會在PATH環(huán)境變量中所定義的所有路徑中尋找。MS-DOS中,如果你定義的命令解釋器沒有找到,其會給你的命令解釋器加上諸如“.exe”、“.com”、“.bat”、“.sh”等后綴。

            4.3 命令出錯

            每當(dāng)命令運(yùn)行完后,make會檢測每個命令的返回碼,如果命令返回成功,那么make會執(zhí)行下一條命令,當(dāng)規(guī)則中所有的命令成功返回后,這個規(guī)則就算是成功完成了。如果一個規(guī)則中的某個命令出錯了(命令退出碼非零),那么make就會終止執(zhí)行當(dāng)前規(guī)則,這將有可能終止所有規(guī)則的執(zhí)行。

            有些時候,命令的出錯并不表示就是錯誤的。例如mkdir命令,我們一定需要建立一個目錄,如果目錄不存在,那么mkdir就成功執(zhí)行,萬事大吉,如果目錄存在,那么就出錯了。我們之所以使用mkdir的意思就是一定要有這樣的一個目錄,于是我們就不希望mkdir出錯而終止規(guī)則的運(yùn)行。

            為了做到這一點(diǎn),忽略命令的出錯,我們可以在Makefile的命令行前加一個減號“-”(在Tab鍵之后),標(biāo)記為不管命令出不出錯都認(rèn)為是成功的。如:

               clean:
            -rm -f *.o
            
            還有一個全局的辦法是,給make加上“-i”或是“--ignore-errors”參數(shù),那么,Makefile中所有命令都會忽略錯誤。而如果一個規(guī)則是以“.IGNORE”作為目標(biāo)的,那么這個規(guī)則中的所有命令將會忽略錯誤。這些是不同級別的防止命令出錯的方法,你可以根據(jù)你的不同喜歡設(shè)置。

            還有一個要提一下的make的參數(shù)的是“-k”或是“--keep-going”,這個參數(shù)的意思是,如果某規(guī)則中的命令出錯了,那么就終目該規(guī)則的執(zhí)行,但繼續(xù)執(zhí)行其它規(guī)則。

            4.4 嵌套執(zhí)行make

            在一些大的工程中,我們會把我們不同模塊或是不同功能的源文件放在不同的目錄中,我們可以在每個目錄中都書寫一個該目錄的Makefile,這有利于讓我們的Makefile變得更加地簡潔,而不至于把所有的東西全部寫在一個Makefile中,這樣會很難維護(hù)我們的Makefile,這個技術(shù)對于我們模塊編譯和分段編譯有著非常大的好處。

            例如,我們有一個子目錄叫subdir,這個目錄下有個Makefile文件,來指明了這個目錄下文件的編譯規(guī)則。那么我們總控的Makefile可以這樣書寫:

                subsystem:
            cd subdir && $(MAKE)
            其等價于:
            subsystem:
            $(MAKE) -C subdir
            
            定義$(MAKE)宏變量的意思是,也許我們的make需要一些參數(shù),所以定義成一個變量比較利于維護(hù)。這兩個例子的意思都是先進(jìn)入“subdir”目錄,然后執(zhí)行make命令。

            我們把這個Makefile叫做“總控Makefile”,總控Makefile的變量可以傳遞到下級的Makefile中(如果你顯示的聲明),但是不會覆蓋下層的Makefile中所定義的變量,除非指定了“-e”參數(shù)。

            如果你要傳遞變量到下級Makefile中,那么你可以使用這樣的聲明:

            export <variable ...>
            如果你不想讓某些變量傳遞到下級Makefile中,那么你可以這樣聲明:
            unexport <variable ...>
            如:
            示例一:
            export variable = value
            其等價于:
            variable = value
            export variable
            其等價于:
            export variable := value
            其等價于:
            variable := value
            export variable
            示例二:
            export variable += value
            其等價于:
            variable += value
            export variable
            
            如果你要傳遞所有的變量,那么,只要一個export就行了。后面什么也不用跟,表示傳遞所有的變量。

            需要注意的是,有兩個變量,一個是SHELL,一個是MAKEFLAGS,這兩個變量不管你是否export,其總是要傳遞到下層Makefile中,特別是MAKEFILES變量,其中包含了make的參數(shù)信息,如果我們執(zhí)行“總控Makefile”時有make參數(shù)或是在上層Makefile中定義了這個變量,那么MAKEFILES變量將會是這些參數(shù),并會傳遞到下層Makefile中,這是一個系統(tǒng)級的環(huán)境變量。

            但是make命令中的有幾個參數(shù)并不往下傳遞,它們是“-C”,“-f”,“-h”“-o”和“-W”(有關(guān)Makefile參數(shù)的細(xì)節(jié)將在后面說明),如果你不想往下層傳遞參數(shù),那么,你可以這樣來:

            subsystem:
            cd subdir && $(MAKE) MAKEFLAGS=
            
            如果你定義了環(huán)境變量MAKEFLAGS,那么你得確信其中的選項(xiàng)是大家都會用到的,如果其中有“-t”,“-n”,和“-q”參數(shù),那么將會有讓你意想不到的結(jié)果,或許會讓你異常地恐慌。

            還有一個在“嵌套執(zhí)行”中比較有用的參數(shù),“-w”或是“--print-directory”會在make的過程中輸出一些信息,讓你看到目前的工作目錄。比如,如果我們的下級make目錄是“/home/hchen/gnu/make”,如果我們使用“make -w”來執(zhí)行,那么當(dāng)進(jìn)入該目錄時,我們會看到:

            make: Entering directory `/home/hchen/gnu/make'.
            
            而在完成下層make后離開目錄時,我們會看到:
            make: Leaving directory `/home/hchen/gnu/make'
            
            當(dāng)你使用“-C”參數(shù)來指定make下層Makefile時,“-w”會被自動打開的。如果參數(shù)中有“-s”(“--slient”)或是“--no-print-directory”,那么,“-w”總是失效的。

            4.5 定義命令包

            如果Makefile中出現(xiàn)一些相同命令序列,那么我們可以為這些相同的命令序列定義一個變量。定義這種命令序列的語法以“define”開始,以“endef”結(jié)束,如:

                define run-yacc
            yacc $(firstword $^)
            mv y.tab.c $@
            endef
            
            這里,“run-yacc”是這個命令包的名字,其不要和Makefile中的變量重名。在“define”和“endef”中的兩行就是命令序列。這個命令包中的第一個命令是運(yùn)行Yacc程序,因?yàn)閅acc程序總是生成“y.tab.c”的文件,所以第二行的命令就是把這個文件改改名字。還是把這個命令包放到一個示例中來看看吧。
                foo.c : foo.y
            $(run-yacc)
            
            我們可以看見,要使用這個命令包,我們就好像使用變量一樣。在這個命令包的使用中,命令包“run-yacc”中的“$^”就是“foo.y”,“$@”就是“foo.c”(有關(guān)這種以“$”開頭的特殊變量,我們會在后面介紹),make在執(zhí)行命令包時,命令包中的每個命令會被依次獨(dú)立執(zhí)行。
            posted @ 2009-08-03 14:29 chaosuper 閱讀(140) | 評論 (0)編輯 收藏

            僅列出標(biāo)題
            共12頁: First 4 5 6 7 8 9 10 11 12 
            99久久国产综合精品网成人影院| 国产精品一区二区久久| 国产叼嘿久久精品久久| 久久水蜜桃亚洲av无码精品麻豆| 99久久无色码中文字幕人妻| 精品伊人久久久| 久久亚洲AV无码精品色午夜麻豆| 久久久WWW免费人成精品| 日本欧美国产精品第一页久久| 久久精品国产99久久香蕉| 久久夜色精品国产亚洲av| 亚洲精品综合久久| 国产69精品久久久久9999APGF| 奇米综合四色77777久久| .精品久久久麻豆国产精品| 伊人色综合久久| 国产精品成人久久久| 精品久久无码中文字幕| 嫩草影院久久国产精品| 狠狠综合久久综合中文88| 亚洲伊人久久成综合人影院| 亚洲精品乱码久久久久久中文字幕 | 2021国内久久精品| 日韩乱码人妻无码中文字幕久久| 久久不射电影网| 久久只有这精品99| 久久精品国产一区二区三区日韩| 一本一本久久a久久精品综合麻豆| 亚洲精品乱码久久久久久| 国产女人aaa级久久久级| 7777精品伊人久久久大香线蕉| 久久精品国产99国产精品澳门| 欧美精品一区二区精品久久| 久久午夜无码鲁丝片午夜精品| 欧洲人妻丰满av无码久久不卡| 久久精品国产亚洲Aⅴ香蕉| 久久久免费精品re6| 久久婷婷人人澡人人| www.久久热.com| 色综合久久无码五十路人妻| 久久精品二区|