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

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

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



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

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

作者:陳信宏(Ivor Chen)

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

地址:嘉義市西門街 80 號

電話:(05)2253215

GNU C/C++ 是由自由軟體協會 (FSF; Free Software Foundation) 所發展和推出的一套功能完備的 C/C++ 語言編譯器和程式庫,C/C++ 語言以其類似高階語言的語法和接近機械語言的執行速度,成為軟體發展的重要選擇之一; C/C++ 語言的可攜帶性能力本來就很高,這使得發展完成的程式只需修改小部份或是根本不需修改,就能輕易的移植到不同的作業系統上;雖然 C/C++ 本身有這些先天上的優點,但更重要的是需要有個良好的編譯程式,才能將可執行檔編得更快、編得更小,而且若是在不同的作業系統上都有一套相同的編譯器,那對於程式設計人員有著莫大的幫助和方便,因為不會有在那個 OS 下要用那個編譯程式的困擾;多年來,GNU C/C++ 一直盡職的扮演這個角色。

如同 Linux 一般,GNU CC 非由一人或一公司所撰寫,除了 Richard Stallman 先生外,全世界的使用者也供獻了許多心力,有人加入新的關念和技巧,有人改善舊有的結構,更有人把她移植到不同的機型和作業系統上,在夜以繼日時時刻刻的改進之下,GNU CC 彙集了各方的優點加以融合,並朝未來進步之中;在 Linux 中就是以她作為發展工具,在重編核心之時便是她擔任重責大任,而且在 DOS 中也有 GNU CC -- DJGPP。然而她並未提供如 Turbo C 的整合發展環境,想要認識她的使用者難免會有吃力的感覺,面對如此的仰之彌高,最直接的方法便是從控制功能的參數認識起,如此一來不但不會鑽之彌堅,而且還能漸入佳境,因此我們就先從參數談起。

GNU C/C++ 的一些相關資料


gcc 是 GNU C/C++ 的 C 語言編譯程式的名稱,在接下來的文章之中,筆者將以大寫的 GCC 來代表 GNU C/C++,而以小寫的 gcc 來代表編譯程式,gcc 的語法如下:
gcc [option|filemane]...
其中 filemane 是將要編譯的檔名,option 是將要認識的參數部份。

當要編譯 C++ 語言時,g++ 為使用的編譯程式,在此 g++ 是一個完全的編譯器 -- 而不只是個前置處理器,直接產生物件碼,這樣的 C++ 執行起來才更有效率,其語法如下:
g++ [option|filemane]...
其中參數部份除非有指明是專屬於 gcc 或是 g++ 外,其餘兩者皆可使用。

在一般的情況下,GCC 的編譯過程有下列四個步驟:preprocessing,compilation,assembly 和 linking,而整體參數 (overall options) 的作用是指定在那一階段停止,其它類型的參數主要是調整這四個步驟中某一步驟的動作。

由於許多參數之後都允許加上其它控制的子參數,因此對於參數間的合併使用有較嚴格的限定 -- 兩個參數不可併成一個,如 -dr 這個參數便和 -d -r 在作用上有著極大的不同。對於不同類型的參數,在排列上不需依任何順序。而相同類型的參數,也沒有只能使用一個的限制。對於某些參數如 -f 或是 -W ,其後的子參數可能是一個字串,如 -fforce-mem 或是 -Wformat,這類型的組合大部份都有 positive 和 negative 兩種型態,如 -ffoo 的 negative 型為 -fno-foo,在接下來的討論之中,若是這兩種型態皆存在者,筆者會一同列出。

整體參數 (Overall Options)


整體參數的目的在於控制編譯的進行,即編譯到那個階段便停止,因此我們先來看看各種字尾和檔案間的關係:

FILE.c 需作前置處理 (preprocess) 的 C 語言原始程式
FILE.i 不需作前置處理的 C 語言原始程式
FILE.m Objective-C 的原始程式,且必需和程式庫 'libobjc.a' 連結才能執行
FILE.h C 語言標頭檔 (header file)
FILE.cc
FILE.cxx
FILE.C
需作前置處理的 C++ 語言原始程式
FILE.ii 不需作前置處理的 C++ 語言原始程式
FILE.s 組合語言檔
FILE.S 需作前置處理的組合語言檔

接下來我們來看整體參數:

  • -x LANGUAGE
    GCC 會以檔案字尾來判定是何種語言來作編譯,而本參數的作用為指定後續的檔案之語言種類,不管其字尾為何,LANGUAGE 的可能內容為
    • c
    • objective-c
    • c++
    • c-header
    • cpp-output
    • c++-cpp-output
    • assembler
    • assembler-with-cpp
    這些指定的語言會一直作用到下一個 -x 參數
  • -x none
    不指定任何語言,以輸入檔案的字尾為準
    當只希望作某階段的編譯時,用 -x 可指定編譯的起點,下述的參數可以指定編譯終點:
  • -E
    此參數指定只作完 perprocess 便停止,不作接下來 compiler 等動作,並輸出到標準輸出 (stdout) 去,對於不需前置處理的檔案本參數無效。
  • -S
    只進行到 compiler 便停止,因此輸出為一組合語言檔,在內定的輸出檔名為輸入檔名去除 .c.i 等字尾之後加上 .s 而成。
  • -c
    只 compile 或 assemble 輸入檔案,而不作連結,因此輸出為一目的檔 (object file), 內定的檔名為去除 .c.i 或是 .s 等之後加上 .o 而成。
  • -o FILE
    不管在那一階段結束動作,都可用此參數將內定的輸出檔名改成 "FILE";若是沒用此參數,最後產生的可執行檔檔名內定為 "a.out",在各階段結束的輸出內定檔名請看上列參數。
  • -v
    將正在執行的編譯階段及編連程式的版本輸出至標準錯誤輸出 (standard error output)。
  • -pipe
  • 在不同階段之編譯時,使用管線 (pipe) 取代暫存檔,在某些系統上的組譯程式 (assembler) 由於不能由管線讀取資料,而使得這參數無效,但對 GNU assembler 而言此參數當然可用。

和 C 之方言 (C Dialect) 有關的參數


所謂的「方言」或是「標準語言」在此是以制定的標準而言,因此 ANSI C 便是「標準語言」,而相對的較舊 (在制定之前的 C) 或是較新者 (如 C++) 便成了「方言」,這樣的分類是因為依實際的情況而定,沒半點優劣的意思。

對於 C++ 語言而言,雖然 gcc 能以檔名字尾為判斷而作正確的編連,但有時仍需指定物件程式庫 libg++ 為連結所需程式庫,因此如果要編連 C++ 語言,建議使用 g++ (某些系統上為 c++) 來作,因為 g++ 會將語言內定為 C++、將 libg++ 內定為標準程式庫,而且對於字尾並不符合標準字尾的檔案也能作編連。

接下來我們來看方言參數:

  • -ansi
    指定為 ANSI C 語言,本參數會將不符合 ANSI C 標準的字視為語法錯誤,因此本參數會關閉 GCC 中許多有用的關鍵字,如 asminlinetypeof,以及用來辨認系統的巨集名稱如 unix 和 vax,而且 $ 將不可用來作為識別字的一部份;__asm____extension____inline____typeof__ 這四個字不管有無本參數都可繼續使用,不過它們通常是放在標頭檔中,而 __unix____vax__ 也是相同情況;當指定本參數時,allocaabortexit_exitffs 將不會視為內建函數。
    當設定本參數時,將會定義 __STRICT_ANCI__ 這個巨集,許多標頭檔利用這個巨集定義與否來宣告相關的其它巨集或函數,這個目的只是為了符合 ANSI 的標準。
    對於非 ANSI 的程式,本參數並不會使編譯終結或發出任何警告,因此通常需加入 -pedantic,有關這個參數將在警告參數中說明。
  • -fno-asm
    不將 asminlinetypeof 識為保留字,因此這些字可當成識別字 (identifiers), 如欲達到這些功能,可用 __asm__, __inline____typeof__ 來代替;而 -ansi 包含本參數的功能。
  • -fno-builtin
    對於某些不是以雙底線 (__) 開頭的函數,不視為內建函數,這些函數為 -- abort, abs, alloca, cos, exit, fabs, ffs, labs, memcmp, memcpy, sin, sqrt, strcmp, strcpystrlen。通常 GCC 對這些函數有特定的處理方式 -- 利用一段小而且更有效率的碼來取代,例如呼叫 alloca 通常會變成直接調整堆疊的單一指令,而 memcpy 則是一 inline 的複製迴圈,這使得程式變小又變快,由於程式碼已經改變,因此無法對這類函數定除錯中斷點,而且也不能以連結別的程式庫的方式來改變這些函數的行為。
  • -trigraphs
    支援 ANSI C trigraphs,本參數包含在 -ansi 中.
  • -traditional
    會使 GCC 以「傳統」的方式來編譯程式,所謂傳統是指在 ANSI C 之前,其方式如下:
    • 所有在函數內宣告的 extern 變數具有整體作用
    • 新的保留字 typeof, inline, signed, constvolatile 不具作用
    • 允許指標和整數作比較
    • unsigned shortunsigned char 皆轉換成 unsigned int
    • 不認定超出範圍的浮點數指標為錯誤
    • 對於 constants 型態的字串,會存放在可寫入的區域,可在程式中更改其內容
    • 不再定義 __STDC__,然而 __GNUC__ 仍將定義,這使得在標頭檔中能以這兩個巨集名稱存在與否來分辨是用 GNU C、traditional GNU C、other ANSI C 或是 other old C 編譯程式來作編譯
  • -traditional-cpp
    以傳統的 C 前置處理器方式來編譯程式。
    • 以 newline 字元為一字串的結束記號。
    • 將 '\x' 和 '\a' 兩 escape sequences 轉換成 'x' 和 'a' 字元。
    • 允許 this 在 C++ 程式中使用。
    以上這三項方式包含在 -traditional 參數中。
  • -fcond-mismatch
    允許 conditional expressions 之第二和第三個參數為 mismatched types。
  • -funsigned-char
  • -fno-signed-char
    char 設為 unsigned;不同的主機和系統對於 char 的基本設定並不相同,有些為 signed、其它則相反,程式中的 char 變數會因不同系統而有差異,本參數主要目的便是作強制設定;本參數之同義詞為 -fno-signed-char
  • -fsigned-char
  • -fno-unsigned-char
    char 設為 signed
  • -fsigned-bitfields
  • -funsigned-bitfields
  • -fno-signed-bitfields
  • -fno-unsigned-bitfields
    這四個參數主要目的為設定 bitfields 之符號值,在內定上 bitfields 是為 signed -- 這可從基本資料如 int 是為 signed 看出;在 -traditional 下所有的 bitfields 全為 unsigned
  • -fwritable-strings
    對於 constants 型態的字串,會存放在可寫入之區域,可在程式中更改其內容; -traditional 包含本項功能。
  • -fallow-single-precision
    就算是使用 -traditional 的情況下,也不要將單精密度的運算子強製成雙精密度;傳統的 K&R 編譯程式會不論運算子之型態而將運算式改成雙精密度,然而編譯時單精密度比雙精密度快多了,因此若是需用 -traditional 又不需要 double 的運算,便可用本參數;對於 ANSI C 和 GNU C 而言本參數無效。

C++ 方言參數


以下是用來控制編譯 C++ 時的一些參數,雖然這些參數只對 C++ 有作用,然而若是使用於其它語言,將會被忽略而不會產生任何錯誤。

  • -fall-virtual
    除了物件建構者 (constructor) 和 newdelete 外,將其它出現的成員函數 (member functions) 視為 virtual 函數;這並非意謂每次都是透過內部的 virtual 函數表來呼叫這些成員函數,而是若 compiler 判斷能直接呼叫 virtual 時,這些成員函數同樣也是直接呼叫。
  • -fdollars-in-identifiers
    允許 $ 這個符號在識別字串 (identifiers) 中使用,傳統的 C 允許 $ 在識別字串中,但 ANSI C 和 C++ 禁止此種用法;本參數有相反詞。
  • -felide-constructors
    在允許的情況下省略建構函數,在下列的程式片段中本參數會使得呼叫 foo() 時直接初始化 y:
        A foo();
        A y = foo();
    未使用本參數時 GCC 之過程如下
    • (一) 呼叫物件 A 的建構函數來初始化 y
    • (二) 將 foo() 的結果存到暫存區
    • (三) 將暫存區的值代換給 y;
    這個過程是 ANSI C++ 的標準。
  • -fenum-int-equiv
    允許 int 型態完全的轉換至 enum
  • -fexternal-templates
    對於 template 函數將只在其定義的所在產生一份 copy,因而對 template 宣告能獲得較小的程式碼,然而要達到這個效果,除了本參數外,還要定義 #pragma implementation 或是 #progma inter-face;本參數會將所有的 template 視為外部函數 (external),因此可用 typedef 來安排在在 im-plementation file 中出現的 instantiations;對於本身並無 template 的 source,無需顧慮相關程式是否有 template,皆不需本參數作編譯,這 樣只是會加大物件程式碼的大小而以;本參數亦有加上 no- 之相反詞。
  • -fmemoize-lookups
  • -fsave-memoized
    利用 heuristics 方法來加速編譯,然而本方法未成為內定的編譯方式是因為本法只能針對一個輸入檔案,對其它檔案之速度反而變慢。
  • -fno-strict-prototype
    在 C++ 中對函數的雛型宣告 (prototype declare) 有嚴格的規定,例如 "int foo()" 之宣告在 C 中是可以的,它代表 foo() 並無輸入值,但在 C++ 中得要宣告成 "int foo(void)",本參數便是允許在 C++ 中作 C 般的宣告。
  • -fnonnull-objects
    設定 references 之物件為非 null 型態。
  • -fthis-is-varible
    允許程式中使用 this,這是為了和過去的程式相容的參數。
  • -nostdinc++
    不要在內定的 C++ 標準目錄下搜尋標頭檔,然而其它標準目錄還是依舊會尋找,本參數通常用於建立 C++ 程式庫 libg++ 時。
  • -traditional
    本參數對 C++ 的作用如同 -fthis-is-varible 參數 (對 C 和 C++ 的共同影響請參閱「C的方言參數」中之 -traditional 項)。

除上所述之外,專對 C++ 之最佳化、警告和程式碼生成參數也一併在下討論:

  • -fno-default-inline
    在 class 中忽略 inline 之作用。
  • -Wenum-clash
  • -Woverloased-virtual
  • -Wtemplate-debugging
    對於這些情況,只對 C++ 程式提出警告 (相關內容請看「警告參數」節)。
  • +eN
    限定已定義之 virtual 函數如何使用 (相關內容請看「程式碼生成參數」節)。

除錯參數


對於編譯後的可執行檔之除錯方式,通常的方是是在編譯之時便在特定部份放入除錯碼,有除錯碼的程式在大小和執行速度上都會略遜一般程式一籌,但相對的卻也提供額外的訊息讓 gdb 等除錯程式進行對原始碼及變數的追蹤等工作,除錯碼的規格有許多種,而 gdb 也有自己獨特的部份,因此在不同的系統和不同的除錯器下,便需不同的參數才能順利工作。

  • -g
    產生作業系統認可格式 (native format) 的除錯碼 (可能為 stabs、COFF、XCOFF 或是 DWARF 之一),以便 GDB 除錯。大多數的系統使用 stabs 格式;本參數除了會產生標準格式碼外,還會產生額外的除錯碼,這些額外的部份只有 GDB 能辨識,目的是為了提供更多訊息,因此若是要以別的除錯程式來除錯,便需使用 -gstabs+-gstabs-gxcoff+-gxcoff-gdwarf+ 或是 -gdwarf,這些參數的作用請看下列依次的說明。
  • -ggdb
    除了產生 native format 的除錯碼外,還再包含 GDB 專用的擴充部份。
  • -gstabs
    產生 stabs 格式除錯碼,但不包含 GDB 的擴充部份,這是在大多數 BSD 系統上 DBX 所使用的格式,然而在 MIPS 和 Alpha 系統上卻會產生 DBX 所不能辨識的內嵌 (embedded) stabs 除錯碼。
  • -gstabs+
    產生包含 GDB 擴充部份的 stabs 格式除錯碼,然而這樣別的除錯程式便無法用來除錯。
  • -gcoff
    產生COFF格式的除錯碼;COFF 格式是 System V 至 Release 4 之前的 SDB 所用之格式。
  • -gxcoff
    產生 XCOFF 格式之除錯碼;這個格式為 DBX 在 IBM RS/6000 系統上所使用。
  • -gxcoff+
    產生包含 GDB擴充部份的 XCOFF格式除錯碼。
  • -gdwarf
    產生 DWARF 格式的除錯碼;這個格式是 System V Release 4 上 SDB 所用之格式。
  • -gdwarf+
    產生包含 GDB 擴充部份的 DWARF 格式除錯碼。
  • -gLEVEL
  • -ggdbLEVEL
  • -gstabsLEVEL
  • -gcoffLEVEL
  • -gxcoffLEVEL
  • -gdwarfLEVEL
    產生不同格式的除錯碼,並依 LEVEL 的數值決定除錯碼的多寡,內定數值為 2。LEVEL 1 產生最少量的除錯碼,通常置於非主要除錯部份,在這些除錯碼中包含函數和外部變數 (external variables) 的描述,但無行編號和區域變數 (local variables)。LEVEL 3 包含比 LEVEL 2 更多額外資訊,如所有此程式中定義的巨集。
  • -p
    產生分析程式 prof 所能確認的特殊碼,在編譯和連結時都需使用本參數才能達到目的。
  • -pg
    產生分析程式 gprof 所能確認的特殊碼,在編譯和連結時都需使用本參數才能達到目的。
  • -a
    產生 basic blocks 的基本訊息,如每個 blocks 執行的時間、啟始位址和包含的函數名稱;如果 -g 一起使用時,行編號和第一個 block 的檔名也會一併記錄,這些資料內定會附加到 bb.out 中,可用 tcov 或是 gprof 作分析。
  • -dLETTERS
    本參數是對 "編譯程式" 作 debug,方式為當編譯進行到 LETTERS 所指定的步驟時,將特定資訊傾印到指定的檔案去,相信讀者看到此可能會覺得奇怪,為何要對編譯程式除錯呢?照說 GCC 應該是不太可能會出錯的,事實上別忘了 GCC 是網路上大家同心撰寫出來的,新增改進部份的測試方式為令其編譯一程式並觀查其結果,這時本參數便派上用場了,有時也會發生 patch 後再編譯舊程式時竟然出現過去沒有的錯誤,若確定舊程式沒問題,可用本參數一步一步追蹤編譯的結果;
    以下是 LETTERS 的部份;
    • M 當前置處理完畢後 dump 出所有的巨集定義
    • N 當前置處理完畢後 dump 出所有的巨集名稱
    • D 當前置處理完畢後 dump 出所有的巨集定義至一般輸出,因此可重新將結果導至檔案
    • y 在語法分析 (parsing) 時將除錯訊息dump至標準錯誤輸出
    • r 在 RLT 產生後將內容 dump 至 FILE.rtl
    • x 只產生 RLT 碼後便停止,本項通常和上一項 r 一起使用
    • j 第一輪跳躍最佳化作完便 dump 至 FILE.jump
    • s 完成 CSE 後 dump 至 FILE.cse
    • L 當迴圈最佳化 (loop optimization) 完成後便 dump 至 FILE.loop
    • t 第二輪 CSE 完成後 dump 至 FILE.cse2
    • f flow analysis 完成後 dump 至 FILE.flow
    • c 指令連結完成後 dump 至 FILE.combine
    • S 第一輪指令排程語法分析完成後 dump 至 FILE.sched
    • l 區域暫存器變數定位完成後 dump 至 FILE.lreg
    • g 全體暫存器變數定位完成後 dump 至 FILE.greg
    • R 第二輪指令排程語法分析完成後 dump 至 FILE.sched2
    • J 最後一輪最佳化完成後 dump 至 FILE.jump2
    • d delayed branch 排程完成後 dump 至 FILE.dbr
    • k 暫存器變數轉換到堆疊後 dump 至 FILE.stack
    • a 完成上列所有 dump
    • m 編譯完畢後印出記憶體使用狀況至 std error
    • p 將 assember 的輸出加上有使用之 alternative 和 pattern 的註釋
  • -fpretend-float
    當進行 cross-compiler 時,將目標主機的浮點運算器假設為和目前主機相同,以便編譯出的程式在目標主機上能執行無誤;所謂 cross-compiler 為在某一型主機或是 OS 上(如 Unix )編譯出另一 OS (如 DOS) 執行檔的過程,如此在軟體移植上更為方便,相關的詳細內容會另文再敘。
  • -save-temps
    將編譯時的產生的中間暫存檔儲存起來,例如使用 "-c -save-temps" 來編譯 foo.c 時將會產生 foo.i 和 foo.s。
  • -print-libgcc-file-name
    只是印出 'libgcc.a' 此檔的絕對名稱,在本參數下 GCC 並不作任何事 -- 只是列出 libgcc.a 這個字串;當使用 -nostdlib 參數卻同時想和 libgcc.a 連結時便可依下列方式使用本參數:
    gcc -nostdlib FILES... 'gcc -print-libgcc-file-name'

結語


以上所介紹的為部份 GCC 參數的用法,希望能讓讀者對整個編譯流程有助益,下次我們繼續探討其餘的部份。

修改日期: 02/08/2000 14:01:39

    文章標籤

    C 語言 C GNU GCC gcc

    全站熱搜

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