版本] -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"。
經常使用選項
-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有i38六、i48六、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
1、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
2、連接原理:
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爲最高優 化級別
一、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 選項後附加數字一、二、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):向頭文件搜索目錄中添加新的目錄。
一、用#include"file"的時候,gcc/g++會先在當前目錄查找你所制定的頭文件,如
果沒有找到,他回到缺省的頭文件目錄找。
若是使用-I制定了目錄,他會先在你所制定的目錄查找,而後再按常規的順序去找.
二、用#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
兩種編譯方法:
一、一塊兒編譯
gcc test_a.c test_b.c -o test
二、分別編譯各個源文件,以後對編譯後輸出的目標文件連接
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