這篇是從別人的部落格剪來轉貼的, 花了一點時間校對及重新編排版面.

雖然原 PO 文有點舊了 (2000年), 現在 (2015年) 原 PO 文網址已經連不到了, 還有一部份參數已經不存在了, 但是當新手面對現今的 GCC 參數和選項動則數以百計, 還有厚厚一疊手冊時, 不失為一個不錯的快速入門.

註: 原 PO 文二篇, 本篇為第二篇



來源 http://mis.im.tku.edu.tw/~zbwei12b/program/gcc-2.html

標題:編織出個好程式--GCC參數篇 (二)

作者:陳信宏(Ivor Chen)

E-mail add: civor@ksts.seed.net.tw or
            Ivor.bbs@bbs.ccu.edu.tw

地址:嘉義市西門街 80 號

電話:(05)2253215

上一期所介紹的參數大體上和整個編譯過程關,如整體參數、方言參數和除錯參數等,本期將要更進一層的介紹許多控制細部動作的參數。

最佳化參數


最佳化一個程式的演算法有很多種:從合理的調整程式結構流程到適當的安排變數型態等不一而足,且除了程式方面的最佳化之外,還可配合主機硬體優勢和 OS 過人之處來加以運用,但不管如何,最佳化的目標仍是唯一的 -- 更快、更小、以及能配合所在環境的更好。

  • -O
  • -O1
    初步最佳化,作最佳化的編譯當然會花費更多的時間和記憶體;未使用本參數前 GCC 編譯的原則是減少編譯成本,而且各個階段的編連是各自獨立的 -- 可以在任一階段停下來,重新指定變數內容,且各階段的結果和一般的狀況相同,完全符合正常的程序,然而本參數會以整體四個階段一起考慮,且本參數包含許多其它參數,目的就是要減少程式大小及執行時間;使用本參數同時會將 -fthread-jumps-fdelayed-branch 開啟。
  • -O2
    更進一步最佳化,本參數可直接使用不需配合上一個參數,在本參數下除了「大小 -- 速度」需犧牲一方的演算法外其於都會用來作最佳化處理,也就是除了 frame pointer elimination 和 loop unrolling 外其餘將會利用上;和上一個參數比起來本參數在編連時間和執行碼效能都更優越。
  • -O0
    不作最佳化;然而若是之前有指定其它 LEVEL 的參數,將不會受到本參數的影響。

接下來是許多和 CPU 構造有關的參數,它們的型態為 -fFLAG,大部份有相反詞,而相反詞為 FLAG 之前加上 no- 所形成。

  • -ffloat-store
    "不要" 將浮點變數儲存在暫存器中,而且抑制其它會改變從暫存器或記憶體所得浮點數之值的參數功能;本參數避免了一個現象,即浮點數的值無緣無故的加了好幾位小數出來,本來若將變數內容放在暫存器中,會比在記憶體中快上許多,這也是一般最佳化的方法之一,然而對許多主機例如 68000 來說其浮點暫存器所能容納的長度比 double 還長,這對一般程式來說無可厚非,然而對數值計算程式或是利 IEEE 浮點數的程式而言卻是 "窩裡反" 的行為,本參數就是避免這種情況發生。
  • -fno-default-inline
    inline 這個關鍵字的作用是在呼叫此類函數的地方有著這個函數的程式碼存在,如此雖然程式變大了但執行速度也加快許多,當使用 -O 時物件中定義在 class 區域中的每一個成員函數都是以 inline 的型態處理,而本參數便是限定不要將這些成員函數視為 inline
  • -fno-defer-pop
    函數呼叫時其間傳入值和傳回值的傳遞是靠堆疊來完成的,一般情況下編譯程式會在堆疊之中累積一些 argument 後再一起 pop 出去,這是為了速率著想,而本參數的作用便是不要作堆積,進一個便彈一個出去。
  • -fforce-mem
    強制 memory operands 在作運算前先 copy 到暫存器中以加快運算速度。
  • -fforce-addr
    強制 memory address 之內容在作運算前先 copy 到暫存器中以加快運算速度。本參數對於程式整體效率的提升比 -fforce-mem 還好,但這兩個參數並不適合任一機種。
  • -fomit-frame-pointer
    當函數用不到 frame pointer 時將它從暫存器中清除,本參數同時抑制了任何儲存、設定或回存 frame pointer 的動作,這將使得更多的函數可利用暫存器以提升執行效率。
  • -fno-inline
    忽略關鍵字 inline 的作用。
  • -finline-functions
    整合所有「簡單」的函數到呼叫它們的函數內,而「簡單」的標準由 GCC 自行判斷;假如一個函數被整合到其它函數中,且其型態為 static,此函數當然不會有自己本身的輸出。
  • -fkeep-inline-functions
    本參數要和上一參數對照;縱然一 static 函數被整合到其它函數內,仍輸出執行時期可供呼叫的 version。
  • -fno-function-cse
    不要將函數所在位址放在暫存器中,這將使得每個呼叫函數的指令包含了函數位址,這會產生較沒效率的程式碼,但對於某些會改變 assembler 輸出的 hacks 來說,最佳化的過程會擾亂程式的行為,那時便需使用本參數。
  • -ffast-math
    本參數允許 GCC 違反某些 ANSI 或 IEEE 規則來作運算,這將使得執行速度加快,但對於運算程式等需依賴數值精密度作計算者則本參數不應和任何 -O 參數共同使用,因為很有可能產生錯誤結果。

前置處理器參數


直觀來說前置處理器的作用為處理程式之中以 # 開頭的命令,這些命令於常見的有標頭檔的引入和定義常數及巨集等,可以說是在編譯之前的準備動作,有時也可用依某常數定義與否來決定那些程式片段是否要編譯,這對移植的工作有很大的助益,例如同樣要作到印出檔案名稱,在不同的 OS 和檔案格式上所得的結果就有蠻大的差異,在此就可分別寫不同的處理碼,然後再利用 #ifdef 等命令適機編連,不僅節省程式碼,還能加快速度;以下的參數,便是和前置處理時期有關者,而且許多參數需和 -E 配合使用。

  • -include FILE
    在編譯目標程式之前先編譯 FILE,而且 FILE 並無只能為標頭檔的限定,若是和 -D-U 合併使用時以本參數為優先處理,當參數列中不只一個本參數及 -imacros 時依其順序執行。
  • -imacros FILE
    本參數和上一個參數幾乎相同,同樣都是在編譯目標程式之前先編譯 FILE,所不同的是本參數會捨棄 FILE 的輸出,這使得 FILE 中唯有放置巨集定義才有作用。
  • -idirafter DIR
    將 DIR 內的路徑加至第二包含路徑中,當在主要包含路徑中找不到標頭檔時便可到第二來尋找,而主要包含路徑便是由 -I 所指定。
  • -iprefix PREFIX
    設定 PREFIX 之內容為 -iwithprefix 中之 prefix 預設值。
  • -iwithprefix DIR
    增加一個路徑到第二包含路徑中去,而這個路徑由 DIR 和 -iprefix 中的 PREFIX 之內容聯合組成。
  • -iwithprefixbefore DIR
    增加一個路徑到主要包含路徑中去,而這個路徑由 DIR 和 -iprefix 中的 PREFIX 之內容聯合組成。
  • -nostdinc
    不要在標準系統目錄下尋找標頭檔,只在目前目錄和 -I 所指定的目錄下尋找,因此這對於自行建立程式庫有幫助,有關目錄的所有選項請看稍後的「搜尋參數」。
  • -undef
    不要預先定義任何非標準的巨集。
  • -E
    本參數在「整體參數」有提到過,作用為令 GCC 完成前置處理後便停止。
  • -C
    命令前置處理器不要捨棄註解部份,本參數需和 -E 共用。
  • -P
    命令前置處理器不要產生 #line 命令,本參數需和 -E 共用。
  • -M
    命令前置處理器輸出給 make 程式用的規則,內容為每個目的檔的依存規則,對於每個 source,前置處理器會將其 target 定為目的檔 (object),而其中所使用到的標頭檔便成為依存檔,有關本參數的最佳參考為 ~/linux 下 Makefile 中 depend 這項依存規則的內容;另外一個如本參數功能的方法為設定環境變數 "DEPENDENICS_OUTPUT"。
  • -MM
    本參數唯一和 -M 不同之處在於輸出的規則中唯有使用者自定標頭檔 (#include "FILE" 部份) 才會列入,而系統標頭檔 (#include <FILE> 部份) 並不會列入。
  • -MD
    作用如同 -M 一樣,然而在本參數會將規則輸出到和 .o 檔同檔名的 .d 檔中 (-M 要用重新導向的方法),而 Mach utility 中的 "md" 可將各別的 .d 檔集合成可供 make 使用的依存檔。
  • -MMD
    -MM-MD 功能的交集。
  • -H
    印出所使用的標頭檔。
  • -DMARCO
    定義巨集 MARCO 為字串 "1"。
  • -DMARCO=DEFN
    定義巨集 MARCO 為 DEFN;此時所有的 -D 參數將比 -U 參數優先執行。
  • -UMARCO
    取消 MARCO 的定義;此時 -U 還是比 -D 後執行,但卻在 -include-imarcos 之前。
  • -dM
    命令前置處理器在前置處理完畢後列出所有有效的巨集名稱,本參數需和 -E 共用。
  • -dD
    命令前置處理器輸出所有的巨集定義內容。
  • -dN
    命令前置處理器輸出所有內定之外的巨集定義。
  • -trigraphs
    支援 ANSI C trigraphs,本參數包含於 -ansi

傳遞參數至組譯程式 (Assembler)


  • -Wa,OPTION
    將 OPTION 的內容傳給組譯程式,其內容是由參數們所組成,不同的參數間以逗號 (,) 隔開。

連結參數


當原始程式編譯或組譯完成候,便完成了基本架構,如條件判斷或是迴圈等已正確的轉換成機器語言型式,這便是 .obj 的內容,在不同的主機和 OS 上 .obj 的格式也不相同,在程式中通常都會呼叫一些函數,這些函數的可執行碼便放在程式庫中,需要把它們加進來才算是完整的可執行檔,而這個過程便稱為連結 (link),本區所要介紹的為控制連結過程的各項參數。

  • -OBJECT-FILE-NAME
    連結之時會以 .o 檔為模版造出最終可執行檔,這個過程是以 .o 的字尾來判定是否為 Object 檔,若是當 Object 檔字尾不是 .o 時,便可使用本參數,而 OBJECT-FILE-NAME 的內容是為非標準 Object 檔的檔名。
  • -lLIBRARY
    連結時尋找 LIBRARY.a 程式庫作為程式庫來源,本參數所指的程式庫為附加方式,即標準程式庫仍會用來作連結,不管本參數有設定,而相關的目錄路徑則為標準路徑加上 -L 所指定的路徑;如果參數列中有兩個以上的目的檔,本參數的位置有很大的影響,如在 "foo.o -lz bar.o" 中 -lz 只對 foo.o 有效,即 bar.o 無法連結到 z.a 中的特定函數。
  • -lobjc
    若是編譯 Objective C 程式則需以本參數來連結專用程式庫。
  • -nostartfiles
    編譯完成的可執行檔在啟動時都會經由標準系統啟動程式 (standard system startup file) 作啟動的動作,這個檔案依 OS 的不同而有不同的功能,本參數便是命令連結程式不要將啟動程式連入執行檔中。
  • -nostdlib
    當連結時不要使用標準程式庫和啟動程式,但由 -l 指定者不在此限。
  • -static
    對於提供動態連結 (dynamic linking) 的系統,本參數抑制與共享程式庫作連結 (也就是說 GCC 的內定為動態連結)。
  • -Xlinker OPTION
    在 GCC 中內定的連結程式為 ld,而且 GCC 能以內部的方式將參數傳給 ld,然而在某些系統下能在 Makefile 中改變 LD 變數的內容來指定特別的連結程式,此時控制參數便需本參數來作傳遞,此時 GCC 會將 OPTION 的內容傳給特定連結程式,如果傳遞的參數有附加選項,則需本參數兩個才能成功,如欲傳遞 '-assert definitions' 時,則需寫成 -Xlinker -assert -Xlinker definitions 才行。
  • -Wl,OPTION
    對於 GCC 能辨認的連結程式便可用本參數來傳遞控制值,在 OPTION 中若有一個以上的參數,其間使用逗號 (,) 隔開。
  • -u SYMBOL
    假裝符號 SYMBOL 未曾定義,使得程式庫模組可定義同一符號。

搜尋參數


本系列參數定義有關標頭檔、程式庫和部份編譯程式的搜尋路徑。

  • -IDIR
    增添 DIR 中的路徑到標頭檔搜尋路徑之中。
  • -I-
    任何在本參數之前的 -I 參數,其所設立的路徑將只會用在對 #include "FILE" 的搜尋,對於搜尋 #include <FILE> 並無影響,然而在本參數之後的 -I,其路徑對所有的 #include 皆有作用,因此本參數還會使得目前目錄不是第一個搜尋 "FILE" 的路徑;由於本參數並不會影響對標準系統目錄的標頭檔搜尋,因此和 -nostdinc 各自獨立。
  • -LDIR
    增添 DIR 到由 -l 所指定程式庫的搜尋路徑去。
  • -BPREFIX
    本參數主要作用為指定如何去尋找和編譯程式有關的可執行檔、程式庫及資料檔等;編譯時其實是由四個子程式所共同完成 -- cpp、cc1、as 和 ld,這四個程式在 gcc 的統籌驅動下完成編連的四個步驟,在沒指定本參數的情況下,gcc 會先在 /usr/lib/gcc//usr/local/lib/gcc-lib/ 下尋找相關檔案,若未找到再依 PATH 所指去尋找,若是使用本參數,則 PREFIX 中所指路徑將是第一個尋找的目標。

警告參數


警告是在編譯的過程中所發出的建議訊息,警告並非是錯誤,編譯後的程式仍可執行,不過警告的目的為指出某段可能會出錯的宣告方式語法,因此不可因不影響程式執行而掉以輕心;警告參數的目的為對某種語法提出(或抑制)警告訊息,以方便除錯或是順利執行,通常是以 -W 為開頭,這些參數的相反詞為加 no- 所形成,而不管是那種類型,都不是 GCC 的內定值。

  • -fsyntax-only
    只檢查程式之語法有無錯誤而不作編譯。
  • -w
    抑制所有警告訊息。
  • -Wno-import
    抑制所有和使用 #import 有關之警告訊息。
  • -pedantic
    依嚴格的 ANSI C 的標準發出警告訊息,並駁回任使用不允許擴充函數的程式。
  • -pedantic-errors
    作用如同 -pedantic,但除了警告外還會產生錯誤訊息。
  • -W
    對下列情況提出警告:
    • 一個 non-volatile 自動變數將被改成 longjmp 時,這種情況唯有作最佳化時才會發生。
    • 當一個函數可能有傳回值,也可能不傳回任何值時,例子如下:
      foo (a) {
        if (a > 0)
          return a;
      }
    • 沒任何作用的 expression 敘述。
    • 一個 unsigned 數值和零比較大小時。
    • 如 'x<=y<=z' 的表示式,此表示式 GCC 會將其處理成 '(x<=y?1:0)<=z' 以便程式能順利執行,當然這樣和原來想表現數學式意思差蠻多的。
    • 當 storage-class specifiers 如 'static' 並不是所有宣告中最先執行之時。
  • -Wimplicit
    當某函數或某參數 implicitly 宣告時提出警告。
  • -Wreturn-type
    當一函數內定傳回值為 int 時卻作別種型態的宣告便提出警告,或是當一函數無傳回值 (void),然而其中的 return 接一傳回值時便發出對 return 的警告。
  • -Wunused
    對以下三種情況提出警告:
    • 某一 local 變數自宣告後便沒再使用
    • declare 一 static 函數後但未先 define
    • 一段敘述算出一個結果,這個結果接下來卻未再使用
  • -Wswitch
    使用一列舉 (enum) 形態的變數為 switch 的 index 時,接下來卻缺少一個或以上的 case 作配合,或者是 case 的 label 超出了 enum 中的選項時,便發出警告訊息,若是沒有必要列出每個 case 時可用 default 代替。
  • -Wcomment
    當 '/*' 符號 "又" 出現在註解中時。
  • -Wformat
    檢查 printfscanf...等函數其中第二個以後的參數型態是否和第一個字串中所寫有符合。
  • -Wchar-subscripts
    當陣列的註標 (subscript) 之型態為 char 時便發出警告,因為在某些主機上 charsigned
  • -Wuninitialized
    當一自動變數未給予初值便使用時;如下的例子
      { int x;
        switch (y) {
          case 1: x = 1;
            break;
          case 2: x = 4;
            break;
          case 3: x = 5;
        }
        foo (x);
      }
    這個例子看起來沒錯,因為只要 y 為 1、 2 或 3 則 x 都會有初值,但 GCC 對於這樣的寫法並不夠聰明到能看出 x 會有初值這件事,因此對 foo(x) 而言其中的 x 就出問題了,然而當未使用最佳化編譯時並不會有本警告出現。
  • -Wparentheses
    括號 parentheses 在以下這些敘述中省略時:
    • 當預期將為真假值運算 true (truth value)出現指定式有 assignment 時,
    • 或是有運算元一個串一個但其優先次序令人頭昏眼花的 nest operators 時。
  • -Wenum-clash
    當兩個不同的 enum 型態作轉換時。(C++ only)
  • -Wtemplate-debugging
    若是在程式中使用了模版 (template) 但除錯功能未完全支援時。(C++ only)
  • -Wall
    本參數為以上所有 -W 參數的總集,且本參數為最推薦使用者,因為以上的情況便包含了一般情況下該警告的事項。

接下來的這些參數並不包含於 -Wall 之中,這些參數所要警告的為一些結構上的問題。

  • -Wtraditional
    對傳統和 ANSI C 的一些結構上的差異提出警告:
    • switch 的 operand 型態為 long
    • 函數在 block 中宣告為外部 (external) 而後在 block 結束後使用。
    • 巨集的參數是位於巨集本體的字串內容中,這種情況對傳統 C 是允許的,但對 ANSI C 會有不相容發生。
  • -Wshadow
    當一區域變數 shadow 另一區域變數時。
  • -Wid-clash-LEN
    當兩個以上的辨別字 (identifier) 在開頭和 LEN 相同時。
  • -Wpointer-arith
    當使用 sizeof 在函數時。
  • -Werror
    將所有警告轉換成錯誤,這會讓編連停止。

後言


對於經常使用 C/C++的使用者來說,不管是用來寫商業程式或是交學校的作業,GCC 是一套很不錯的選擇,由介紹了兩期仍無法完全含蓋的參數部份,就可知它的能力強撼,然而它缺少一個友善的環境(如 TC 或是 MSC 中的 IDE),不免令人卻步,不過利用 make 和 Makefile 的統一管理,一切就變得清楚許多;下次若有機會,再來討論一些安裝 GCC 的方式和其它相關設定。

修改日期: 02/08/2000 14:00:28

    文章標籤

    C 語言 C GNU GCC gcc

    全站熱搜

    MagicJackTing 發表在 痞客邦 留言(0) 人氣()