gcc編譯問題


https://gcc.gnu.org/onlinedocs/gcc-7.2.0/gcc/Optimize-Options.html

 

https://gcc.gnu.org/onlinedocs/

 

.PHONY : all clean

all : test.exe
clean :
    -rm -rf *~
    -rm -rf test.exe

test.exe : test.c json.c
    gcc -g -Wall -O2 -o $@ $^ -lm

 https://github.com/wangzhione/scjson/Makefile

 

gcc avl.o hash.o list.o rb.o example.o -o 123.exe

多個.o輸出 exe

 

-c和-o都是gcc編譯器的可選參數。-c表示只編譯(compile)源文件但不鏈接,會把.c或.cc的c源程序編譯成目標文件,一般是.o文件。-o用於指定輸出(out)文件名。不用-o的話,一般會在當前文件夾下生成默認的a.out文件作為可執行程序。

 
  • gcc:GNU編譯器套件(GNU Compiler Collection)包括C、C++、Objective-C、Fortran、Java、Ada和Go語言的前端,也包括了這些語言的庫(如libstdc++、libgcj等等)。GCC的初衷是為GNU操作系統專門編寫的一款編譯器。GNU系統是徹底的自由軟件。此處,“自由”的含義是它尊重用戶的自由。

  • 創作背景:GCC(GNU Compiler Collection,GNU編譯器套件),是由 GNU 開發的編程語言編譯器。它是以GPL許可證所發行的自由軟件,也是 GNU計划的關鍵部分。GCC原本作為GNU操作系統的官方編譯器,現已被大多數類Unix操作系統(如Linux、BSD、Mac OS X等)采納為標准的編譯器,GCC同樣適用於微軟的Windows。

  • 基本用法:

  1. -o output_filename,確定輸出文件的名稱為output_filename,同時這個名稱不能和源文件同名。如果不給出這個選項,gcc就給出預設的可執行文件a.out。

  2. -O,對程序進行優化編譯、鏈接,采用這個選項,整個源代碼會在編譯、鏈接過程中進行優化處理,這樣產生的可執行文件的執行效率可以提高,但是,編譯、鏈接的速度就相應地要慢一些。

  3. -O2,比-O更好的優化編譯、鏈接,當然整個編譯、鏈接過程會更慢。

  4. -Idirname,將dirname所指出的目錄加入到程序頭文件目錄列表中,是在預編譯過程中使用的參數。

  5.  

 

-O參數只有5個級別,並不是設置的越高越好(以前的GCC支持很高的級別,但是現在確實只有5級)

◆-O0:關閉所有優化選項

◆-O1:基本優化,編譯器會生成更快的代碼

◆-O2:-O1的升級版,推薦使用

◆-O3:這是目前最高的優化級別,它會使用更多的編譯時間,雖然它生成的代碼只會比-O2塊一點點(GCC3.x中是這樣的,但是在GCC4.x中有時候可能還沒有-O2快),但是它會增大二進制文件的體積並讓他們更消耗內存,因此在GCC4.x中-O3是不推薦的。

◆-Os:這個級別是用來優化代碼尺寸的,他只是給一些CPU緩存或是磁盤空間小的機器使用

 

-O0: 無優化。

-O和-O1: 使用能減少目標代碼尺寸以及執行時間並且不會使編譯時間明顯增加的優化。在編譯大型程序的時候會顯著增加編譯時內存的使用。

-O2: 包含-O1的優化並增加了不需要在目標文件大小和執行速度上進行折衷的優化。編譯器不執行循環展開以及函數內聯。此選項將增加編譯時間和目標文件的執行性能。

-Os: 可以看成 -O2.5,專門優化目標文件大小,執行所有的不增加目標文件大小的-O2優化選項,並且執行專門減小目標文件大小的優化選項。適用於磁盤空間緊張時使用。但有可能有未知的問題發生,況且目前硬盤容量很大,常用程序無必要使用。

-O3: 打開所有 -O2 的優化選項外增加 -finline-functions、-funswitch-loops、-fgcse-after-reload 優化選項。相對於 -O2 性能並未有較多提高,編譯時間也最長,生成的目標文件也更大更占內存,有時性能不增反而降低,甚至產生不可預知的問題(包括錯誤),所以並不被大多數軟件安裝推薦,除非有絕對把握方可使用此優化級別。

修改GCC編譯參數,提高編譯優化級別,此方法適用於所有通過GCC編譯安裝的程序,不止Nginx。穩妥起見用 -O2,這也是大多數軟件編譯推薦的優化級別。查看Nginx源碼文件 auto/cc/gcc,搜索NGX_GCC_OPT,默認GCC編譯參數為-O,可以直接修改內容為NGX_GCC_OPT="-O2"或者在 ./configure配置時添加--with-cc-opt='-O2'選項。

 
來源:CSDN
原文:https://blog.csdn.net/xiaoyi23000/article/details/79789529
 

 

使用GCC編譯一個.c文件影藏了哪些過程?


GCC四步詳解
第一步:預處理(也叫預編譯)
        gcc -E  hello.c  -o hello.i
        或者 cpp hello.c > hello.i     【cpp是預編譯器】
        將所有#define刪除,並且展開所有的宏定義
        處理所有的條件預編譯指令,如#if #ifdef  #undef  #ifndef  #endif #elif
        處理#include,將包含的文件插入到此處,這是一個遞歸的過程
        刪除所有注釋   //   /* */
        添加行號和文件名標識,以便於編譯時產生的錯誤警告能顯示行號
        保留#pragma編譯器指令
第二步:編譯
        gcc  -S  hello.i   -o  hello.s
        將預處理完的.i文件進行一系列的詞法分析、語法分析、語義分析及優
        化后生成響應的匯編代碼文件,這是整個程序構建的最核心的部分,也是最復雜的部分

第三步:匯編
        gcc  -c  hello.s  -o  hello.o或者 as  hello.s -o  hello.o
        匯編是將第二步生成的匯編代碼編程機器可執行的指令,每一個匯編語句幾乎都對應一條機器指令


第四步:鏈接

         鏈接動態庫和靜態庫

 

生成的目標文件有什么,什么是目標文件?
目標文件就是源代碼經過編譯后但未進行鏈接的那些中間文件
Linux下的 .o文件就是目標文件,目標文件和可執行文件內容和
格式幾乎都一樣,所以我們可以廣義地將目標文件和可執行文化
看成一類型文件。他們都是按照ELF文件格式存儲的


Linux下有哪些ELF類型的文件?
.o文件、可執行文件、核心轉儲文件(core dump)、.so文件(動態鏈
鏈接庫)


可執行文件的概貌詳解
File  Header.text section.data section.bss section
文件頭(File Header)
描述了整個文件的文件屬性,包括目標文件是否可執行、是靜態鏈接還 是動
態鏈接及入口地址、目標硬件、目標操作系統等信息、段表(描述文件中各
個段的偏移位置及屬性等)
代碼段(.text)
存放了程序源代碼編譯后生成的機器指令
數據段(.data)
存放已初始化的全局靜態與非靜態變量和已初始化的局部靜態變量
.bss段
存放未初始化的全局變量(全局靜態和非靜態變量)和局部靜態變量
但是.bss段只是為這些變量預留位置而已,並沒有內容,所以這些變量
在.bss段中也不占據空間


深入挖掘 .o文件


使用命令:


objdump  -h  xxxx.o

        打印主要段的信息

objdump  -x  xxxx.o 
            打印更多的詳細信息
objdump  -s  xxx.o
            將所有段的內容以16進制方式打印出來
objdump  -d  xxx.o  或者-S
            將所有包含指令的段反匯編
objdump   -t   xxx.o
            查看所有的符號以及他們所在段
readelf  -h   xxx.o
            查看.o文件的文件頭詳細信息
readelf   -S   xxx.o
            顯示.o文件中的所有段,即查看段表
size xxx.o
            查看.o文件中各個段所占大小
nm xxx.o 
            查看.o文件中所有的符號


使用命令gcc -c test.c編譯下面這個test.c程序生成test.o文件,然后查看test.o文件結構


test.c

/* this is a test code */
/* test.c */

int printf(const char *format, ...);

int g_var2 = 10;
int g_var2;

void func(int i)
{
printf("%d\n",i);
}

int main(void)
{
static int static_var1 = 20;
static int static_var2;

int var3 = 1;
int var4;
func(static_var1 + static_var2 + var3 + var4);
return var3;
}

然后查看生成的test.o文件的結構
objdump -h test.o

行:
    .text  :代碼段(存放函數的二進制機器指令)
    .data :數據段(存已初始化的局部/全局靜態變量、未初始化的全局靜態變量)
    .bss  :bss段(聲明未初始化變量所占大小)
    .rodata :只讀數據段(存放 " " 引住的只讀字符串)
    .comment :注釋信息段
    .node.GUN-stack :堆棧提示段
列:
    Size:段的長度
    File Off :段的所在位置(即距離文件頭的偏移位置)
段的屬性:
    CONTENTS:表示該段在文件中存在
    ALLOC :表示只分配了大小,但沒有存內容


關於.bss段
我們說.bss段是存放未初始化的全局變量(靜態與非靜態)和局部靜態變量的
所以我們程序中的g_var2和stactic_var2應該都在.bss段中被預留位置,所以
.bss段的size應該是8個字節,但是結果卻是4個字節,怎么回事呢?
這就是不用的編譯器實現不一樣的原因了,有些編譯器會將未初始化的全局非靜態變量放在.bss段,有些則不放,只是預留一個未定義的全局變量符號,等到最終鏈接成可執行文件的時候再在.bss段分配空間。而我的編譯器是沒有將g_var2(全局未初始化的非靜態變量)放在任何段
下面讓我們真正的查看一下g_var2
首先,我們使用  readelf -S  test.o  查看段表(主要為了查看每個段的段號)


然后我們再使用 readelf -s  test.o看一下符號表(我們定義的變量名都是符號,包括函數名)
符號表里會顯示這個符號所在的位置

 


我們看到static_var1和g_var1所在段的段號為3(3是.data段),static_var2所在段的段號為4(4是.bss段),而g_var2卻沒有被放入任何一個段,只是用COM標記了一下,那這個COM表示什么意思呢?COM標記的符號被稱為弱符號,一個變量名是弱符號,則這個變量的大小在編譯的時候不能被確定,而在鏈接之后才能確定該變量的大小。test.o文件在鏈接之后,g_var2會被放入.bss段(當然,也只是說明g_var2所需要的空間大小,並不會存放內容),而在程序運行的時候g_var2這樣的變量才會真正去占用內存空間


強制將某變量或者某函數放入某個段
__attribute__((section(".data")))  int   g_var2;   //強制將g_var2放入.data段中

 

各種變量所在位置總結
    全局已初始化非靜態變量、局部已初始化靜態變量會被放入.data段
    全局未初始化靜態變量會被放入.bss段
    全圖未初始化非靜態變量不會被放入任何一個段,只是用COM標記一下
 
來源:CSDN
原文:https://blog.csdn.net/gt1025814447/article/details/80442673
 

 

版本] -0.13  
[聲明]  
這篇文檔是我的關於gcc參數的筆記,我很懷念dos年代我用小本子,紀錄任何的dos 命令的參數.哈哈,下面的東西可能也不是很全面,我參考了很多的書,和gcc的幫助.不全的原因是,有可能我還沒有看到這個參數,另一種原因是,我可能還不會用他 但是,我會慢慢的補齊的.哈哈 假如您要轉在本文章請保留我email(pianopan@beeship.com)和文章的全面性.  
[介紹]  
gcc and g++分別是gnu的c & c++編譯器 gcc/g++在執行編譯工作的時候,總共需要4步  
1.預處理,生成.i的文檔[預處理器cpp]  
2.將預處理后的文檔不轉換成匯編語言,生成文檔.s[編譯器egcs]  
3.有匯編變為目標代碼(機器代碼)生成.o的文檔[匯編器as]  
4.連接目標代碼,生成可執行程式[鏈接器ld] 

[參數詳解]  
-x language filename  
  設定文檔所使用的語言,使后綴名無效,對以后的多個有效.也就是根據約定C語言的后綴名稱是.c的,而C++的后綴名是.C或.cpp,假如您很個性,決定您的C代碼文檔的后綴名是.pig 哈哈,那您就要用這個參數,這個參數對他后面的文檔名都起作用,除非到了下一個參數的使用。  
  能夠使用的參數嗎有下面的這些  
  `c', `objective-c', `c-header', `c++', `cpp-output', `assembler', and `assembler-with-cpp'.  
  看到英文,應該能夠理解的。  
  例子用法:  
  gcc -x c hello.pig  
    
-x none filename  
  關掉上一個選項,也就是讓gcc根據文檔名后綴,自動識別文檔類型  
  例子用法:  
  gcc -x c hello.pig -x none hello2.c  
    
-c  
  只激活預處理,編譯,和匯編,也就是他只把程式做成obj文檔  
  例子用法:  
  gcc -c hello.c  
  他將生成.o的obj文檔 

-S  
  只激活預處理和編譯,就是指把文檔編譯成為匯編代碼。  
  例子用法  
  gcc -S hello.c  
  他將生成.s的匯編代碼,您能夠用文本編輯器察看 

-E  
  只激活預處理,這個不生成文檔,您需要把他重定向到一個輸出文檔里面.  
  例子用法:  
  gcc -E hello.c > pianoapan.txt  
  gcc -E hello.c | more  
  慢慢看吧,一個hello word 也要和處理成800行的代碼 

-o  
  定制目標名稱,缺省的時候,gcc 編譯出來的文檔是a.out,很難聽,假如您和我有同感,改掉他,哈哈  
  例子用法  
  gcc -o hello.exe hello.c (哦,windows用習慣了)  
  gcc -o hello.asm -S hello.c 

-pipe  
  使用管道代替編譯中臨時文檔,在使用非gnu匯編工具的時候,可能有些問題  
  gcc -pipe -o hello.exe hello.c 

-ansi  
  關閉gnu c中和ansi c不兼容的特性,激活ansi c的專有特性(包括禁止一些asm inline typeof關鍵字,連同UNIX,vax等預處理宏, 

-fno-asm  
  此選項實現ansi選項的功能的一部分,他禁止將asm,inline和typeof用作關鍵字。       
-fno-strict-prototype  
  只對g++起作用,使用這個選項,g++將對不帶參數的函數,都認為是沒有顯式的對參數的個數和類型說明,而不是沒有參數.  
  而gcc無論是否使用這個參數,都將對沒有帶參數的函數,認為城沒有顯式說明的類型  
    
-fthis-is-varialble  
  就是向傳統c++看齊,能夠使用this當一般變量使用.  
    
-fcond-mismatch  
  允許條件表達式的第二和第三參數類型不匹配,表達式的值將為void類型  
    
-funsigned-char  
-fno-signed-char  
-fsigned-char  
-fno-unsigned-char  
  這四個參數是對char類型進行配置,決定將char類型配置成unsigned char(前兩個參數)或 signed char(后兩個參數)  
    
-include file  
  包含某個代碼,簡單來說,就是便以某個文檔,需要另一個文檔的時候,就能夠用他設定,功能就相當於在代碼中使用#include<filename>  
  例子用法:  
  gcc hello.c -include /root/pianopan.h  
    
-imacros file  
  將file文檔的宏,擴展到gcc/g++的輸入文檔,宏定義本身並不出現在輸入文檔中  
    
-Dmacro  
  相當於C語言中的#define macro  
    
-Dmacro=defn  
  相當於C語言中的#define macro=defn  
    
-Umacro  
  相當於C語言中的#undef macro 

-undef  
  取消對任何非標准宏的定義  
    
-Idir  
  在您是用#include"file"的時候,gcc/g++會先在當前目錄查找您所定制的頭文檔,假如沒有找到,他回到缺省的頭文檔目錄找,假如使用-I定制了目錄,他  
  回先在您所定制的目錄查找,然后再按常規的順序去找.  
  對於#include<file>,gcc/g++會到-I定制的目錄查找,查很難找到,然后將到系統的缺省的頭文檔目錄查找  
    
-I-  
  就是取消前一個參數的功能,所以一般在-Idir之后使用  
    
-idirafter dir  
  在-I的目錄里面查找失敗,講到這個目錄里面查找.  
    
-iprefix prefix  
-iwithprefix dir  
  一般一起使用,當-I的目錄查找失敗,會到prefix+dir下查找  
    
-nostdinc  
  使編譯器不再系統缺省的頭文檔目錄里面找頭文檔,一般和-I聯合使用,明確限定頭文檔的位置  
    
-nostdin C++  
  規定不在g++指定的標准路經中搜索,但仍在其他路徑中搜索,.此選項在創libg++庫使用  
    
-C  
  在預處理的時候,不刪除注釋信息,一般和-E使用,有時候分析程式,用這個很方便的  
    
-M  
  生成文檔關聯的信息。包含目標文檔所依賴的任何源代碼您能夠用gcc -M hello.c來測試一下,很簡單。  
    
-MM  
  和上面的那個相同,但是他將忽略由#include<file>造成的依賴關系。  
    
-MD  
  和-M相同,但是輸出將導入到.d的文檔里面  
    
-MMD  
  和-MM相同,但是輸出將導入到.d的文檔里面  
    
-Wa,option  
  此選項傳遞option給匯編程式;假如option中間有逗號,就將option分成多個選項,然后傳遞給會匯編程式  
    
-Wl.option  
  此選項傳遞option給連接程式;假如option中間有逗號,就將option分成多個選項,然后傳遞給會連接程式.  
   

-llibrary  
  定制編譯的時候使用的庫  
  例子用法  
  gcc -lcurses hello.c  
  使用ncurses庫編譯程式  
    
-Ldir  
  定制編譯的時候,搜索庫的路徑。比如您自己的庫,能夠用他定制目錄,不然  
  編譯器將只在標准庫的目錄找。這個dir就是目錄的名稱。  
    
-O0  
-O1  
-O2  
-O3  
  編譯器的優化選項的4個級別,-O0表示沒有優化,-O1為缺省值,-O3優化級別最高       
-g  
  只是編譯器,在編譯的時候,產生調試信息。  
    
-gstabs  
  此選項以stabs格式聲稱調試信息,但是不包括gdb調試信息.  
    
-gstabs+  
  此選項以stabs格式聲稱調試信息,並且包含僅供gdb使用的額外調試信息.  
    
-ggdb  
  此選項將盡可能的生成gdb的能夠使用的調試信息. 

-static  
  此選項將禁止使用動態庫,所以,編譯出來的東西,一般都很大,也無需什么  
動態連接庫,就能夠運行. 

-share  
  此選項將盡量使用動態庫,所以生成文檔比較小,但是需要系統由動態庫. 

-traditional  
  試圖讓編譯器支持傳統的C語言特性 

 

 

gcc提供了大量的警告選項,對代碼中可能存在的問題提出警 告,通常可以使用-Wall來開啟以下警告:
           -Waddress -Warray-bounds (only with -O2) -Wc++0x-compat
           -Wchar-subscripts -Wimplicit-int -Wimplicit-function-declaration
           -Wcomment -Wformat -Wmain (only for C/ObjC and unless
           -ffreestanding) -Wmissing-braces -Wnonnull -Wparentheses
           -Wpointer-sign -Wreorder -Wreturn-type -Wsequence-point
           -Wsign-compare (only in C++) -Wstrict-aliasing -Wstrict-overflow=1
           -Wswitch -Wtrigraphs -Wuninitialized (only with -O1 and above)
           -Wunknown-pragmas -Wunused-function -Wunused-label -Wunused-value
           -Wunused-variable
unused-function:警告聲明但是沒有定義的static函數;
unused- label:聲明但是未使用的標簽;
unused-parameter:警告未使用的函數參數;
unused-variable:聲明但 是未使用的本地變量;
unused-value:計算了但是未使用的值;
format:printf和scanf這樣的函數中的格式字符 串的使用不當;
implicit-int:未指定類型;
implicit-function:函數在聲明前使用;
char- subscripts:使用char類作為數組下標(因為char可能是有符號數);
missingbraces:大括號不匹配;
parentheses: 圓括號不匹配;
return-type:函數有無返回值以及返回值類型不匹配;
sequence-point:違反順序點的代碼,比如 a[i] = c[i++];
switch:switch語句缺少default或者switch使用枚舉變量為索引時缺少某個變量的case;
strict- aliasing=n:使用n設置對指針變量指向的對象類型產生警告的限制程度,默認n=3;只有在-fstrict-aliasing設置的情況下有 效;
unknow-pragmas:使用未知的#pragma指令;
uninitialized:使用的變量為初始化,只在-O2時有 效;

以下是在-Wall中不會激活的警告選項:
cast-align:當指針進行類型轉換后有內存對齊要求更嚴格時發出警告;
sign- compare:當使用signed和unsigned類型比較時;
missing-prototypes:當函數在使用前沒有函數原型時;
packed:packed 是gcc的一個擴展,是使結構體各成員之間不留內存對齊所需的 空 間,有時候會造成內存對齊的問題;
padded:也是gcc的擴展,使結構體成員之間進行內存對齊的填充,會 造成結構體體積增大.
unreachable-code:有不會執行的代碼時.
inline:當inline函數不再保持inline時 (比如對inline函數取地址);
disable-optimization:當不能執行指定的優化時.(需要太多時間或 系統資源).
可以使用 -Werror時所有的警告都變成錯誤,使出現警告時也停止編譯.需要和指定警告的參數一起使用.

優化:
gcc默認提供了5級優 化選項的集合:
-O0:無優化(默認)
-O和-O1:使用能減少目標 文 件大小以及執行時間並且不會使編譯時間明顯增加的優化.在編譯大型程序的時候會顯著增加編譯時內存的使用.
-O2: 包含-O1的優化並增加了不需要在目標文件大小和執行速度上進行折衷的優化.編譯器不執行循環展開以及函數內聯.此選項將增加編譯時間和目標文件的執行性 能.
-Os:專門優化目標文件大小,執行所有的不增加目標文件大小的-O2優化選項.並且執行專門減小目標文件大小的優化選項.
-O3: 打開所有-O2的優化選項並且增加 -finline-functions, -funswitch-loops,-fpredictive-commoning, -fgcse-after-reload and -ftree-vectorize優化選項.

-O1包含的選項-O1通常可以安全的和調試的選項一起使用:
           -fauto-inc-dec -fcprop-registers -fdce -fdefer-pop -fdelayed-branch
           -fdse -fguess-branch-probability -fif-conversion2 -fif-conversion
           -finline-small-functions -fipa-pure-const -fipa-reference
           -fmerge-constants -fsplit-wide-types -ftree-ccp -ftree-ch
           -ftree-copyrename -ftree-dce -ftree-dominator-opts -ftree-dse
           -ftree-fre -ftree-sra -ftree-ter -funit-at-a-time

以下所有的優化選項需要在名字 前加上-f,如果不需要此選項可以使用-fno-前綴
defer-pop:延遲到只在必要時從函數參數棧中pop參數;
thread- jumps:使用跳轉線程優化,避免跳轉到另一個跳轉;
branch-probabilities:分支優化;
cprop- registers:使用寄存器之間copy-propagation傳值;
guess-branch-probability:分支預測;
omit- frame-pointer:可能的情況下不產生棧幀;

-O2:以下是-O2在-O1基礎上增加的優化選項:
           -falign-functions  -falign-jumps -falign-loops  -falign-labels
           -fcaller-saves -fcrossjumping -fcse-follow-jumps  -fcse-skip-blocks
           -fdelete-null-pointer-checks -fexpensive-optimizations -fgcse
           -fgcse-lm -foptimize-sibling-calls -fpeephole2 -fregmove
           -freorder-blocks  -freorder-functions -frerun-cse-after-loop
           -fsched-interblock  -fsched-spec -fschedule-insns
           -fschedule-insns2 -fstrict-aliasing -fstrict-overflow -ftree-pre
           -ftree-vrp
cpu架構的優化選項,通常是-mcpu(將被取消);-march,-mtune

Debug選項:
在 gcc編譯源代碼時指定-g選項可以產生帶有調試信息的目標代碼,gcc可以為多個不同平台上帝不同調試器提供調試信息,默認gcc產生的調試信息是為 gdb使用的,可以使用-gformat 指定要生成的調試信息的格式以提供給其他平台的其他調試器使用.常用的格式有
-ggdb:生成gdb專 用的調試信息,使用最適合的格式(DWARF 2,stabs等)會有一些gdb專用的擴展,可能造成其他調試器無法運行.
-gstabs:使用 stabs格式,不包含gdb擴展,stabs常用於BSD系統的DBX調試器.
-gcoff:產生COFF格式的調試信息,常用於System V下的SDB調試器;
-gxcoff:產生XCOFF格式的調試信息,用於IBM的RS/6000下的DBX調試器;
-gdwarf- 2:產生DWARF version2 的格式的調試信息,常用於IRIXX6上的DBX調試器.GCC會使用DWARF version3的一些特性.
可 以指定調試信息的等級:在指定的調試格式后面加上等級:
如: -ggdb2 等,0代表不產生調試信息.在使用-gdwarf-2時因為最早的格式為-gdwarf2會造成混亂,所以要額外使用一個-glevel來指定調試信息的 等級,其他格式選項也可以另外指定等級.

gcc可以使用-p選項指定生成信息以供porf使用.

 

GCC常用選項

選項 含義
--help 
--target-help
顯示 gcc 幫助說明。‘target-help’是顯示目標機器特定的命令行選項。
--version 顯示 gcc 版本號和版權信息 。
-ooutfile 輸出到指定的文件。
-xlanguage 指明使用的編程語言。允許的語言包括:c c++ assembler none 。 ‘none’意味着恢復默認行為,即根據文件的擴展名猜測源文件的語言。
-v 打印較多信息,顯示編譯器調用的程序。
-### 與 -v 類似,但選項被引號括住,並且不執行命令。
-E 僅作預處理,不進行編譯、匯編和鏈接。如上圖所示。
-S 僅編譯到匯編語言,不進行匯編和鏈接。如上圖所示。
-c 編譯、匯編到目標代碼,不進行鏈接。如上圖所示。
-pipe 使用管道代替臨時文件。
-combine 將多個源文件一次性傳遞給匯編器。

 

 

3 其他GCC選項

更多有用的GCC選項:

 

命令 描述
-l library
-llibrary
進行鏈接時搜索名為library的庫。
例子: $ gcc test.c -lm -o test
-Idir dir加入到搜索頭文件的路徑列表中。
例子: $ gcc test.c -I../inc -o test
-Ldir dir加入到搜索庫文件的路徑列表中。
例子: $ gcc -I/home/foo -L/home/foo -ltest test.c -o test
-Dname 預定義一個名為name的宏,值為1。
例子: $ gcc -DTEST_CONFIG test.c -o test
-Dname=definition 預定義名為name,值為definition的宏。
-ggdb 
-ggdblevel
為調試器 gdb 生成調試信息。level可以為1,2,3,默認值為2。
-g 
-glevel
生成操作系統本地格式的調試信息。-g 和 -ggdb 並不太相同, -g 會生成 gdb 之外的信息。level取值同上。
-s 去除可執行文件中的符號表和重定位信息。用於減小可執行文件的大小。
-M 告訴預處理器輸出一個適合make的規則,用於描述各目標文件的依賴關系。對於每個 源文件,預處理器輸出 一個make規則,該規則的目標項(target)是源文件對應的目標文件名,依賴項(dependency)是源文件中 `#include引用的所有文件。生成的規則可 以是單行,但如果太長,就用`\'-換行符續成多行。規則 顯示在標准輸出,不產生預處理過的C程序。
-C 告訴預處理器不要丟棄注釋。配合`-E'選項使用。
-P 告訴預處理器不要產生`#line'命令。配合`-E'選項使用。
-static 在支持動態鏈接的系統上,阻止連接共享庫。該選項在其它系統上 無效。
-nostdlib 不連接系統標准啟動文件和標准庫文件,只把指定的文件傳遞給連接器。
Warnings  
-Wall 會打開一些很有用的警告選項,建議編譯時加此選項。
-W 
-Wextra
打印一些額外的警告信息。
-w 禁止顯示所有警告信息。
-Wshadow 當一個局部變量遮蓋住了另一個局部變量,或者全局變量時,給出警告。很有用的選項,建議打開。 -Wall 並不會打開此項。
-Wpointer-arith 對函數指針或者void *類型的指針進行算術操作時給出警告。也很有用。 -Wall 並不會打開此項。
-Wcast-qual 當強制轉化丟掉了類型修飾符時給出警告。 -Wall 並不會打開此項。
-Waggregate-return 如果定義或調用了返回結構體或聯合體的函數,編譯器就發出警告。
-Winline 無論是聲明為 inline 或者是指定了-finline-functions 選項,如果某函數不能內聯,編譯器都將發出警告。如果你的代碼含有很多 inline 函數的話,這是很有用的選項。
-Werror 把警告當作錯誤。出現任何警告就放棄編譯。
-Wunreachable-code 如果編譯器探測到永遠不會執行到的代碼,就給出警告。也是比較有用的選項。
-Wcast-align 一旦某個指針類型強制轉換導致目標所需的地址對齊增加時,編譯器就發出警告。
-Wundef 當一個沒有定義的符號出現在 #if 中時,給出警告。
-Wredundant-decls 如果在同一個可見域內某定義多次聲明,編譯器就發出警告,即使這些重復聲明有效並且毫無差別。
Optimization  
-O0 禁止編譯器進行優化。默認為此項。
-O 
-O1
嘗試優化編譯時間和可執行文件大小。
-O2 更多的優化,會嘗試幾乎全部的優化功能,但不會進行“空間換時間”的優化方法。
-O3 在 -O2 的基礎上再打開一些優化選項:-finline-functions, -funswitch-loops 和 -fgcse-after-reload 。
-Os 對生成文件大小進行優化。它會打開 -O2 開的全部選項,除了會那些增加文件大小的。
-finline-functions 把所有簡單的函數內聯進調用者。編譯器會探索式地決定哪些函數足夠簡單,值得做這種內聯。
-fstrict-aliasing 施加最強的別名規則(aliasing rules)。
Standard  
-ansi 支持符合ANSI標准的C程序。這樣就會關閉GNU C中某些不兼容ANSI C的特性。
-std=c89 
-iso9899:1990
指明使用標准 ISO C90 作為標准來編譯程序。
-std=c99 
-std=iso9899:1999
指明使用標准 ISO C99 作為標准來編譯程序。
-std=c++98 指明使用標准 C++98 作為標准來編譯程序。
-std=gnu9x 
-std=gnu99
使用 ISO C99 再加上 GNU 的一些擴展。
-fno-asm 不把asm, inline或typeof當作關鍵字,因此這些詞可以用做標識符。用 __asm__, __inline__和__typeof__能夠替代它們。 `-ansi' 隱含聲明了`-fno-asm'。
-fgnu89-inline 告訴編譯器在 C99 模式下看到 inline 函數時使用傳統的 GNU 句法。
C options  
-fsigned-char 
-funsigned-char
把char定義為有/無符號類型,如同signed char/unsigned char。
-traditional 嘗試支持傳統C編譯器的某些方面。詳見GNU C手冊。
-fno-builtin 
-fno-builtin-function
不接受沒有 __builtin_ 前綴的函數作為內建函數。
-trigraphs 支持ANSI C的三聯符( trigraphs)。`-ansi'選項隱含聲明了此選項。
-fsigned-bitfields 
-funsigned-bitfields
如果沒有明確聲明`signed'或`unsigned'修飾符,這些選項用來定義有符號位域或無符號位域。缺省情況下,位域是有符號的,因為它們繼承的基本整數類型,如int,是有符號數。
-Wstrict-prototypes 如果函數的聲明或定義沒有指出參數類型,編譯器就發出警告。很有用的警告。
-Wmissing-prototypes 如果沒有預先聲明就定義了全局函數,編譯器就發出警告。即使函數定義自身提供了函數原形也會產生這個警告。這個選項 的目的是檢查沒有在頭文件中聲明的全局函數。
-Wnested-externs 如果某extern聲明出現在函數內部,編譯器就發出警告。
C++ options  
-ffor-scope 從頭開始執行程序,也允許進行重定向。
-fno-rtti 關閉對 dynamic_cast 和 typeid 的支持。如果你不需要這些功能,關閉它會節省一些空間。
-Wctor-dtor-privacy 當一個類沒有用時給出警告。因為構造函數和析構函數會被當作私有的。
-Wnon-virtual-dtor 當一個類有多態性,而又沒有虛析構函數時,發出警告。-Wall會開啟這個選項。
-Wreorder 如果代碼中的成員變量的初始化順序和它們實際執行時初始化順序不一致,給出警告。
-Wno-deprecated 使用過時的特性時不要給出警告。
-Woverloaded-virtual 如果函數的聲明隱藏住了基類的虛函數,就給出警告。
Machine Dependent Options (Intel)  
-mtune=cpu-type 為指定類型的 CPU 生成代碼。cpu-type可以是:i386,i486,i586,pentium,i686,pentium4 等等。
-msse 
-msse2 
-mmmx 
-mno-sse 
-mno-sse2 
-mno-mmx
使用或者不使用MMX,SSE,SSE2指令。
-m32 
-m64
生成32位/64位機器上的代碼。
-mpush-args 
-mno-push-args
(不)使用 push 指令來進行存儲參數。默認是使用。
-mregparm=num 當傳遞整數參數時,控制所使用寄存器的個數。

 

 

 

 

 

讓我們先看看 Makefile 規則中的編譯命令通常是怎么寫的。

大多數軟件包遵守如下約定俗成的規范:

#1,首先從源代碼生成目標文件(預處理,編譯,匯編),"-c"選項表示不執行鏈接步驟。
$(CC) $(CPPFLAGS) $(CFLAGS) example.c   -c   -o example.o
#2,然后將目標文件連接為最終的結果(連接),"-o"選項用於指定輸出文件的名字。
$(CC) $(LDFLAGS) example.o   -o example

#有一些軟件包一次完成四個步驟:
$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) example.c   -o example

當然也有少數軟件包不遵守這些約定俗成的規范,比如:

#1,有些在命令行中漏掉應有的Makefile變量(注意:有些遺漏是故意的)
$(CC) $(CFLAGS) example.c    -c   -o example.o
$(CC) $(CPPFLAGS) example.c  -c   -o example.o
$(CC) example.o   -o example
$(CC) example.c   -o example
#2,有些在命令行中增加了不必要的Makefile變量
$(CC) $(CFLAGS) $(LDFLAGS) example.o   -o example
$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) example.c   -c   -o example.o

當然還有極個別軟件包完全是"胡來":亂用變量(增加不必要的又漏掉了應有的)者有之,不用$(CC)者有之,不一而足.....

盡管將源代碼編譯為二進制文件的四個步驟由不同的程序(cpp,gcc/g++,as,ld)完成,但是事實上 cpp, as, ld 都是由 gcc/g++ 進行間接調用的。換句話說,控制了 gcc/g++ 就等於控制了所有四個步驟。從 Makefile 規則中的編譯命令可以看出,編譯工具的行為全靠 CC/CXX CPPFLAGS CFLAGS/CXXFLAGS LDFLAGS 這幾個變量在控制。當然理論上控制編譯工具行為的還應當有 AS ASFLAGS ARFLAGS 等變量,但是實踐中基本上沒有軟件包使用它們。

那么我們如何控制這些變量呢?一種簡易的做法是首先設置與這些 Makefile 變量同名的環境變量並將它們 export 為全局,然后運行 configure 腳本,大多數 configure 腳本會使用這同名的環境變量代替 Makefile 中的值。但是少數 configure 腳本並不這樣做(比如GCC-3.4.6和Binutils-2.16.1的腳本就不傳遞LDFLAGS),你必須手動編輯生成的 Makefile 文件,在其中尋找這些變量並修改它們的值,許多源碼包在每個子文件夾中都有 Makefile 文件,真是一件很累人的事!

CC 與 CXX

這是 C 與 C++ 編譯器命令。默認值一般是 "gcc" 與 "g++"。這個變量本來與優化沒有關系,但是有些人因為擔心軟件包不遵守那些約定俗成的規范,害怕自己苦心設置的 CFLAGS/CXXFLAGS/LDFLAGS 之類的變量被忽略了,而索性將原本應當放置在其它變量中的選項一股老兒塞到 CC 或 CXX 中,比如:CC="gcc -march=k8 -O2 -s"。這是一種怪異的用法,本文不提倡這種做法,而是提倡按照變量本來的含義使用變量。

CPPFLAGS

這是用於預處理階段的選項。不過能夠用於此變量的選項,看不出有哪個與優化相關。如果你實在想設一個,那就使用下面這兩個吧:

-DNDEBUG
"NDEBUG"是一個標准的 ANSI 宏,表示不進行調試編譯。
-D_FILE_OFFSET_BITS=64
大多數包使用這個來提供大文件(>2G)支持。

CFLAGS 與 CXXFLAGS

CFLAGS 表示用於 C 編譯器的選項,CXXFLAGS 表示用於 C++ 編譯器的選項。這兩個變量實際上涵蓋了編譯和匯編兩個步驟。大多數程序和庫在編譯時默認的優化級別是"2"(使用"-O2"選項)並且帶有調試符號來編 譯,也就是 CFLAGS="-O2 -g", CXXFLAGS=$CFLAGS 。事實上,"-O2"已經啟用絕大多數安全的優化選項了。另一方面,由於大部分選項可以同時用於這兩個變量,所以僅在最后講述只能用於其中一個變量的選 項。[提醒]下面所列選項皆為非默認選項,你只要按需添加即可。

先說說"-O3"在"-O2"基礎上增加的幾項:

-finline-functions
允許編譯器選擇某些簡單的函數在其被調用處展開,比較安全的選項,特別是在CPU二級緩 存較大時建議使用。
-funswitch-loops
將循環體中不改變值的變量移動到循環體之外。
-fgcse-after-reload
為了清除多余的溢出,在重載之后執行一個額外的載入消除步驟。

另外:

-fomit-frame-pointer
對於不需要棧指針的函數就不在寄存器中保存指針,因此可以忽略存儲和檢索地址的代 碼,同時對許多函數提供一個額外的寄存器。所有"-O"級別都打開它,但僅在調試器可以不依靠棧指針運行時才有效。在AMD64平台上此選項默認打開,但 是在x86平台上則默認關閉。建議顯式的設置它。
-falign-functions=N
-falign-jumps=N
-falign-loops=N
-falign-labels=N
這 四個對齊選項在"-O2"中打開,其中的根據不同的平台N使用不同的默認值。如果你想指定不同於默認值的N,也可以單獨指定。比如,對於L2- cache>=1M的cpu而言,指定 -falign-functions=64 可能會獲得更好的性能。建議在指定了 -march 的時候不明確指定這里的值。

調試選項:

-fprofile-arcs
在使用這一選項編譯程序並運行它以創建包含每個代碼塊的執行次數的文件后,程序可以再次使用 -fbranch-probabilities 編譯,文件中的信息可以用來優化那些經常選取的分支。如果沒有這些信息,gcc將猜測哪個分支將被經常運行以進行優化。這類優化信息將會存放在一個以源文 件為名字的並以".da"為后綴的文件中。

全局選項:

-pipe
在編譯過程的不同階段之間使用管道而非臨時文件進行通信,可以加快編譯速度。建議使用。

目錄選項:

--sysroot=dir
將dir作為邏輯根目錄。比如編譯器通常會在 /usr/include 和 /usr/lib 中搜索頭文件和庫,使用這個選項后將在 dir/usr/include 和 dir/usr/lib 目錄中搜索。如果使用這個選項的同時又使用了 -isysroot 選項,則此選項僅作用於庫文件的搜索路徑,而 -isysroot 選項將作用於頭文件的搜索路徑。這個選項與優化無關,但是在 CLFS 中有着神奇的作用。

代碼生成選項:

-fno-bounds-check
關閉所有對數組訪問的邊界檢查。該選項將提高數組索引的性能,但當超出數組邊界時,可能會 造成不可接受的行為。
-freg-struct-return
如果struct和union足夠小就通過寄存器返回,這將提高較小結構的效率。如果 不夠小,無法容納在一個寄存器中,將使用內存返回。建議僅在完全使用GCC編譯的系統上才使用。
-fpic
生成可用於共享庫的位置獨立代碼。所有的內部尋址均通過全局偏移表完成。要確定一個地址,需要將代碼自身的內存位置 作為表中一項插入。該選項產生可以在共享庫中存放並從中加載的目標模塊。
-fstack-check
為防止程序棧溢出而進行必要的檢測,僅在多線程環境中運行時才可能需要它。
-fvisibility=hidden
設置默認的ELF鏡像中符號的可見性為隱藏。使用這個特性可以非常充分的提高連接和加 載共享庫的性能,生成更加優化的代碼,提供近乎完美的API輸出和防止符號碰撞。我們強烈建議你在編譯任何共享庫的時候使用該選項。參見 -fvisibility-inlines-hidden 選項。

硬件體系結構相關選項[僅僅針對x86與x86_64]:

-march=cpu-type
為特定的cpu-type編譯二進制代碼(不能在更低級別的cpu上運行)。Intel可以 用:pentium2, pentium3(=pentium3m), pentium4(=pentium4m), pentium-m, prescott, nocona, core2(GCC-4.3新增) 。AMD可以用:k6-2(=k6-3), athlon(=athlon-tbird), athlon-xp(=athlon-mp), k8(=opteron=athlon64=athlon-fx)
-mfpmath=sse
P3和athlon-xp級別及以上的cpu支持"sse"標量浮點指令。僅建議在P4和K8以上級 別的處理器上使用該選項。
-malign-double
將double, long double, long long對齊於雙字節邊界上;有助於生成更高速的代碼,但是程序的尺寸會變大,並且不能與未使用該選項編譯的程序一起工作。
-m128bit-long-double
指定long double為128位,pentium以上的cpu更喜歡這種標准,並且符合x86-64的ABI標准,但是卻不附合i386的ABI標准。
-mregparm=N
指定用於傳遞整數參數的寄存器數目(默認不使用寄存器)。0<=N<=3 ;注意:當N>0時你必須使用同一參數重新構建所有的模塊,包括所有的庫。
-msseregparm
使用SSE寄存器傳遞float和double參數和返回值。注意:當你使用了這個選項以后,你必須 使用同一參數重新構建所有的模塊,包括所有的庫。
-mmmx
-msse
-msse2
-msse3
-m3dnow
-mssse3(沒寫錯!GCC-4.3 新增)
-msse4.1(GCC-4.3新增)
-msse4.2(GCC-4.3新增)
-msse4(含4.1和 4.2,GCC-4.3新增)
是否使用相應的擴展指令集以及內置函數,按照自己的cpu選擇吧!
-maccumulate-outgoing-args
指定在函數引導段中計算輸出參數所需最大空間,這在大部分現代cpu中 是較快的方法;缺點是會明顯增加二進制文件尺寸。
-mthreads
支持Mingw32的線程安全異常處理。對於依賴於線程安全異常處理的程序,必須啟用這個選項。使用這個選 項時會定義"-D_MT",它將包含使用選項"-lmingwthrd"連接的一個特殊的線程輔助庫,用於為每個線程清理異常處理數據。
-minline-all-stringops
默認時GCC只將確定目的地會被對齊在至少4字節邊界的字符串操作內聯進程序代 碼。該選項啟用更多的內聯並且增加二進制文件的體積,但是可以提升依賴於高速 memcpy, strlen, memset 操作的程序的性能。
-minline-stringops-dynamically
GCC-4.3新增。對未知尺寸字符串的小塊操作使用內聯代 碼,而對大塊操作仍然調用庫函數,這是比"-minline-all-stringops"更聰明的策略。決定策略的算法可以通 過"-mstringop-strategy"控制。
-momit-leaf-frame-pointer
不為葉子函數在寄存器中保存棧指針,這樣可以節省寄存器,但是將會使調試 變的困難。注意:不要與 -fomit-frame-pointer 同時使用,因為會造成代碼效率低下。
-m64
生成專門運行於64位環境的代碼,不能運行於32位環境,僅用於x86_64[含EMT64]環境。
-mcmodel=small
[默認值]程序和它的符號必須位於2GB以下的地址空間。指針仍然是64位。程序可以靜態連接也 可以動態連接。僅用於x86_64[含EMT64]環境。
-mcmodel=kernel
內核運行於2GB地址空間之外。在編譯linux內核時必須使用該選項!僅用於 x86_64[含EMT64]環境。
-mcmodel=medium
程序必須位於2GB以下的地址空間,但是它的符號可以位於任何地址空間。程序可以靜態連接也可 以動態連接。注意:共享庫不能使用這個選項編譯!僅用於x86_64[含EMT64]環境。

其它優化選項:

-fforce-addr
必須將地址復制到寄存器中才能對他們進行運算。由於所需地址通常在前面已經加載到寄存器中了,所以這 個選項可以改進代碼。
-finline-limit=n
對偽指令數超過n的函數,編譯程序將不進行內聯展開,默認為600。增大此值將增加編譯時間 和編譯內存用量並且生成的二進制文件體積也會變大,此值不宜太大。
-fmerge-all-constants
試圖將跨編譯單元的所有常量值和數組合並在一個副本中。但是標准C/C++要求每 個變量都必須有不同的存儲位置,所以該選項可能會導致某些不兼容的行為。
-fgcse-sm
在全局公共子表達式消除之后運行存儲移動,以試圖將存儲移出循環。gcc-3.4中曾屬於"-O2"級別的 選項。
-fgcse-las
在全局公共子表達式消除之后消除多余的在存儲到同一存儲區域之后的加載操作。gcc-3.4中曾屬 於"-O2"級別的選項。
-floop-optimize
已廢除(GCC-4.1曾包含在"-O1"中)。
-floop-optimize2
使用改進版本的循環優化器代替原來"-floop-optimize"。該優化器將使用不同 的選項(-funroll-loops, -fpeel-loops, -funswitch-loops, -ftree-loop-im)分別控制循環優化的不同方面。目前這個新版本的優化器尚在開發中,並且生成的代碼質量並不比以前的版本高。已廢除,僅存在 於GCC-4.1之前的版本中。
-funsafe-loop-optimizations
假定循環不會溢出,並且循環的退出條件不是無窮。這將可以在一個比較 廣的范圍內進行循環優化,即使優化器自己也不能斷定這樣做是否正確。
-fsched-spec-load
允許一些裝載指令執行一些投機性的動作。
-ftree-loop-linear
在trees上進行線型循環轉換。它能夠改進緩沖性能並且允許進行更進一步的循環優化。
-fivopts
在trees上執行歸納變量優化。
-ftree-vectorize
在trees上執行循環向量化。
-ftracer
執行尾部復制以擴大超級塊的尺寸,它簡化了函數控制流,從而允許其它的優化措施做的更好。據說挺有效。
-funroll-loops
僅對循環次數能夠在編譯時或運行時確定的循環進行展開,生成的代碼尺寸將變大,執行速度可能變快 也可能變慢。
-fprefetch-loop-arrays
生成數組預讀取指令,對於使用巨大數組的程序可以加快代碼執行速度,適合數據庫 相關的大型軟件等。具體效果如何取決於代碼。
-fweb
建立經常使用的緩存器網絡,提供更佳的緩存器使用率。gcc-3.4中曾屬於"-O3"級別的選項。
-ffast-math
違反IEEE/ANSI標准以提高浮點數計算速度,是個危險的選項,僅在編譯不需要嚴格遵守IEEE規 范且浮點計算密集的程序考慮采用。
-fsingle-precision-constant
將浮點常量作為單精度常量對待,而不是隱式地將其轉換為雙精度。
-fbranch-probabilities
在使用 -fprofile-arcs 選項編譯程序並執行它來創建包含每個代碼塊執行次數的文件之后,程序可以利用這一選項再次編譯,文件中所產生的信息將被用來優化那些經常發生的分支代碼。 如果沒有這些信息,gcc將猜測那一分支可能經常發生並進行優化。這類優化信息將會存放在一個以源文件為名字的並以".da"為后綴的文件中。
-frename-registers
試圖驅除代碼中的假依賴關系,這個選項對具有大量寄存器的機器很有效。gcc-3.4中 曾屬於"-O3"級別的選項。
-fbranch-target-load-optimize
-fbranch-target-load-optimize2
在 執行序啟動以及結尾之前執行分支目標緩存器加載最佳化。
-fstack-protector
在關鍵函數的堆棧中設置保護值。在返回地址和返回值之前,都將驗證這個保護值。如果出現了 緩沖區溢出,保護值不再匹配,程序就會退出。程序每次運行,保護值都是隨機的,因此不會被遠程猜出。
-fstack-protector-all
同上,但是在所有函數的堆棧中設置保護值。
--param max-gcse-memory=xxM
執行GCSE優化使用的最大內存量(xxM),太小將使該優化無法進 行,默認為50M。
--param max-gcse-passes=n
執行GCSE優化的最大迭代次數,默認為 1。

傳遞給匯編器的選項:

-Wa,options
options是一個或多個由逗號分隔的可以傳遞給匯編器的選項列表。其中的每一個均可作為命令行選項 傳遞給匯編器。
-Wa,--strip-local-absolute
從輸出符號表中移除局部絕對符號。
-Wa,-R
合並數據段和正文段,因為不必在數據段和代碼段之間轉移,所以它可能會產生更短的地址移動。
-Wa,--64
設置字長為64bit,僅用於x86_64,並且僅對ELF格式的目標文件有效。此外,還需要使 用"--enable-64-bit-bfd"選項編譯的BFD支持。
-Wa,-march=CPU
按照特定的CPU進行優化:pentiumiii, pentium4, prescott, nocona, core, core2; athlon, sledgehammer, opteron, k8 。

僅可用於 CFLAGS 的選項:

-fhosted
按宿主環境編譯,其中需要有完整的標准庫,入口必須是main()函數且具有int型的返回值。內核以外幾乎 所有的程序都是如此。該選項隱含設置了 -fbuiltin,且與 -fno-freestanding 等價。
-ffreestanding
按獨立環境編譯,該環境可以沒有標准庫,且對main()函數沒有要求。最典型的例子就是操作系 統內核。該選項隱含設置了 -fno-builtin,且與 -fno-hosted 等價。

僅可用於 CXXFLAGS 的選項:

-fno-enforce-eh-specs
C++標准要求強制檢查異常違例,但是該選項可以關閉違例檢查,從而減小生成代碼 的體積。該選項類似於定義了"NDEBUG"宏。
-fno-rtti
如果沒有使用'dynamic_cast'和'typeid',可以使用這個選項禁止為包含虛方法的類生成 運行時表示代碼,從而節約空間。此選項對於異常處理無效(仍然按需生成rtti代碼)。
-ftemplate-depth-n
將最大模版實例化深度設為'n',符合標准的程序不能超過17,默認值為500。
-fno-optional-diags
禁止輸出診斷消息,C++標准並不需要這些消息。
-fno-threadsafe-statics
GCC自動在訪問C++局部靜態變量的代碼上加鎖,以保證線程安全。如果你不 需要線程安全,可以使用這個選項。
-fvisibility-inlines-hidden
默認隱藏所有內聯函數,從而減小導出符號表的大小,既能縮減文件的大 小,還能提高運行性能,我們強烈建議你在編譯任何共享庫的時候使用該選項。參見 -fvisibility=hidden 選項。

LDFLAGS

LDFLAGS 是傳遞給連接器的選項。這是一個常被忽視的變量,事實上它對優化的影響也是很明顯的。

[提示]以下選項是在完整的閱讀了ld-2.18文檔之后挑選出來的選項。 http://blog.chinaunix.net/u1/41220/showart_354602.html 有2.14版本的中文手冊。

-s
刪除可執行程序中的所有符號表和所有重定位信息。其結果與運行命令 strip 所達到的效果相同,這個選項是比較安全的。
-Wl,options
options是由一個或多個逗號分隔的傳遞給鏈接器的選項列表。其中的每一個選項均會作為命令行選項 提供給鏈接器。
-Wl,-On
當n>0時將會優化輸出,但是會明顯增加連接操作的時間,這個選項是比較安全的。
-Wl,--exclude-libs=ALL
不自動導出庫中的符號,也就是默認將庫中的符號隱藏。
-Wl,-m<emulation>
仿真<emulation>連接器,當前ld所有可用的仿真可以 通過"ld -V"命令獲取。默認值取決於ld的編譯時配置。
-Wl,--sort-common
把全局公共符號按照大小排序后放到適當的輸出節,以防止符號間因為排布限制而出現間隙。
-Wl,-x
刪除所有的本地符號。
-Wl,-X
刪除所有的臨時本地符號。對於大多數目標平台,就是所有的名字以'L'開頭的本地符號。
-Wl,-zcomberloc
組合多個重定位節並重新排布它們,以便讓動態符號可以被緩存。
-Wl,--enable-new-dtags
在ELF中創建新式的"dynamic tags",但在老式的ELF系統上無法識別。
-Wl,--as-needed
移除不必要的符號引用,僅在實際需要的時候才連接,可以生成更高效的代碼。
-Wl,--no-define-common
限制對普通符號的地址分配。該選項允許那些從共享庫中引用的普通符號只在主程序 中被分配地址。這會消除在共享庫中的無用的副本的空間,同時也防止了在有多個指定了搜索路徑的動態模塊在進行運行時符號解析時引起的混亂。
-Wl,--hash-style=gnu
使用gnu風格的符號散列表格式。它的動態鏈接性能比傳統的sysv風格(默認)有 較大提升,但是它生成的可執行程序和庫與舊的Glibc以及動態鏈接器不兼容。

最后說兩個與優化無關的系統環境變量,因為會影響GCC編譯程序的方式,下面兩個是咱中國人比較關心的:

LANG
指定編譯程序使用的字符集,可用於創建寬字符文件、串文字、注釋;默認為英文。[目前只支持日文"C-JIS,C- SJIS,C-EUCJP",不支持中文]
LC_ALL
指定多字節字符的字符分類,主要用於確定字符串的字符邊界以及編譯程序使用何種語言發出診斷消息;默認設置與 LANG相同。中文相關的幾項:"zh_CN.GB2312 , zh_CN.GB18030 , zh_CN.GBK , zh_CN.UTF-8 , zh_TW.BIG5"。

 

 
  https://www.cnblogs.com/LiuYanYGZ/p/5548855.html
 
 
GCG  -o選項用來指定輸出文件,它的用法為:

[infile] -o [outfile]

[infile] 表示輸入文件(也即要處理的文件),它可以是源文件,也可以是匯編文件或者是目標文件;[outfile] 表示輸出文件(也即處理的結果),它可以是預處理文件、目標文件、可執行文件等。

[infile] 和 [outfile] 可以是一個文件,也可以是一組文件:
  • 如果 [infile] 是一組文件,那么就表示有多個輸入文件;
  • 如果 [outfile] 是一組文件,那么就表示有多個輸出文件。

如果不使用 -o 選項,那么將采用默認的輸出文件,例如,把可執行文件作為輸出文件,它的名字默認為 a.out。

GCC -o選項使用舉例

1) 將源文件作為輸入文件,將可執行文件作為輸出文件,也即完整地編譯整個程序:

$ gcc main.c func.c -o app.out

將 main.c 和 func.c 兩個源文件編譯成一個可執行文件,其名字為 app.out。如果不使用 -o 選項,那么將生成名字為 a.out 的可執行文件。

2) 將源文件作為輸入文件,將目標文件作為輸出文件,也即只編譯不鏈接:

$ gcc -c main.c -o a.o

將源文件 main.c 編譯為目標文件 a.o。如果不使用 -o 選項,那么將生成名為 main.o 的目標文件。

3) 將源文件作為輸入文件,將預處理文件作為輸出文件,也即只進行預處理操作:

$ gcc -E main.c -o demo.i

對源文件 main.c 進行預處理操作,並將結果放在 demo.i 文件中。如果不使用 -o 選項,那么將生成名為 main.i 的預處理文件。

4) 將目標文件作為輸入文件,將可執行文件作為輸出文件:

$ gcc -c func.c main.c
$ gcc func.o main.o -o app.out

第一條命令只編譯不鏈接,將生成 func.o 和 main.o 兩個目標文件。第二條命令將生成的兩個目標文件生成最終的可執行文件 app.out。如果不使用 -o 選項,那么將生成名字為 a.out 的可執行文件。
 
 
-g為了調試用的
加個-g 是為了 gdb 用,不然 gdb用不到

-o output_filename,確定輸出文件的名稱為output_filename,同時這個名稱不能和源文件同名。如果不給出這個選項,gcc就給出預設的可執行文件a.out。
一般語法:
gcc filename.c -o filename
上面的意思是如果你不打 -o filename 那么默認就是輸出filemame.out.這個-o就是用來控制輸出文件的。

-c 只編譯不鏈接
 
追問
-c 只編譯不鏈接 
打上-c就是不產生可執行文件嗎?
追答
產生.o文件,就是obj文件,不產生執行文件

 

 
 
 

常用選項

 

-E:只進行預處理,不編譯
-S:只編譯,不匯編
-c:只編譯、匯編,不鏈接
-g:包含調試信息
-I:指定include包含文件的搜索目錄
-o:輸出成指定文件名

高級選項

-v:詳細輸出編譯過程中所采用的每一個選項
-C:預處理時保留注釋信息
-ggdb:在可執行文件中包含可供GDB使用的調試信息
-fverbose-asm:在編譯成匯編語言時,把C變量的名稱作為匯編語言中的注釋
-save-temps:自動輸出預處理文件、匯編文件、對象文件,編譯正常進行
-fsyntax-only:只測試源文件語法是否正確,不會進行任何編譯操作
-ffreestanding:編譯成獨立程序,而非宿主程序

語言標准

-ansi:ANSI標准
-std=c99:C99標准
-std=gnu89:ISO/IEC 9899:1990 以及GNU擴充
-std=gnu99:ISO/IEC 9899:1999 以及GNU擴充
-trigraphs:支持ISO C三字符組

出錯提示

-w:忽略所有警告
-Werror:不區分警告和錯誤,遇到任何警告都停止編譯
-Wall:開啟大部分警告提示
-Wshadow:某語句塊作用域變量與更大作用域的另一變量同名時發出警告(此警告未包含在-Wall選項中,需單獨開啟)
-Wextra:對所有合法但值得懷疑的表達式發出警告

優化選項

-O0:關閉所有優化選項
-O1:第一級別優化,使用此選項可使可執行文件更小、運行更快,並不會增加太多編譯時間,可以簡寫為-O
-O2:第二級別優化,采用了幾乎所有的優化技術,使用此選項會延長編譯時間
-O3:第三級別優化,在-O2的基礎上增加了產生inline函數、使用寄存器等優化技術
-Os:此選項類似於-O2,作用是優化所占用的空間,但不會進行性能優化,常用於生成最終版本


 

 

 

 

GNU CC(簡稱為Gcc)是GNU項目中符合ANSI C標准的編譯系統,能夠編譯用C、C++和Object C等語言編寫的程序。Gcc不僅功能強大,而且可以編譯如C、C++、Object C、Java、Fortran、Pascal、Modula-3和Ada等多種語言,而且Gcc又是一個交叉平台編譯器,它能夠在當前CPU平台上為多種不同體系結構的硬件平台開發軟件,因此尤其適合在嵌入式領域的開發編譯。本章中的示例,除非特別注明,否則均采用Gcc版本為4.0.0。

 

 

GCC入門基礎

表3.6 Gcc所支持后綴名解釋

后 綴 名

所對應的語言

后 綴 名

所對應的語言

.c

C原始程序

.s/.S

匯編語言原始程序

.C/.cc/.cxx

C++原始程序

.h

預處理文件(頭文件)

.m

Objective-C原始程序

.o

目標文件

.i

已經過預處理的C原始程序

.a/.so

編譯后的庫文件

.ii

已經過預處理的C++原始程序

   

如本章開頭提到的,Gcc的編譯流程分為了四個步驟,分別為:

· 預處理(Pre-Processing)

· 編譯(Compiling)

· 匯編(Assembling)

· 鏈接(Linking)

下面就具體來查看一下Gcc是如何完成四個步驟的。

首先,有以下hello.c源代碼

#include<stdio.h>

int main()

{

printf("Hello! This is our embedded world!n");

return 0;

}

(1)預處理階段

在該階段,編譯器將上述代碼中的stdio.h編譯進來,並且用戶可以使用Gcc的選項”-E”進行查看,該選項的作用是讓Gcc在預處理結束后停止編譯過程。

 

注意

Gcc指令的一般格式為:Gcc [選項] 要編譯的文件 [選項] [目標文件]

其中,目標文件可缺省,Gcc默認生成可執行的文件,命為:編譯文件.out

 

[root@localhost Gcc]# gcc -E hello.c  -o hello.i

 

在此處,選項”-o”是指目標文件,由表3.6可知,”.i”文件為已經過預處理的C原始程序。以下列出了hello.i文件的部分內容:

 

typedef int (*__gconv_trans_fct) (struct __gconv_step *,

struct __gconv_step_data *, void *,

__const unsigned char *,

__const unsigned char **,

__const unsigned char *, unsigned char **,

size_t *);

# 2 "hello.c" 2

int main()

{

printf("Hello! This is our embedded world!n");

return 0;

}

 

由此可見,Gcc確實進行了預處理,它把”stdio.h”的內容插入到hello.i文件中。

(2)編譯階段

接下來進行的是編譯階段,在這個階段中,Gcc首先要檢查代碼的規范性、是否有語法錯誤等,以確定代碼的實際要做的工作,在檢查無誤后,Gcc把代碼翻譯成匯編語言。用戶可以使用”-S”選項來進行查看,該選項只進行編譯而不進行匯編,生成匯編代碼。

 

[root@localhost Gcc]# Gcc -S hello.i -o hello.s

 

以下列出了hello.s的內容,可見Gcc已經將其轉化為匯編了,感興趣的讀者可以分析一下這一行簡單的C語言小程序是如何用匯編代碼實現的。

 

.file "hello.c"

.section .rodata

.align 4

.LC0:

.string"Hello! This is our embedded world!"

.text

.globl main

.type main, @function

main:

pushl �p

movl %esp, �p

subl $8, %esp

andl $-16, %esp

movl $0, �x

addl $15, �x

addl $15, �x

shrl $4, �x

sall $4, �x

subl �x, %esp

subl $12, %esp

pushl $.LC0

call puts

addl $16, %esp

movl $0, �x

leave

ret

.size main, .-main

.ident "GCC: (GNU) 4.0.0 20050519 (Red Hat 4.0.0-8)"

.section .note.GNU-stack,"",@progbits

 

(3)匯編階段

匯編階段是把編譯階段生成的”.s”文件轉成目標文件,讀者在此可使用選項”-c”就可看到匯編代碼已轉化為”.o”的二進制目標代碼了。如下所示:

 

[root@localhost Gcc]# gcc -c hello.s -o hello.o

 

(4)鏈接階段

在成功編譯之后,就進入了鏈接階段。在這里涉及到一個重要的概念:函數庫。

讀者可以重新查看這個小程序,在這個程序中並沒有定義”printf”的函數實現,且在預編譯中包含進的”stdio.h”中也只有該函數的聲明,而沒有定義函數的實現,那么,是在哪里實現”printf”函數的呢?最后的答案是:系統把這些函數實現都被做到名為libc.so.6的庫文件中去了,在沒有特別指定時,Gcc會到系統默認的搜索路徑”/usr/lib”下進行查找,也就是鏈接到libc.so.6庫函數中去,這樣就能實現函數”printf”了,而這也就是鏈接的作用。

函數庫一般分為靜態庫和動態庫兩種。靜態庫是指編譯鏈接時,把庫文件的代碼全部加入到可執行文件中,因此生成的文件比較大,但在運行時也就不再需要庫文件了。其后綴名一般為”.a”。動態庫與之相反,在編譯鏈接時並沒有把庫文件的代碼加入到可執行文件中,而是在程序執行時由運行時鏈接文件加載庫,這樣可以節省系統的開銷。動態庫一般后綴名為”.so”,如前面所述的libc.so.6就是動態庫。Gcc在編譯時默認使用動態庫。

完成了鏈接之后,Gcc就可以生成可執行文件,如下所示。

 

[root@localhost Gcc]# gcc hello.o -o hello

 

運行該可執行文件,出現正確的結果如下。

 

[root@localhost Gcc]# ./hello

Hello! This is our embedded world!

Gcc編譯選項分析

Gcc有超過100個的可用選項,主要包括總體選項、告警和出錯選項、優化選項和體系結構相關選項。以下對每一類中最常用的選項進行講解。

(1)總體選項

Gcc的總結選項如表3.7所示,很多在前面的示例中已經有所涉及。

表3.7 Gcc總體選項列表

后綴名

所對應的語言

-c

只是編譯不鏈接,生成目標文件“.o”

-S

只是編譯不匯編,生成匯編代碼

-E

只進行預編譯,不做其他處理

-g

在可執行程序中包含標准調試信息

-o file

把輸出文件輸出到file里

-v

打印出編譯器內部編譯各過程的命令行信息和編譯器的版本

-I dir

在頭文件的搜索路徑列表中添加dir目錄

-L dir

在庫文件的搜索路徑列表中添加dir目錄

-static

鏈接靜態庫

-llibrary

連接名為library的庫文件

 

對於“-c”、“-E”、“-o”、“-S”選項在前一小節中已經講解了其使用方法,在此主要講解另外兩個非常常用的庫依賴選項“-I dir”和“-L dir”。

· “-I dir”

正如上表中所述,“-I dir”選項可以在頭文件的搜索路徑列表中添加dir目錄。由於Linux中頭文件都默認放到了“/usr/include/”目錄下,因此,當用戶希望添加放置在其他位置的頭文件時,就可以通過“-I dir”選項來指定,這樣,Gcc就會到相應的位置查找對應的目錄。

比如在“/root/workplace/Gcc”下有兩個文件:

 

 

#include<my.h>

int main()

{

printf(“Hello!!n”);

return 0;

}

 

#include<stdio.h>

 

這樣,就可在Gcc命令行中加入“-I”選項:

 

[root@localhost Gcc] Gcc hello1.c –I /root/workplace/Gcc/ -o hello1

 

這樣,Gcc就能夠執行出正確結果。

 

小知識

在include語句中,“<>”表示在標准路徑中搜索頭文件,““””表示在本目錄中搜索。故在上例中,可把hello1.c的“#include<my.h>”改為“#include “my.h””,就不需要加上“-I”選項了。

 

· “-L dir”

選項“-L dir”的功能與“-I dir”類似,能夠在庫文件的搜索路徑列表中添加dir目錄。例如有程序hello_sq.c需要用到目錄“/root/workplace/Gcc/lib”下的一個動態庫libsunq.so,則只需鍵入如下命令即可:

 

[root@localhost Gcc] Gcc hello_sq.c –L /root/workplace/Gcc/lib –lsunq –o hello_sq

 

需要注意的是,“-I dir”和“-L dir”都只是指定了路徑,而沒有指定文件,因此不能在路徑中包含文件名。

另外值得詳細解釋一下的是“-l”選項,它指示Gcc去連接庫文件libsunq.so。由於在Linux下的庫文件命名時有一個規定:必須以lib三個字母開頭。因此在用-l選項指定鏈接的庫文件名時可以省去lib三個字母。也就是說Gcc在對”-lsunq”進行處理時,會自動去鏈接名為libsunq.so的文件。

(2)告警和出錯選項

Gcc的告警和出錯選項如表3.8所示。

表3.8 Gcc總體選項列表

選項

含義

-ansi

支持符合ANSI標准的C程序

-pedantic

允許發出ANSI C標准所列的全部警告信息

選項

含義

-pedantic-error

允許發出ANSI C標准所列的全部錯誤信息

-w

關閉所有告警

-Wall

允許發出Gcc提供的所有有用的報警信息

-werror

把所有的告警信息轉化為錯誤信息,並在告警發生時終止編譯過程

 

下面結合實例對這幾個告警和出錯選項進行簡單的講解。

如有以下程序段:

 

#include<stdio.h>

 

void main()

{

long long tmp = 1;

printf(“This is a bad code!n”);

return 0;

}

 

這是一個很糟糕的程序,讀者可以考慮一下有哪些問題?

· “-ansi”

該選項強制Gcc生成標准語法所要求的告警信息,盡管這還並不能保證所有沒有警告的程序都是符合ANSI C標准的。運行結果如下所示:

 

[root@localhost Gcc]# Gcc –ansi warning.c –o warning

warning.c: 在函數“main”中:

warning.c:7 警告:在無返回值的函數中,“return”帶返回值

warning.c:4 警告:“main”的返回類型不是“int”

 

可以看出,該選項並沒有發現”long long”這個無效數據類型的錯誤。

· “-pedantic”

允許發出ANSI C標准所列的全部警告信息,同樣也保證所有沒有警告的程序都是符合ANSI C標准的。其運行結果如下所示:

 

[root@localhost Gcc]# Gcc –pedantic warning.c –o warning

warning.c: 在函數“main”中:

warning.c:5 警告:ISO C90不支持“long long”

warning.c:7 警告:在無返回值的函數中,“return”帶返回值

warning.c:4 警告:“main”的返回類型不是“int”

 

可以看出,使用該選項查看出了”long long”這個無效數據類型的錯誤。

· “-Wall”

允許發出Gcc能夠提供的所有有用的報警信息。該選項的運行結果如下所示:

[root@localhost Gcc]# Gcc –Wall warning.c –o warning

warning.c:4 警告:“main”的返回類型不是“int”

warning.c: 在函數”main”中:

warning.c:7 警告:在無返回值的函數中,”return”帶返回值

warning.c:5 警告:未使用的變量“tmp”

 

使用“-Wall”選項找出了未使用的變量tmp,但它並沒有找出無效數據類型的錯誤。

另外,Gcc還可以利用選項對單獨的常見錯誤分別指定警告,有關具體選項的含義感興趣的讀者可以查看Gcc手冊進行學習。

(3)優化選項

Gcc可以對代碼進行優化,它通過編譯選項“-On”來控制優化代碼的生成,其中n是一個代表優化級別的整數。對於不同版本的Gcc來講,n的取值范圍及其對應的優化效果可能並不完全相同,比較典型的范圍是從0變化到2或3。

不同的優化級別對應不同的優化處理工作。如使用優化選項“-O”主要進行線程跳轉(Thread Jump)和延遲退棧(Deferred Stack Pops)兩種優化。使用優化選項“-O2”除了完成所有“-O1”級別的優化之外,同時還要進行一些額外的調整工作,如處理器指令調度等。選項“-O3”則還包括循環展開和其他一些與處理器特性相關的優化工作。

雖然優化選項可以加速代碼的運行速度,但對於調試而言將是一個很大的挑戰。因為代碼在經過優化之后,原先在源程序中聲明和使用的變量很可能不再使用,控制流也可能會突然跳轉到意外的地方,循環語句也有可能因為循環展開而變得到處都有,所有這些對調試來講都將是一場噩夢。所以筆者建議在調試的時候最好不使用任何優化選項,只有當程序在最終發行的時候才考慮對其進行優化。

(4)體系結構相關選項

Gcc的體系結構相關選項如表3.9所示。

表3.9Gcc體系結構相關選項列表

選項

含義

-mcpu=type

針對不同的CPU使用相應的CPU指令。可選擇的type有i386、i486、pentium及i686等

-mieee-fp

使用IEEE標准進行浮點數的比較

-mno-ieee-fp

不使用IEEE標准進行浮點數的比較

-msoft-float

輸出包含浮點庫調用的目標代碼

-mshort

把int類型作為16位處理,相當於short int

-mrtd

強行將函數參數個數固定的函數用ret NUM返回,節省調用函數的一條指令

 

這些體系結構相關選項在嵌入式的設計中會有較多的應用,讀者需根據不同體系結構將對應的選項進行組合處理。在本書后面涉及到具體實例會有針對性的講解。

Gdb調試器

調試是所有程序員都會面臨的問題。如何提高程序員的調試效率,更好更快地定位程序中的問題從而加快程序開發的進度,是大家共同面對的。就如讀者熟知的Windows下的一些調試工具,如VC自帶的如設置斷點、單步跟蹤等,都受到了廣大用戶的贊賞。那么,在Linux下有什么很好的調試工具呢?

本文所介紹的Gdb調試器是一款GNU開發組織並發布的UNIX/Linux下的程序調試工具。雖然,它沒有圖形化的友好界面,但是它強大的功能也足以與微軟的VC工具等媲美。下面就請跟隨筆者一步步學習Gdb調試器。

Gdb使用流程

首先,筆者給出了一個短小的程序,由此帶領讀者熟悉一下Gdb的使用流程。強烈建議讀者能夠實際動手操作。

首先,打開Linux下的編輯器Vi或者Emacs,編輯如下代碼。(由於為了更好地熟悉Gdb的操作,筆者在此使用Vi編輯,希望讀者能夠參見3.3節中對Vi的介紹,並熟練使用Vi)。

 

 

#include <stdio.h>

int sum(int m);

int main()

{

int i,n=0;

sum(50);

for(i=1; i<=50; i++)

{

n += i;

}

printf("The sum of 1-50 is %d n", n );

 

}

int sum(int m)

{

int i,n=0;

for(i=1; i<=m;i++)

n += i;

printf("The sum of 1-m is %dn", n);

}

 

在保存退出后首先使用Gcc對test.c進行編譯,注意一定要加上選項”-g”,這樣編譯出的可執行代碼中才包含調試信息,否則之后Gdb無法載入該可執行文件。

 

[root@localhost Gdb]# gcc -g test.c -o test

 

雖然這段程序沒有錯誤,但調試完全正確的程序可以更加了解Gdb的使用流程。接下來就啟動Gdb進行調試。注意,Gdb進行調試的是可執行文件,而不是如”.c”的源代碼,因此,需要先通過Gcc編譯生成可執行文件才能用Gdb進行調試。

 

[root@localhost Gdb]# gdb test

GNU Gdb Red Hat Linux (6.3.0.0-1.21rh)

Copyright 2004 Free Software Foundation, Inc.

GDB is free software, covered by the GNU General Public License, and you are

welcome to change it and/or distribute copies of it under certain conditions.

Type "show copying" to see the conditions.

There is absolutely no warranty for GDB. Type "show warranty" for details.

This GDB was configured as "i386-redhat-linux-gnu"...Using host libthread_db library "/lib/libthread_db.so.1".

(gdb)

 

可以看出,在Gdb的啟動畫面中指出了Gdb的版本號、使用的庫文件等信息,接下來就進入了由“(gdb)”開頭的命令行界面了。

(1)查看文件

在Gdb中鍵入”l”(list)就可以查看所載入的文件,如下所示:

 

 

注意

在Gdb的命令中都可使用縮略形式的命令,如“l”代便“list”,“b”代表“breakpoint”,“p”代表“print”等,讀者也可使用“help”命令查看幫助信息。

 

(Gdb) l

1 #include <stdio.h>

2 int sum(int m);

3 int main()

4 {

5 int i,n=0;

6 sum(50);

7 for(i=1; i<=50; i++)

8 {

9 n += i;

10 }

(Gdb) l

11 printf("The sum of 1~50 is %d n", n );

12

13 }

14 int sum(int m)

15 {

16 int i,n=0;

17 for(i=1; i<=m;i++)

18 n += i;

19 printf("The sum of 1~m is = %dn", n);

20 }

 

可以看出,Gdb列出的源代碼中明確地給出了對應的行號,這樣就可以大大地方便代碼的定位。

(2)設置斷點

設置斷點是調試程序中是一個非常重要的手段,它可以使程序到一定位置暫停它的運行。因此,程序員在該位置處可以方便地查看變量的值、堆棧情況等,從而找出代碼的症結所在

在Gdb中設置斷點非常簡單,只需在”b”后加入對應的行號即可(這是最常用的方式,另外還有其他方式設置斷點)。如下所示:

 

(Gdb) b 6

Breakpoint 1 at 0x804846d: file test.c, line 6.

 

要注意的是,在Gdb中利用行號設置斷點是指代碼運行到對應行之前將其停止,如上例中,代碼運行到第五行之前暫停(並沒有運行第五行)。

(3)查看斷點情況

在設置完斷點之后,用戶可以鍵入”info b”來查看設置斷點情況,在Gdb中可以設置多個斷點。

 

(Gdb) info b

Num Type Disp Enb Address What

1 breakpoint keep y 0x0804846d in main at test.c:6

 

(4)運行代碼

接下來就可運行代碼了,Gdb默認從首行開始運行代碼,可鍵入”r”(run)即可(若想從程序中指定行開始運行,可在r后面加上行號)。

 

(Gdb) r

Starting program: /root/workplace/Gdb/test

Reading symbols from shared object read from target memory...done.

Loaded system supplied DSO at 0x5fb000

 

Breakpoint 1, main () at test.c:6

6 sum(50);

 

可以看到,程序運行到斷點處就停止了。

(5)查看變量值

在程序停止運行之后,程序員所要做的工作是查看斷點處的相關變量值。在Gdb中只需鍵入”p”+變量值即可,如下所示:

 

(Gdb) p n

$1 = 0

(Gdb) p i

$2 = 134518440

 

在此處,為什么變量”i”的值為如此奇怪的一個數字呢?原因就在於程序是在斷點設置的對應行之前停止的,那么在此時,並沒有把”i”的數值賦為零,而只是一個隨機的數字。但變量”n”是在第四行賦值的,故在此時已經為零。

 

小技巧

Gdb在顯示變量值時都會在對應值之前加上”$N”標記,它是當前變量值的引用標記,所以以后若想再次引用此變量就可以直接寫作”$N”,而無需寫冗長的變量名。

 

(6)單步運行

單步運行可以使用命令”n”(next)或”s”(step),它們之間的區別在於:若有函數調用的時候,”s”會進入該函數而”n”不會進入該函數。因此,”s”就類似於VC等工具中的”step in”,”n”類似與VC等工具中的”step over”。它們的使用如下所示:

 

(Gdb) n

The sum of 1-m is 1275

7 for(i=1; i<=50; i++)

(Gdb) s

sum (m=50) at test.c:16

16 int i,n=0;

 

可見,使用”n”后,程序顯示函數sum的運行結果並向下執行,而使用”s”后則進入到sum函數之中單步運行。

(7)恢復程序運行

在查看完所需變量及堆棧情況后,就可以使用命令”c”(continue)恢復程序的正常運行了。這時,它會把剩余還未執行的程序執行完,並顯示剩余程序中的執行結果。以下是之前使用”n”命令恢復后的執行結果:

 

(Gdb) c

Continuing.

The sum of 1-50 is :1275

 

Program exited with code 031.

 

可以看出,程序在運行完后退出,之后程序處於“停止狀態”。

 

小知識

在Gdb中,程序的運行狀態有“運行”、“暫停”和“停止”三種,其中“暫停”狀態為程序遇到了斷點或觀察點之類的,程序暫時停止運行,而此時函數的地址、函數參數、函數內的局部變量都會被壓入“棧”(Stack)中。故在這種狀態下可以查看函數的變量值等各種屬性。但在函數處於“停止”狀態之后,“棧”就會自動撤銷,它也就無法查看各種信息了。

Gdb基本命令

Gdb的命令可以通過查看help進行查找,由於Gdb的命令很多,因此Gdb的help將其分成了很多種類(class),用戶可以通過進一步查看相關class找到相應命令。如下所示:

 

(gdb) help

List of classes of commands:

 

aliases -- Aliases of other commands

breakpoints -- Making program stop at certain points

data -- Examining data

files -- Specifying and examining files

internals -- Maintenance commands

Type "help" followed by a class name for a list of commands in that class.

Type "help" followed by command name for full documentation.

Command name abbreViations are allowed if unambiguous.

 

上述列出了Gdb各個分類的命令,注意底部的加粗部分說明其為分類命令。接下來可以具體查找各分類種的命令。如下所示:

 

(gdb) help data

Examining data.

 

List of commands:

 

call -- Call a function in the program

delete display -- Cancel some expressions to be displayed when program stops

delete mem -- Delete memory region

disable display -- Disable some expressions to be displayed when program stops

Type "help" followed by command name for full documentation.

Command name abbreViations are allowed if unambiguous.

 

至此,若用戶想要查找call命令,就可鍵入“help call”。

 

(gdb) help call

Call a function in the program.

The argument is the function name and arguments, in the notation of the

current working language. The result is printed and saved in the value

history, if it is not void.

 

當然,若用戶已知命令名,直接鍵入“help [command]”也是可以的。

Gdb中的命令主要分為以下幾類:工作環境相關命令、設置斷點與恢復命令、源代碼查看命令、查看運行數據相關命令及修改運行參數命令。以下就分別對這幾類的命令進行講解。

1.工作環境相關命令

Gdb中不僅可以調試所運行的程序,而且還可以對程序相關的工作環境進行相應的設定,甚至還可以使用shell中的命令進行相關的操作,其功能極其強大。表3.10所示列出了Gdb常見工作環境相關命令。

表3.10 Gdb工作環境相關命令

命 令 格 式

含義

set args運行時的參數

指定運行時參數,如:set args 2

show args

查看設置好的運行參數

path dir

設定程序的運行路徑

show paths

查看程序的運行路徑

set enVironment var [=value]

設置環境變量

show enVironment [var]

查看環境變量

cd dir

進入到dir目錄,相當於shell中的cd命令

pwd

顯示當前工作目錄

shell command

運行shell的command命令

2.設置斷點與恢復命令

Gdb中設置斷點與恢復的常見命令如表3.11所示。

表3.11 Gdb設置斷點與恢復相關命令

命 令 格 式

含義

bnfo b

查看所設斷點

break 行號或函數名 <條件表達式>

設置斷點

tbreak 行號或函數名 <條件表達式>

設置臨時斷點,到達后被自動刪除

delete [斷點號]

刪除指定斷點,其斷點號為”info b”中的第一欄。若缺省斷點號則刪除所有斷點

disable [斷點號]]

停止指定斷點,使用”info b”仍能查看此斷點。同delete一樣,省斷點號則停止所有斷點

enable [斷點號]

激活指定斷點,即激活被disable停止的斷點

condition [斷點號] <條件表達式>

修改對應斷點的條件

ignore [斷點號]<num>

在程序執行中,忽略對應斷點num次

step

單步恢復程序運行,且進入函數調用

next

單步恢復程序運行,但不進入函數調用

finish

運行程序,直到當前函數完成返回

c

繼續執行函數,直到函數結束或遇到新的斷點

 

由於設置斷點在Gdb的調試中非常重要,所以在此再着重講解一下Gdb中設置斷點的方法。

Gdb中設置斷點有多種方式:其一是按行設置斷點,設置方法在3.5.1節已經指出,在此就不重復了。另外還可以設置函數斷點和條件斷點,在此結合上一小節的代碼,具體介紹后兩種設置斷點的方法。

① 函數斷點

Gdb中按函數設置斷點只需把函數名列在命令”b”之后,如下所示:

 

(gdb) b sum

Breakpoint 1 at 0x80484ba: file test.c, line 16.

(gdb) info b

Num Type Disp Enb Address What

1 breakpoint keep y 0x080484ba in sum at test.c:16

 

要注意的是,此時的斷點實際是在函數的定義處,也就是在16行處(注意第16行還未執行)。

② 條件斷點

Gdb中設置條件斷點的格式為:b 行數或函數名 if 表達式。具體實例如下所示:

 

(gdb) b 8 if i==10

Breakpoint 1 at 0x804848c: file test.c, line 8.

(gdb) info b

Num Type Disp Enb Address What

1 breakpoint keep y 0x0804848c in main at test.c:8

stop only if i == 10

(gdb) r

Starting program: /home/yul/test

The sum of 1-m is 1275

 

Breakpoint 1, main () at test.c:9

9 n += i;

(gdb) p i

$1 = 10

 

可以看到,該例中在第8行(也就是運行完第7行的for循環)設置了一個“i==0”的條件斷點,在程序運行之后可以看出,程序確實在i為10時暫停運行。

3.Gdb中源碼查看相關命令

在Gdb中可以查看源碼以方便其他操作,它的常見相關命令如表3.12所示:

表3.12 Gdb源碼查看相關相關命令

命 令 格 式

含義

list <行號>|<函數名>

查看指定位置代碼

file [文件名]

加載指定文件

forward-search 正則表達式

源代碼前向搜索

reverse-search 正則表達式

源代碼后向搜索

dir dir

停止路徑名

show directories

顯示定義了的源文件搜索路徑

info line

顯示加載到Gdb內存中的代碼

4.Gdb中查看運行數據相關命令

Gdb中查看運行數據是指當程序處於“運行”或“暫停”狀態時,可以查看的變量及表達式的信息,其常見命令如表3.13所示:

表3.13 Gdb查看運行數據相關命令

命 令 格 式

含義

print 表達式|變量

查看程序運行時對應表達式和變量的值

x <n/f/u>

查看內存變量內容。其中n為整數表示顯示內存的長度,f表示顯示的格式,u表示從當前地址往后請求顯示的字節數

display 表達式

設定在單步運行或其他情況中,自動顯示的對應表達式的內容

5.Gdb中修改運行參數相關命令

Gdb還可以修改運行時的參數,並使該變量按照用戶當前輸入的值繼續運行。它的設置方法為:在單步執行的過程中,鍵入命令“set 變量=設定值”。這樣,在此之后,程序就會按照該設定的值運行了。下面,筆者結合上一節的代碼將n的初始值設為4,其代碼如下所示:

 

(Gdb) b 7

Breakpoint 5 at 0x804847a: file test.c, line 7.

(Gdb) r

Starting program: /home/yul/test

The sum of 1-m is 1275

 

Breakpoint 5, main () at test.c:7

7 for(i=1; i<=50; i++)

(Gdb) set n=4

(Gdb) c

Continuing.

The sum of 1-50 is 1279

 

Program exited with code 031.

 

可以看到,最后的運行結果確實比之前的值大了4。

 

 

Gdb的使用切記點:

· 在Gcc編譯選項中一定要加入”-g”。

· 只有在代碼處於“運行”或“暫停”狀態時才能查看變量值。

· 設置斷點后程序在指定行之前停止。

Make工程管理器

到此為止,讀者已經了解了如何在Linux下使用編輯器編寫代碼,如何使用Gcc把代碼編譯成可執行文件,還學習了如何使用Gdb來調試程序,那么,所有的工作看似已經完成了,為什么還需要Make這個工程管理器呢?

所謂工程管理器,顧名思義,是指管理較多的文件的。讀者可以試想一下,有一個上百個文件的代碼構成的項目,如果其中只有一個或少數幾個文件進行了修改,按照之前所學的Gcc編譯工具,就不得不把這所有的文件重新編譯一遍,因為編譯器並不知道哪些文件是最近更新的,而只知道需要包含這些文件才能把源代碼編譯成可執行文件,於是,程序員就不能不再重新輸入數目如此龐大的文件名以完成最后的編譯工作。

但是,請讀者仔細回想一下本書在3.1.2節中所闡述的編譯過程,編譯過程是分為編譯、匯編、鏈接不同階段的,其中編譯階段僅檢查語法錯誤以及函數與變量的聲明是否正確聲明了,在鏈接階段則主要完成是函數鏈接和全局變量的鏈接。因此,那些沒有改動的源代碼根本不需要重新編譯,而只要把它們重新鏈接進去就可以了。所以,人們就希望有一個工程管理器能夠自動識別更新了的文件代碼,同時又不需要重復輸入冗長的命令行,這樣,Make工程管理器也就應運而生了。

實際上,Make工程管理器也就是個“自動編譯管理器”,這里的“自動”是指它能夠根據文件時間戳自動發現更新過的文件而減少編譯的工作量,同時,它通過讀入Makefile文件的內容來執行大量的編譯工作。用戶只需編寫一次簡單的編譯語句就可以了。它大大提高了實際項目的工作效率,而且幾乎所有Linux下的項目編程均會涉及到它,希望讀者能夠認真學習本節內容。

Makefile基本結構

Makefile是Make讀入的惟一配置文件,因此本節的內容實際就是講述Makefile的編寫規則。在一個Makefile中通常包含如下內容:

· 需要由make工具創建的目標體(target),通常是目標文件或可執行文件;

· 要創建的目標體所依賴的文件(dependency_file);

· 創建每個目標體時需要運行的命令(command)。

它的格式為:

 

target: dependency_files

command

 

例如,有兩個文件分別為hello.c和hello.h,創建的目標體為hello.o,執行的命令為gcc編譯指令:gcc –c hello.c,那么,對應的Makefile就可以寫為:

 

#The simplest example

hello.o: hello.c hello.h

gcc –c hello.c –o hello.o

 

接着就可以使用make了。使用make的格式為:make target,這樣make就會自動讀入Makefile(也可以是首字母小寫makefile)並執行對應target的command語句,並會找到相應的依賴文件。如下所示:

 

[root@localhost makefile]# make hello.o

gcc –c hello.c –o hello.o

[root@localhost makefile]# ls

hello.c hello.h hello.o Makefile

 

可以看到,Makefile執行了“hello.o”對應的命令語句,並生成了“hello.o”目標體。

 

 

注意

在Makefile中的每一個command前必須有“Tab”符,否則在運行make命令時會出錯。

Makefile變量

上面示例的Makefile在實際中是幾乎不存在的,因為它過於簡單,僅包含兩個文件和一個命令,在這種情況下完全不必要編寫Makefile而只需在Shell中直接輸入即可,在實際中使用的Makefile往往是包含很多的文件和命令的,這也是Makefile產生的原因。下面就可給出稍微復雜一些的Makefile進行講解:

 

sunq:kang.o yul.o

Gcc kang.o bar.o -o myprog

kang.o : kang.c kang.h head.h

Gcc –Wall –O -g –c kang.c -o kang.o

yul.o : bar.c head.h

Gcc - Wall –O -g –c yul.c -o yul.o

 

在這個Makefile中有三個目標體(target),分別為sunq、kang.o和yul.o,其中第一個目標體的依賴文件就是后兩個目標體。如果用戶使用命令“make sunq”,則make管理器就是找到sunq目標體開始執行。

這時,make會自動檢查相關文件的時間戳。首先,在檢查“kang.o”、“yul.o”和“sunq”三個文件的時間戳之前,它會向下查找那些把“kang.o”或“yul.o”做為目標文件的時間戳。比如,“kang.o”的依賴文件為:“kang.c”、“kang.h”、“head.h”。如果這些文件中任何一個的時間戳比“kang.o”新,則命令“gcc –Wall –O -g –c kang.c -o kang.o”將會執行,從而更新文件“kang.o”。在更新完“kang.o”或“yul.o”之后,make會檢查最初的“kang.o”、“yul.o”和“sunq”三個文件,只要文件“kang.o”或“yul.o”中的任比文件時間戳比“sunq”新,則第二行命令就會被執行。這樣,make就完成了自動檢查時間戳的工作,開始執行編譯工作。這也就是Make工作的基本流程。

接下來,為了進一步簡化編輯和維護Makefile,make允許在Makefile中創建和使用變量。變量是在Makefile中定義的名字,用來代替一個文本字符串,該文本字符串稱為該變量的值。在具體要求下,這些值可以代替目標體、依賴文件、命令以及makefile文件中其它部分。在Makefile中的變量定義有兩種方式:一種是遞歸展開方式,另一種是簡單方式。

遞歸展開方式定義的變量是在引用在該變量時進行替換的,即如果該變量包含了對其他變量的應用,則在引用該變量時一次性將內嵌的變量全部展開,雖然這種類型的變量能夠很好地完成用戶的指令,但是它也有嚴重的缺點,如不能在變量后追加內容(因為語句:CFLAGS = $(CFLAGS) -O在變量擴展過程中可能導致無窮循環)。

為了避免上述問題,簡單擴展型變量的值在定義處展開,並且只展開一次,因此它不包含任何對其它變量的引用,從而消除變量的嵌套引用。

遞歸展開方式的定義格式為:VAR=var

簡單擴展方式的定義格式為:VAR:=var

Make中的變量使用均使用格式為:$(VAR)

 

 

注意

變量名是不包括“:”、“#”、“=”結尾空格的任何字符串。同時,變量名中包含字母、數字以及下划線以外的情況應盡量避免,因為它們可能在將來被賦予特別的含義。

變量名是大小寫敏感的,例如變量名“foo”、“FOO”、和“Foo”代表不同的變量。

推薦在makefile內部使用小寫字母作為變量名,預留大寫字母作為控制隱含規則參數或用戶重載命令選項參數的變量名。

 

下面給出了上例中用變量替換修改后的Makefile,這里用OBJS代替kang.o和yul.o,用CC代替Gcc,用CFLAGS代替“-Wall -O –g”。這樣在以后修改時,就可以只修改變量定義,而不需要修改下面的定義實體,從而大大簡化了Makefile維護的工作量。

經變量替換后的Makefile如下所示:

 

OBJS = kang.o yul.o

CC = Gcc

CFLAGS = -Wall -O -g

sunq : $(OBJS)

$(CC) $(OBJS) -o sunq

kang.o : kang.c kang.h

$(CC) $(CFLAGS) -c kang.c -o kang.o

yul.o : yul.c yul.h

$(CC) $(CFLAGS) -c yul.c -o yul.o

 

可以看到,此處變量是以遞歸展開方式定義的。

Makefile中的變量分為用戶自定義變量、預定義變量、自動變量及環境變量。如上例中的OBJS就是用戶自定義變量,自定義變量的值由用戶自行設定,而預定義變量和自動變量為通常在Makefile都會出現的變量,其中部分有默認值,也就是常見的設定值,當然用戶可以對其進行修改。

預定義變量包含了常見編譯器、匯編器的名稱及其編譯選項。下表3.14列出了Makefile中常見預定義變量及其部分默認值。

表3.14 Makefile中常見預定義變量

命 令 格 式

含義

AR

庫文件維護程序的名稱,默認值為ar

AS

匯編程序的名稱,默認值為as

CC

C編譯器的名稱,默認值為cc

CPP

C預編譯器的名稱,默認值為$(CC) –E

CXX

C++編譯器的名稱,默認值為g++

FC

FORTRAN編譯器的名稱,默認值為f77

RM

文件刪除程序的名稱,默認值為rm –f

ARFLAGS

庫文件維護程序的選項,無默認值

ASFLAGS

匯編程序的選項,無默認值

CFLAGS

C編譯器的選項,無默認值

CPPFLAGS

C預編譯的選項,無默認值

CXXFLAGS

C++編譯器的選項,無默認值

FFLAGS

FORTRAN編譯器的選項,無默認值

 

可以看出,上例中的CC和CFLAGS是預定義變量,其中由於CC沒有采用默認值,因此,需要把“CC=Gcc”明確列出來。

由於常見的Gcc編譯語句中通常包含了目標文件和依賴文件,而這些文件在Makefile文件中目標體的一行已經有所體現,因此,為了進一步簡化Makefile的編寫,就引入了自動變量。自動變量通常可以代表編譯語句中出現目標文件和依賴文件等,並且具有本地含義(即下一語句中出現的相同變量代表的是下一語句的目標文件和依賴文件)。下表3.15列出了Makefile中常見自動變量。

表3.15Makefile中常見自動變量

命令格式

含義

$*

不包含擴展名的目標文件名稱

$+

所有的依賴文件,以空格分開,並以出現的先后為序,可能包含重復的依賴文件

$<

第一個依賴文件的名稱

$?

所有時間戳比目標文件晚的依賴文件,並以空格分開

命令格式

含義

$@

目標文件的完整名稱

$^

所有不重復的依賴文件,以空格分開

$%

如果目標是歸檔成員,則該變量表示目標的歸檔成員名稱

 

自動變量的書寫比較難記,但是在熟練了之后會非常的方便,請讀者結合下例中的自動變量改寫的Makefile進行記憶。

 

OBJS = kang.o yul.o

CC = Gcc

CFLAGS = -Wall -O -g

sunq : $(OBJS)

$(CC) $^ -o $@

kang.o : kang.c kang.h

$(CC) $(CFLAGS) -c $< -o $@

yul.o : yul.c yul.h

$(CC) $(CFLAGS) -c $< -o $@

 

另外,在Makefile中還可以使用環境變量。使用環境變量的方法相對比較簡單,make在啟動時會自動讀取系統當前已經定義了的環境變量,並且會創建與之具有相同名稱和數值的變量。但是,如果用戶在Makefile中定義了相同名稱的變量,那么用戶自定義變量將會覆蓋同名的環境變量。

Makefile規則

Makefile的規則是Make進行處理的依據,它包括了目標體、依賴文件及其之間的命令語句。一般的,Makefile中的一條語句就是一個規則。在上面的例子中,都顯示地指出了Makefile中的規則關系,如“$(CC) $(CFLAGS) -c $< -o $@”,但為了簡化Makefile的編寫,make還定義了隱式規則和模式規則,下面就分別對其進行講解。

1.隱式規則

隱含規則能夠告訴make怎樣使用傳統的技術完成任務,這樣,當用戶使用它們時就不必詳細指定編譯的具體細節,而只需把目標文件列出即可。Make會自動搜索隱式規則目錄來確定如何生成目標文件。如上例就可以寫成:

 

OBJS = kang.o yul.o

CC = Gcc

CFLAGS = -Wall -O -g

sunq : $(OBJS)

$(CC) $^ -o $@

 

為什么可以省略后兩句呢?因為Make的隱式規則指出:所有“.o”文件都可自動由“.c”文件使用命令“$(CC) $(CPPFLAGS) $(CFLAGS) -c file.c –o file.o”生成。這樣“kang.o”和“yul.o”就會分別調用“$(CC) $(CFLAGS) -c kang.c -o kang.o”和“$(CC) $(CFLAGS) -c yul.c -o yul.o”生成。

 

 

注意

在隱式規則只能查找到相同文件名的不同后綴名文件,如”kang.o”文件必須由”kang.c”文件生成。

 

下表3.16給出了常見的隱式規則目錄:

表3.16 Makefile中常見隱式規則目錄

對應語言后綴名

規則

C編譯:.c變為.o

$(CC) –c $(CPPFLAGS) $(CFLAGS)

C++編譯:.cc或.C變為.o

$(CXX) -c $(CPPFLAGS) $(CXXFLAGS)

Pascal編譯:.p變為.o

$(PC) -c $(PFLAGS)

Fortran編譯:.r變為-o

$(FC) -c $(FFLAGS)

2.模式規則

模式規則是用來定義相同處理規則的多個文件的。它不同於隱式規則,隱式規則僅僅能夠用make默認的變量來進行操作,而模式規則還能引入用戶自定義變量,為多個文件建立相同的規則,從而簡化Makefile的編寫。

模式規則的格式類似於普通規則,這個規則中的相關文件前必須用“%”標明。使用模式規則修改后的Makefile的編寫如下:

 

OBJS = kang.o yul.o

CC = Gcc

CFLAGS = -Wall -O -g

sunq : $(OBJS)

$(CC) $^ -o $@

%.o : %.c

$(CC) $(CFLAGS) -c $< -o $@

Make使用

使用make管理器非常簡單,只需在make命令的后面鍵入目標名即可建立指定的目標,如果直接運行make,則建立Makefile中的第一個目標。

此外make還有豐富的命令行選項,可以完成各種不同的功能。下表3.17列出了常用的make命令行選項。

表3.17 make的命令行選項

命令格式

含 義

-C dir

讀入指定目錄下的Makefile

-f file

讀入當前目錄下的file文件作為Makefile

命令格式

含 義

-i

忽略所有的命令執行錯誤

-I dir

指定被包含的Makefile所在目錄

-n

只打印要執行的命令,但不執行這些命令

-p

顯示make變量數據庫和隱含規則

-s

在執行命令時不顯示命令

-w

如果make在執行過程中改變目錄,則打印當前目錄名

使用autotools

在上一小節,讀者已經了解到了make項目管理器的強大功能。的確,Makefile可以幫助make完成它的使命,但要承認的是,編寫Makefile確實不是一件輕松的事,尤其對於一個較大的項目而言更是如此。那么,有沒有一種輕松的手段生成Makefile而同時又能讓用戶享受make的優越性呢?本節要講的autotools系列工具正是為此而設的,它只需用戶輸入簡單的目標文件、依賴文件、文件目錄等就可以輕松地生成Makefile了,這無疑是廣大用戶的所希望的。另外,這些工具還可以完成系統配置信息的收集,從而可以方便地處理各種移植性的問題。也正是基於此,現在Linux上的軟件開發一般都用autotools來制作Makefile,讀者在后面的講述中就會了解到。

autotools使用流程

正如前面所言,autotools是系列工具,讀者首先要確認系統是否裝了以下工具(可以用which命令進行查看)。

· aclocal

· autoscan

· autoconf

· autoheader

· automake

使用autotools主要就是利用各個工具的腳本文件以生成最后的Makefile。其總體流程是這樣的:

· 使用aclocal生成一個“aclocal.m4”文件,該文件主要處理本地的宏定義;

· 改寫“configure.scan”文件,並將其重命名為“configure.in”,並使用autoconf文件生成configure文件。

接下來,筆者將通過一個簡單的hello.c例子帶領讀者熟悉autotools生成makefile的過程,由於在這過程中有涉及到較多的腳本文件,為了更清楚地了解相互之間的關系,強烈建議讀者實際動手操作以體會其整個過程。

1.autoscan

它會在給定目錄及其子目錄樹中檢查源文件,若沒有給出目錄,就在當前目錄及其子目錄樹中進行檢查。它會搜索源文件以尋找一般的移植性問題並創建一個文件“configure.scan”,該文件就是接下來autoconf要用到的“configure.in”原型。如下所示:

 

[root@localhost automake]# autoscan

autom4te: configure.ac: no such file or directory

autoscan: /usr/bin/autom4te failed with exit status: 1

[root@localhost automake]# ls

autoscan.log configure.scan hello.c

 

如上所示,autoscan首先會嘗試去讀入“configure.ac”(同configure.in的配置文件)文件,此時還沒有創建該配置文件,於是它會自動生成一個“configure.in”的原型文件“configure.scan”。

2.autoconf

configure.in是autoconf的腳本配置文件,它的原型文件“configure.scan”如下所示:

 

# -*- Autoconf -*-

# Process this file with autoconf to produce a configure script.

AC_PREREQ(2.59)

#The next one is modified by sunq

#AC_INIT(FULL-PACKAGE-NAME,VERSION,BUG-REPORT-ADDRESS)

AC_INIT(hello,1.0)

# The next one is added by sunq

AM_INIT_AUTOMAKE(hello,1.0)

AC_CONFIG_SRCDIR([hello.c])

AC_CONFIG_HEADER([config.h])

# Checks for programs.

AC_PROG_CC

# Checks for libraries.

# Checks for header files.

# Checks for typedefs, structures, and compiler characteristics.

# Checks for library functions.

AC_CONFIG_FILES([Makefile])

AC_OUTPUT

 

下面對這個腳本文件進行解釋:

· 以“#”號開始的行為注釋。

· AC_PREREQ宏聲明本文件要求的autoconf版本,如本例使用的版本2.59。

· AC_INIT宏用來定義軟件的名稱和版本等信息,在本例中省略了BUG-REPORT-ADDRESS,一般為作者的e-mail。

· AM_INIT_AUTOMAKE是筆者另加的,它是automake所必備的宏,也同前面一樣,PACKAGE是所要產生軟件套件的名稱,VERSION是版本編號。

· AC_CONFIG_SRCDIR宏用來偵測所指定的源碼文件是否存在,來確定源碼目錄的有

效性。在此處為當前目錄下的hello.c。

· AC_CONFIG_HEADER宏用於生成config.h文件,以便autoheader使用。

· AC_CONFIG_FILES宏用於生成相應的Makefile文件。

· 中間的注釋間可以添加分別用戶測試程序、測試函數庫、測試頭文件等宏定義。

接下來首先運行aclocal,生成一個“aclocal.m4”文件,該文件主要處理本地的宏定義。如下所示:

 

[root@localhost automake]# aclocal

 

再接着運行autoconf,生成“configure”可執行文件。如下所示:

 

[root@localhost automake]# autoconf

[root@localhost automake]# ls

aclocal.m4 autom4te.cache autoscan.log configure configure.in hello.c

3.autoheader

接着使用autoheader命令,它負責生成config.h.in文件。該工具通常會從“acconfig.h”文件中復制用戶附加的符號定義,因此此處沒有附加符號定義,所以不需要創建“acconfig.h”文件。如下所示:

 

[root@localhost automake]# autoheader

4.automake

這一步是創建Makefile很重要的一步,automake要用的腳本配置文件是Makefile.am,用戶需要自己創建相應的文件。之后,automake工具轉換成Makefile.in。在該例中,筆者創建的文件為Makefile.am如下所示:

 

AUTOMAKE_OPTIONS=foreign

bin_PROGRAMS= hello

hello_SOURCES= hello.c

 

下面對該腳本文件的對應項進行解釋。

· 其中的AUTOMAKE_OPTIONS為設置automake的選項。由於GNU(在第1章中已經有所介紹)對自己發布的軟件有嚴格的規范,比如必須附帶許可證聲明文件COPYING等,否則automake執行時會報錯。automake提供了三種軟件等級:foreign、gnu和gnits,讓用戶選擇采用,默認等級為gnu。在本例使用foreign等級,它只檢測必須的文件。

· bin_PROGRAMS定義要產生的執行文件名。如果要產生多個執行文件,每個文件名用空格隔開。

· hello_SOURCES定義“hello”這個執行程序所需要的原始文件。如果”hello”這個程序是由多個原始文件所產生的,則必須把它所用到的所有原始文件都列出來,並用空格隔開。例如:若目標體“hello”需要“hello.c”、“sunq.c”、“hello.h”三個依賴文件,則定義hello_SOURCES=hello.c sunq.c hello.h。要注意的是,如果要定義多個執行文件,則對每個執行程序都要定義相應的file_SOURCES。

接下來可以使用automake對其生成“configure.in”文件,在這里使用選項“—adding-missing”可以讓automake自動添加有一些必需的腳本文件。如下所示:

 

[root@localhost automake]# automake --add-missing

configure.in: installing './install-sh'

configure.in: installing './missing'

Makefile.am: installing 'depcomp'

[root@localhost automake]# ls

aclocal.m4 autoscan.log configure.in hello.c Makefile.am missing

autom4te.cache configure depcomp install-sh Makefile.in config.h.in

 

可以看到,在automake之后就可以生成configure.in文件。

5.運行configure

在這一步中,通過運行自動配置設置文件configure,把Makefile.in變成了最終的Makefile。如下所示:

 

[root@localhost automake]# ./configure

checking for a BSD-compatible install... /usr/bin/install -c

checking whether build enVironment is sane... yes

checking for gawk... gawk

checking whether make sets $(MAKE)... yes

checking for Gcc... Gcc

checking for C compiler default output file name... a.out

checking whether the C compiler works... yes

checking whether we are cross compiling... no

checking for suffix of executables...

checking for suffix of object files... o

checking whether we are using the GNU C compiler... yes

checking whether Gcc accepts -g... yes

checking for Gcc option to accept ANSI C... none needed

checking for style of include used by make... GNU

checking dependency style of Gcc... Gcc3

configure: creating ./config.status

config.status: creating Makefile

config.status: executing depfiles commands

可以看到,在運行configure時收集了系統的信息,用戶可以在configure命令中對其進行方便地配置。在./configure的自定義參數有兩種,一種是開關式(--enable-XXX或--disable-XXX),另一種是開放式,即后面要填入一串字符(--with-XXX=yyyy)參數。讀者可以自行嘗試其使用方法。另外,讀者可以查看同一目錄下的”config.log”文件,以方便調試之用。

到此為止,makefile就可以自動生成了。回憶整個步驟,用戶不再需要定制不同的規則,而只需要輸入簡單的文件及目錄名即可,這樣就大大方便了用戶的使用。下面的圖3.9總結了上述過程:

 

圖3.9 autotools生成Makefile流程圖

使用autotools所生成的Makefile

autotools生成的Makefile除具有普通的編譯功能外,還具有以下主要功能(感興趣的讀者可以查看這個簡單的hello.c程序的makefile):

1.make

鍵入make默認執行”make all”命令,即目標體為all,其執行情況如下所示:

 

[root@localhost automake]# make

if Gcc -DPACKAGE_NAME="" -DPACKAGE_TARNAME="" -DPACKAGE_VERSION="" -DPACKAGE_STRING="" -DPACKAGE_BUGREPORT="" -DPACKAGE="hello" -DVERSION="1.0" -I. -I. -g -O2 -MT hello.o -MD -MP -MF ".deps/hello.Tpo" -c -o hello.o hello.c;

then mv -f ".deps/hello.Tpo" ".deps/hello.Po"; else rm -f ".deps/hello.Tpo"; exit 1; fi

Gcc -g -O2 -o hello hello.o

此時在本目錄下就生成了可執行文件“hello”,運行“./hello”能出現正常結果,如下所示:

 

[root@localhost automake]# ./hello

Hello!Autoconf!

2.make install

此時,會把該程序安裝到系統目錄中去,如下所示:

 

[root@localhost automake]# make install

if Gcc -DPACKAGE_NAME="" -DPACKAGE_TARNAME="" -DPACKAGE_VERSION="" -DPACKAGE_STRING="" -DPACKAGE_BUGREPORT="" -DPACKAGE="hello" -DVERSION="1.0" -I. -I. -g -O2 -MT hello.o -MD -MP -MF ".deps/hello.Tpo" -c -o hello.o hello.c;

then mv -f ".deps/hello.Tpo" ".deps/hello.Po"; else rm -f ".deps/hello.Tpo"; exit 1; fi

Gcc -g -O2 -o hello hello.o

make[1]: Entering directory '/root/workplace/automake'

test -z "/usr/local/bin" || mkdir -p -- "/usr/local/bin"

/usr/bin/install -c 'hello' '/usr/local/bin/hello'

make[1]: Nothing to be done for 'install-data-am'.

make[1]: LeaVing directory '/root/workplace/automake'

 

此時,若直接運行hello,也能出現正確結果,如下所示:

 

[root@localhost automake]# hello

Hello!Autoconf!

3.make clean

此時,make會清除之前所編譯的可執行文件及目標文件(object file, *.o),如下所示:

 

[root@localhost automake]# make clean

test -z "hello" || rm -f hello

rm -f *.o

4.make dist

此時,make將程序和相關的文檔打包為一個壓縮文檔以供發布,如下所示:

 

[root@localhost automake]# make dist

[root@localhost automake]# ls hello-1.0-tar.gz

hello-1.0-tar.gz

 

可見該命令生成了一個hello-1.0-tar.gz的壓縮文件。

由上面的講述讀者不難看出,autotools確實是軟件維護與發布的必備工具,也鑒於此,如今GUN的軟件一般都是由automake來制作的。

 

 

想一想

對於automake制作的這類軟件,應如何安裝呢?

Vi使用練習

1.實驗目的

通過指定指令的Vi操作練習,使讀者能夠熟練使用Vi中的常見操作,並且熟悉Vi的三種模式,如果讀者能夠熟練掌握實驗內容中所要求的內容,則表明對Vi的操作已經很熟練了。

2.實驗內容

(1)在“/root”目錄下建一個名為“/Vi”的目錄。

(2)進入“/Vi”目錄。

(3)將文件“/etc/inittab”復制到“/Vi”目錄下。

(4)使用Vi打開“/Vi”目錄下的inittab。

(5)設定行號,指出設定initdefault(類似於“id:5:initdefault”)的所在行號。

(6)將光標移到該行。

(7)復制該行內容。

(8)將光標移到最后一行行首。

(9)粘貼復制行的內容。

(10)撤銷第9步的動作。

(11)將光標移動到最后一行的行尾。

(12)粘貼復制行的內容。

(13)光標移到“si::sysinit:/etc/rc.d/rc.sysinit”。

(14)刪除該行。

(15)存盤但不退出。

(16)將光標移到首行。

(17)插入模式下輸入“Hello,this is Vi world!”。

(18)返回命令行模式。

(19)向下查找字符串“0:wait”。

(20)再向上查找字符串“halt”。

(21)強制退出Vi,不存盤。

分別指出每個命令處於何種模式下?

3.實驗步驟

(1)mkdir /root/Vi

(2)cd /root/Vi

(3)cp /etc/inittab ./

(4)Vi ./inittab

(5):set nu(底行模式)

(6)17<enter>(命令行模式)

(7)yy

(8)G

(9)p

(10)u

(11)$

(12)p

(13)21G

(14)dd

(15):w(底行模式)

(16)1G

(17)i 並輸入“Hello,this is Vi world!”(插入模式)

(18)Esc

(19)/0:wait(命令行模式)

(20)?halt

(21):q!(底行模式)

4.實驗結果

該實驗最后的結果只對“/root/inittab”增加了一行復制的內容:“id:5:initdefault”。

用Gdb調試有問題的程序

1.實驗目的

通過調試一個有問題的程序,使讀者進一步熟練使用Vi操作,而且熟練掌握Gcc編譯命令及Gdb的調試命令,通過對有問題程序的跟蹤調試,進一步提高發現問題和解決問題的能力。這是一個很小的程序,只有35行,希望讀者認真調試。

2.實驗內容

(1)使用Vi編輯器,將以下代碼輸入到名為greet.c的文件中。此代碼的原意為輸出倒序main函數中定義的字符串,但結果顯示沒有輸出。代碼如下所示:

 

#include <stdio.h>

int display1(char *string);

int display2(char *string);

 

int main ()

{

char string[] = "Embedded Linux";

display1 (string);

display2 (string);

}

int display1 (char *string)

{

printf ("The original string is %s n", string);

}

int display2 (char *string1)

{

char *string2;

int size,i;

size = strlen (string1);

string2 = (char *) malloc (size + 1);

for (i = 0; i < size; i++)

string2[size - i] = string1[i];

string2[size+1] = ' ';

printf("The string afterward is %sn",string2);

}

 

(2)使用Gcc編譯這段代碼,注意要加上“-g”選項以方便之后的調試。

(3)運行生成的可執行文件,觀察運行結果。

(4)使用Gdb調試程序,通過設置斷點、單步跟蹤,一步步找出錯誤所在。

(5)糾正錯誤,更改源程序並得到正確的結果。

3.實驗步驟

(1)在工作目錄上新建文件greet.c,並用Vi啟動:vi greet.c。

(2)在Vi中輸入以上代碼。

(3)在Vi中保存並退出:wq。

(4)用Gcc編譯:gcc -g greet.c -o greet。

(5)運行greet:./greet,輸出為:

 

The original string is Embedded Linux

The string afterward is

 

可見,該程序沒有能夠倒序輸出。

(6)啟動Gdb調試:gdb greet。

(7)查看源代碼,使用命令“l”。

(8)在30行(for循環處)設置斷點,使用命令“b 30”。

(9)在33行(printf函數處)設置斷點,使用命令“b 33”。

(10)查看斷點設置情況,使用命令“info b”。

(11)運行代碼,使用命令“r”。

(12)單步運行代碼,使用命令“n”。

(13)查看暫停點變量值,使用命令“p string2[size - i]”。

(14)繼續單步運行代碼數次,並使用命令查看,發現string2[size-1]的值正確。

(15)繼續程序的運行,使用命令“c”。

(16)程序在printf前停止運行,此時依次查看string2[0]、string2[1]…,發現string[0]沒有被正確賦值,而后面的復制都是正確的,這時,定位程序第31行,發現程序運行結果錯誤的原因在於“size-1”。由於i只能增到“size-1”,這樣string2[0]就永遠不能被賦值而保持NULL,故輸不出任何結果。

(17)退出Gdb,使用命令q。

(18)重新編輯greet.c,把其中的“string2[size - i] = string1[i]”改為“string2[size – i - 1] = string1[i];”即可。

(19)使用Gcc重新編譯:gcc -g greet.c -o greet。

(20)查看運行結果:./greet

 

The original string is Embedded Linux

The string afterward is xuniL deddedbmE

 

這時,輸入結果正確。

4.實驗結果

將原來有錯的程序經過Gdb調試,找出問題所在,並修改源代碼,輸出正確的倒序顯示字符串的結果。

編寫包含多文件的Makefile

1.實驗目的

通過對包含多文件的Makefile的編寫,熟悉各種形式的Makefile,並且進一步加深對Makefile中用戶自定義變量、自動變量及預定義變量的理解。

2.實驗過程

(1)用Vi在同一目錄下編輯兩個簡單的Hello程序,如下所示:

 

#hello.c

#include "hello.h"

int main()

{

printf("Hello everyone!n");

}

#hello.h

#include <stdio.h>

 

(2)仍在同一目錄下用Vi編輯Makefile,且不使用變量替換,用一個目標體實現(即直接將hello.c和hello.h編譯成hello目標體)。然后用make驗證所編寫的Makefile是否正確。

(3)將上述Makefile使用變量替換實現。同樣用make驗證所編寫的Makefile是否正確

(4)用編輯另一Makefile,取名為Makefile1,不使用變量替換,但用兩個目標體實現(也就是首先將hello.c和hello.h編譯為hello.o,再將hello.o編譯為hello),再用make的”-f”選項驗證這個Makefile1的正確性。

(5)將上述Makefile1使用變量替換實現。

3.實驗步驟

(1)用Vi打開上述兩個代碼文件“hello.c”和“hello.h”。

(2)在shell命令行中用Gcc嘗試編譯,使用命令:”Gcc hello.c –o hello”,並運行hello可執行文件查看結果。

(3)刪除此次編譯的可執行文件:rm hello。

(4)用Vi編輯Makefile,如下所示:

 

hello:hello.c hello.h

Gcc hello.c -o hello

 

(5)退出保存,在shell中鍵入:make,查看結果。

(6)再次用Vi打開Makefile,用變量進行替換,如下所示:

 

OBJS :=hello.o

CC :=Gcc

hello:$(OBJS)

$(CC) $^ -o $@

 

(7)退出保存,在shell中鍵入:make,查看結果。

(8)用Vi編輯Makefile1,如下所示:

 

hello:hello.o

Gcc hello.o -o hello

hello.o:hello.c hello.h

Gcc -c hello.c -o hello.o

 

(9)退出保存,在shell中鍵入:make -f Makefile1,查看結果。

(10)再次用Vi編輯Makefile1,如下所示:

 

OBJS1 :=hello.o

OBJS2 :=hello.c hello.h

CC :=Gcc

hello:$(OBJS1)

$(CC) $^ -o $@

$(OBJS1):$(OBJS2)

$(CC) -c $< -o $@

 

在這里請注意區別“$^”和“$<”。

(11)退出保存,在shell中鍵入:make -f Makefile1,查看結果

4.實驗結果

各種不同形式的makefile都能完成其正確的功能。

使用autotools生成包含多文件的Makefile

1.實驗目的

通過使用autotools生成包含多文件的Makefile,進一步掌握autotools的正確使用方法。同時,掌握Linux下安裝軟件的常用方法。

2.實驗過程

(1)在原目錄下新建文件夾auto。

(2)利用上例的兩個代碼文件“hello.c”和“hello.h”,並將它們復制到該目錄下。

(3)使用autoscan生成configure.scan。

(4)編輯configure.scan,修改相關內容,並將其重命名為configure.in。

(5)使用aclocal生成aclocal.m4。

(6)使用autoconf生成configure。

(7)使用autoheader生成config.in.h。

(8)編輯Makefile.am。

(9)使用automake生成Makefile.in。

(10)使用configure生成Makefile。

(11)使用make生成hello可執行文件,並在當前目錄下運行hello查看結果。

(12)使用make install將hello安裝到系統目錄下,並運行,查看結果。

(13)使用make dist生成hello壓縮包。

(14)解壓hello壓縮包。

(15)進入解壓目錄。

(16)在該目錄下安裝hello軟件。

3.實驗步驟

(1)mkdir ./auto。

(2)cp hello.* ./auto(假定原先在“hello.c”文件目錄下)。

(3)命令:autoscan。

(4)使用Vi編輯configure.scan為:

 

# -*- Autoconf -*-

# Process this file with autoconf to produce a configure script.

 

AC_PREREQ(2.59)

AC_INIT(hello, 1.0)

AM_INIT_AUTOMAKE(hello,1.0)

AC_CONFIG_SRCDIR([hello.h])

AC_CONFIG_HEADER([config.h])

# Checks for programs.

AC_PROG_CC

# Checks for libraries.

# Checks for header files.

# Checks for typedefs, structures, and compiler characteristics.

# Checks for library functions.

AC_OUTPUT(Makefile)

 

(5)保存退出,並重命名為configure.in。

(6)運行:aclocal。

(7)運行:autoconf,並用ls查看是否生成了configure可執行文件。

(8)運行:autoheader。

(9)用Vi編輯Makefile.am文件為:

 

AUTOMAKE_OPTIONS=foreign

bin_PROGRAMS=hello

hello_SOURCES=hello.c hello.h

 

(10)運行:automake。

(11)運行:./configure。

(12)運行:make。

(13)運行:./hello,查看結果是否正確。

(14)運行:make install。

(15)運行:hello,查看結果是否正確。

(16)運行:make dist。

(17)在當前目錄下解壓hello-1.0.tar.gz:tar –zxvf hello-1.0.tar.gz。

(18)進入解壓目錄:cd ./hello-1.0。

(19)下面開始Linux下常見的安裝軟件步驟:./configure。

(20)運行:make。

(21)運行:./hello(在正常安裝時這一步可省略)。

(22)運行:make install。

(23)運行:hello,查看結果是否正確。

4.實驗結果

能夠正確使用autotools生成Makefile,並且能夠安裝成功短小的Hello軟件。

 

 

在shell上通過man gcc命令可以查看manpage文檔。

自己根據平時所學分享一些常用的命令使用,請大家批評指正!

1. gcc -E sourceFile.c
-E,只預編譯。直接輸出預編譯結果。


-E參數,進行預編譯時,將輸出信息,將程序所包含的頭文件,函數,宏定義等,進行擴展。

 

2. gcc -S sourceFile.c 
-S,只執行到源代碼到匯編代碼的轉換,輸出匯編代碼。


3. gcc -c source_file.c
-c,只執行到編譯,輸出目標文件。


匯編知識將在以后的博客推出。


4. gcc -c sourceFile.c -o outputFileName
-o, 指定輸出文件名 該參數可以省略。默認下(gcc sourceFile.c):生成名為a.out的可執行文件。
-c:生成名為sourceFile.o的目標文件。(進行編譯,不鏈接)

 

5. gcc -g sourceFile.c 
-g,生成供調試用的可執行文件,可以在gdb中運行。
用strip命令重新將debug信息清除。這是會發現生成的文件比正常編譯的輸出小。

這是因為strip把原先正常編譯中的一些額外信息(如函數名之類)去除。

 

6. gcc -s sourceFile.c
-s效果與strip相同。


7. gcc -O source_file.c
-O(大寫的字母O),編譯器對代碼進行自動優化編譯,輸出效率更高的可執行文件。
-O 后面還可以跟上數字指定優化級別,如:
gcc -O2 source_file.c
一般可選擇2;3會有一定風險。(這里不做演示)


8. gcc -Wall source_file.c
-W,在編譯中開啟一些額外的警告(warning)信息。-Wall,打開所有的警告信息。


9. gcc source_file.c -L/XXX/lib -llibXXX.a -I/XXX/include
-l, 指定所使用到的函數庫,鏈接器將鏈接名為libxxx.a(后綴.a表示靜態庫)的函數庫。
-L,指定函數庫所在的文件夾,鏈接器會搜索/XXX/lib(一般可以指定路徑)文件夾。
-I, 指定頭文件所在的文件夾,預編譯器會搜索/XXX/include文件夾。

10.gcc -D MAX_SIZE=value sourceFile.c 
預定義名為MAX_SIZE ,值為value的宏。

 

若不指定MAX_SIZE的值,默認為1

 
來源:CSDN
原文:https://blog.csdn.net/m1223853767/article/details/79464729
 

一、C/C++文件的編譯過程:
先來看一下gcc的使用方法和常用選項
提示:gcc --help

Ⅰ、使用方法:
gcc [選項] 文件名
Ⅱ、常用選項:
選項 含義
-v 查看gcc編譯器的版本,顯示gcc執行時的詳細過程
-o <file> Place the output into <file>;指定輸出文件名為file,這個名稱不能跟源文件名同名
-E Preprocess only; do not compile, assemble or link;只預處理,不會編譯、匯編、鏈接
-S Compile only; do not assemble or link;只編譯,不會匯編、鏈接
-c Compile and assemble, but do not link; 編譯和匯編,不會鏈接
      一個C/C++文件要經過預處理(preprocessing)、編譯(compilation)、匯編(assembly)、和連接(linking)才能變成可執行文件。
以下列程序為例,追層來分析編譯過程。
hello.c:

#include <stdio.h>

#define MAX 20
#define MIN 10

#define _DEBUG
#define SetBit(x) (1<<x)

int main(int argc, char* argv[])
{
printf("Hello World \n");
printf("MAX = %d,MIN = %d,MAX + MIN = %d\n",MAX,MIN,MAX + MIN);

#ifdef _DEBUG
printf("SetBit(5) = %d,SetBit(6) = %d\n",SetBit(5),SetBit(6));
printf("SetBit( SetBit(2) ) = %d\n",SetBit( SetBit(2) ));
#endif
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
① 預處理:
gcc -E -o hello.i hello.c


預處理就是將要包含(include)的文件插入原文件中、將宏定義展開、根據條件編譯命令選擇要使用的代碼,最后將這些代碼輸出到一個“.i”文件中等待進一步處理。
② 編譯:
gcc -S -o hello.s hello.i


編譯就是把C/C++代碼(比如上面的”.i”文件)“翻譯”成匯編代碼。
③ 匯編:
gcc -c -o hello.o hello.s


.o:object file(OBJ文件) 這里表現為二進制目標文件:


匯編就是將第二步輸出的匯編代碼翻譯成符合一定格式的機器代碼,在Linux系統上一般表現位ELF目標文件(OBJ文件)。
④ 鏈接:
gcc -o hello hello.o


鏈接就是將匯編生成的OBJ文件、系統庫的OBJ文件、庫文件鏈接起來,最終生成可以在特定平台運行的可執行程序。
總結:在編譯過程中。除非使用了”-c”,“-S”,或”-E”選項(或者編譯錯誤阻止了完整的過程),否則統一完整鏈接步驟。
譬如:gcc hello.c 和gcc -o hello hello.c都已經完成鏈接操作。


又如:gcc -c -o hello.o hello.c


二、鏈接原理:
gcc -c -o hello.o hello.c 不作最后一步鏈接,得到hello.o二進制OBJ文件
gcc -v -o hello hello.o 我們來看一樣鏈接過程是怎樣的:
book@www.100ask.org:/work/gcc_options/1th$ gcc -v -o hello hello.o
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/5/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 5.4.0-6ubuntu1~16.04.4' --with-bugurl=file:///usr/share/doc/gcc-5/README.Bugs --enable-languages=c,ada,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-5 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-5-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-5-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-5-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.4)
COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/
LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-v' '-o' 'hello' '-mtune=generic' '-march=x86-64'
/usr/lib/gcc/x86_64-linux-gnu/5/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/5/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/5/lto-wrapper -plugin-opt=-fresolution=/tmp/ccbhavbV.res
-plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s
-plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc
-plugin-opt=-pass-through=-lgcc_s --sysroot=/ --build-id
--eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed
-dynamic-linker /lib64/ld-linux-x86-64.so.2 -z relro
-o hello
/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crt1.o
/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crti.o
/usr/lib/gcc/x86_64-linux-gnu/5/crtbegin.o
-L/usr/lib/gcc/x86_64-linux-gnu/5
-L/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu
-L/usr/lib/gcc/x86_64-linux-gnu/5/../../../../lib
-L/lib/x86_64-linux-gnu -L/lib/../lib
-L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib
-L/usr/lib/gcc/x86_64-linux-gnu/5/../../..
hello.o
-lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed
/usr/lib/gcc/x86_64-linux-gnu/5/crtend.o
/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crtn.o
book@www.100ask.org:/work/gcc_options/1th$
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
crt1.o、crti.o、crtbegin.o、crtend.o、crtn.o是gcc加入的系統標准啟動文件,對於一般應用程序,這些啟動是必需的。
-lc:鏈接libc庫文件,其中libc庫文件中就實現了printf等函數。
① 動態鏈接:動態鏈接使用動態鏈接庫進行鏈接,生成的程序在執行的時候需要加載所需的動態庫才能運行。  動態鏈接生成的程序體積較小,但是必須依賴所需的動態庫,否則無法執行。
默認使用動態鏈接:gcc -o hello_shared hello.o

 

② 靜態鏈接:靜態鏈接使用靜態庫進行鏈接,生成的程序包含程序運行所需要的全部庫,可以直接運行,不過靜態鏈接生成的程序體積較大。
gcc -static -o hello_static hello.o

 

③ -nostartfiles
不鏈接系統標准啟動文件,而標准庫文件仍然正常使用:
gcc -v -nostartfiles -o hello hello.o

 

④ -nostdlib(最常用)
不鏈接系統標准啟動文件和標准庫文件:
gcc -v -nostdlib -o hello hello.o


- 會提示因為沒有鏈接系統標准啟動文件和標准庫文件,而鏈接失敗。
- 這個-nostdlib選項常用於裸機/bootloader、linux內核等程序,因為它們不需要啟動文件、標准庫文件。

 
來源:CSDN
原文:https://blog.csdn.net/czg13548930186/article/details/78331692
 

 

    .c為后綴的文件,C語言源代碼文件;
    .a為后綴的文件,是由目標文件構成的檔案庫文件;
    .C,.cc或.cxx 為后綴的文件,是C++源代碼文件;
    .h為后綴的文件,是程序所包含的頭文件;
    .i 為后綴的文件,是已經預處理過的C源代碼文件;
    .ii為后綴的文件,是已經預處理過的C++源代碼文件;
    .m為后綴的文件,是Objective-C源代碼文件;
    .o為后綴的文件,是編譯后的目標文件;
    .s為后綴的文件,是匯編語言源代碼文件;
    .S為后綴的文件,是經過預編譯的匯編語言源代碼文件。

 

    gcc的基本用法和選項
    在使用gcc編譯器的時候,我們必須給出一系列必要的調用參數和文件名稱。gcc編譯器的調用參數大約有100多個,其中多數參數我們可能根本就用不到,這里只介紹其中最基本、最常用的參數。
    gcc最基本的用法是∶gcc [options] [filenames]
    其中options就是編譯器所需要的參數,filenames給出相關的文件名稱。
    -c,只編譯,不連接成為可執行文件,編譯器只是由輸入的.c等源代碼文件生成.o為后綴的目標文件,通常用於編譯不包含主程序的子程序文件。
    -o output_filename,確定輸出文件的名稱為output_filename,同時這個名稱不能和源文件同名。如果不給出這個選項,gcc就給出預設的可執行文件a.out。
    -g,產生符號調試工具(GNU的gdb)所必要的符號資訊,要想對源代碼進行調試,我們就必須加入這個選項。
    -O,對程序進行優化編譯、連接,采用這個選項,整個源代碼會在編譯、連接過程中進行優化處理,這樣產生的可執行文件的執行效率可以提高,但是,編譯、連接的速度就相應地要慢一些。
    -O2,比-O更好的優化編譯、連接,當然整個編譯、連接過程會更慢。
    -Idirname,將dirname所指出的目錄加入到程序頭文件目錄列表中,是在預編譯過程中使用的參數。
    C程序中的頭文件包含兩種情況∶
    A)#include
    B)#include “myinc.h”
 

   這時,預編譯、編譯連接一次完成,生成一個系統預設的名為a.out的可執行文件,對於稍為復雜的情況,比如有多個源代碼文件、需要連接檔案庫或者有其他比較特別的要求,就要給定適當的調用選項參數。再看一個簡單的例子。整個源代碼程序由兩個文件testmain.c 和testsub.c組成,程序中使用了系統提供的數學庫,同時希望給出的可執行文件為test,這時的編譯命令可以是∶
    gcc testmain.c testsub.c -lm -o test
    其中,-lm表示連接系統的數學庫libm.a,這個過程可以用圖12-1框圖描述。

 
 

gcc常用編譯選項

-g : 加入調試信息

-m64 : 64位

-c : 只做預處理、編譯和匯編,不鏈接,產生的是目標文件(.o文件)

-S : 只做預處理和編譯,把文件編譯成為匯編代碼

-include : 某個代碼,簡單來說,就是便以某個文件,需要另一個文件的時候,就可以用它設定,功能就相當於在代碼中使用#include ,例如gcc hello.c -include /root/test.h

-I : 程序中用#include”file”的時候,gcc/g++會先在當前目錄查找你所制定的頭文件,如果沒有找到,他回到缺省的頭文件目錄找,如果使用-I指定了目錄,他會先在你-I后所指定的目錄查找,然后再按常規的順序去找

-I$(PATH) : inlcude,PATH指定一個環境變量的值

-fPIC : 該選項用於生成位置無關的代碼

-shared : 將-fPIC生成的位置無關的代碼作為動態庫,一般情況下,-fPIC和-shared都是一起使用的。生成SO文件,共享庫

-static : 此選項將禁止使用動態庫,所以,編譯出來的東西,一般都很大,也不需要什么動態連接庫,就可以運行

-o : 指定程序的名字

-l : 指定so文件的名字,比如需要libcdaParser.so,就可以寫成-lcdaParser,前面的lib和后面的.so可以省略

-L : 指定so文件所在的目錄

-O : 編譯器的優化選項,-O0表示不做優化,-O1為默認,-O3為最高優 化級別

 

 

1、gdb 常用命令

 

首先程序編譯時加 -g 選項才可打開調試選項 
eg:gcc –o filename –Wall filename.c –g //進入調試
gdb filename //進入調試
l    //顯示代碼 (list)
b  4    //在第四行設置斷點 相當於 Windows 的 F9 (break)           //若為 b  main  則表示斷點打在main處
r    //運行   相當於 Windows 的 F5 (run)
n //下一步不進入函數 相當於 Windows 的 F10  (next)
s //表示單步進入函數, 相當於 Windows 的 F11 (step)
p  I  //打印變量 I 相當於 Windows 的 Watch 窗口(print)
c     //運行到最后(continue)
q     //退出 相當於 Windows 的   Shift+F5 (quit)

 
來源:CSDN
原文:https://blog.csdn.net/smilejiasmile/article/details/74946733
 

gcc的基本用法

命令格式:gcc [選項] [文件名]

編譯的四個階段:
-E:僅執行編譯預處理;
-c:僅執行編譯操作,不進行連接操作;
-S:將C代碼轉換為匯編代碼;
-o:指定生成的輸出文件。


–c是使用GNU匯編器將源文件轉化為目標代碼之后就結束,在這種情況下,只調用了C編譯器(ccl)和匯編器(as),而連接器(ld)並沒有被執行,所以輸出的目標文件不會包含作為Linux程序在被裝載和執行時所必須的包含信息,但它可以在以后被連接到一個程序
-c表示只編譯(compile),而不連接成為可執行文件。生成同名字的 .o 目標文件。通常用於編譯不包含主程序的子程序文件。
gcc -c hello.c
生成:hello.o

-o選項用於說明輸出(output)文件名,gcc將生成一個目標(object)文件xx。
gcc hello.c -o xqf
或者:gcc -o xqf hello.c(順序可以調換)
輸出:xqf 為程序可執行文件

-g 選項產生符號調試工具(GNU的gdb)所必要的符號信息,插入到生成的二進制代碼中。表示編譯DEBUG版本。
想要對源代碼進行調試,就必須加入這個選項。當然,會增加可執行文件的大小。
gcc study.c -o xqf
gcc -g study.c -o xqf_g
結果如下:(確實加了 -g 可執行文件后變大了一點)
-rwxr-xr-x 1 root root 12393 Apr 19 21:39 xqf_g
-rwxr-xr-x 1 root root 11817 Apr 19 20:48 xqf

gcc 在產生調試符號時,同樣采用了分級的思路,開發人員可以通過在 -g 選項后附加數字1、2、3指定在代碼中加入調試信息的多少。默認的級別是2(-g2),此時產生的調試信息包括:擴展的符號表、行號、局部或外部變量信息。
級別3(-g3)包含級別2中的所有調試信息以及源代碼中定義的宏。
級別1(-g1)不包含局部變量和與行號有關的調試信息,因此只能夠用於回溯跟蹤和堆棧轉儲。
回溯追蹤:指的是監視程序在運行過程中函數調用歷史。
堆棧轉儲:則是一種以原始的十六進制格式保存程序執行環境的方法。


-pedantic 選項:當gcc在編譯不符合ANSI/ISO C 語言標准的源代碼時,將產生相應的警告信息
//study.c
#include <stdio.h>

int main()
{
long long int var = 1;
printf("hello world!\n");
return 0;
}

gcc -pedantic -o mm study.c
study.c: In function ‘main’:
study.c:5: warning: ISO C90 does not support ‘long long’


-Wall選項:使gcc產生盡可能多的警告信息,警告信息很有可能是錯誤的來源,特別是隱式編程錯誤,所以盡量保持0 warning。
用上面的代碼:study.c,編譯如下
gcc -Wall -o he study.c
study.c: In function ‘main’:
study.c:5: warning: unused variable ‘var’


-Werror 選項:要求gcc將所有的警告當作錯誤進行處理。
同樣是上面的程序:study.c
gcc -Werror -o haha study.c
竟然沒有錯誤!!
改一下study.c
#include <stdio.h>


void main()
{
long long int var = 1;
printf("hello world!\n");
//return 0;
}


再編譯:
gcc -Werror -o haha study.c
cc1: warnings being treated as errors
study.c: In function ‘main’:
study.c:4: error: return type of ‘main’ is not ‘int’

gcc -Wall -o hehe study.c
study.c:3: warning: return type of ‘main’ is not ‘int’
study.c: In function ‘main’:
study.c:5: warning: unused variable ‘var’

所以說:並不是所有的warning都變成 error。具體的,后面再深究。

 

-fPIC選項。PIC指Position Independent Code。共享庫要求有此選項,以便實現動態連接(dynamic linking)。


-I 選項(大寫的 i):向頭文件搜索目錄中添加新的目錄。
1、用#include"file"的時候,gcc/g++會先在當前目錄查找你所制定的頭文件,如
果沒有找到,他回到缺省的頭文件目錄找。
如果使用-I制定了目錄,他會先在你所制定的目錄查找,然后再按常規的順序去找.
2、用#include<file>,gcc/g++會到-I制定的目錄查找,查找不到,然后將到系統的缺
省的頭文件目錄查找
例如:
gcc –I /usr/dev/mysql/include test.c –o test.o


-l選項(小寫的 l)說明庫文件的名字。如果庫文件為 libtest.so, 則選項為: -ltest


-L選項說明庫文件所在的路徑。
例如:-L.(“.”表示當前路徑)。
      -L/usr/lib (“/usr/lib” 為路徑。注:這里的路徑是絕對路徑)
如果沒有提供 -L選項,gcc 將在默認庫文件路徑下搜索


-shared選項指定生成動態連接庫,不用該標志外部程序無法連接。相當於一個可執行文件, 生成 .so 文件


-static 選項,強制使用靜態鏈接庫,生成 .a 文件。因為gcc在鏈接時優先選擇動態鏈接庫,只有當動態鏈接庫不存在時才使用靜態鏈接庫。加上該選項可強制使用靜態鏈接庫。
.so 和 .a 的區別:運行時動態加載,編譯時靜態加載
具體的例子在文章:linux so文件生成與鏈接中有講。

 

多個文件一起編譯:
文件:test_a.c  test_b.c
兩種編譯方法:
1、一起編譯
gcc test_a.c test_b.c -o test

2、分別編譯各個源文件,之后對編譯后輸出的目標文件鏈接
gcc -c test_a.c
gcc -c test_b.c
gcc -o test_a.o test_b.o -o test

比較:第一中方法編譯時需要所有文件重新編譯;第二種植重新編譯修改的文件,未修改的不用重新編譯。
 
來源:CSDN
原文:https://blog.csdn.net/xiaqunfeng123/article/details/51301749
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM