GCC(警告.優化以及調試選項)
[介紹]
gcc and g++分別是gnu的c & c++編譯器
gcc/g++在執行編譯工作的時候,總共需要4步
1.預處理,生成.i的文件
預處理器cpp
2.將預處理后的文件不轉換成匯編語言,生成文件.s
編譯器egcs
3.有匯編變為目標代碼(機器代碼)生成.o的文件
匯編器as
4.連接目標代碼,生成可執行程序
連接器ld
1.總體選項
-E
只激活預處理,這個不生成文件,你需要把它重定向到一個輸出文件里
面.
例子用法:
gcc -E hello.c > pianoapan.txt
gcc -E hello.c | more
慢慢看吧,一個hello word 也要與處理成800行的代碼
-S
只激活預處理和編譯,就是指把文件編譯成為匯編代碼。
例子用法
gcc -S hello.c
他將生成.s的匯編代碼,你可以用文本編輯器察看
-c
只激活預處理,編譯,和匯編,也就是他只把程序做成obj文件
例子用法:
gcc -c hello.c
他將生成.o的obj文件
2.目錄選項
-Idir
在你是用#include"file"的時候,gcc/g++會先在當前目錄查找你所制定的頭
文件,如果沒有找到,他回到缺省的頭文件目錄找,如果使用-I制定了目錄,他
回先在你所制定的目錄查找,然后再按常規的順序去找.
對於#include,gcc/g++會到-I制定的目錄查找,查找不到,然后將到系
統的缺省的頭文件目錄查找
-include file
-i
相當於“#include”
包含某個代碼,簡單來說,就是便以某個文件,需要另一個文件的時候,就可以
用它設定,功能就相當於在代碼中使用#include
例子用法:
gcc hello.c -include /root/pianopan.h
-I-
就是取消前一個參數的功能,所以一般在-Idir之后使用
-idirafter dir
在-I的目錄里面查找失敗,講到這個目錄里面查找.
-iprefix prefix
-iwithprefix dir
一般一起使用,當-I的目錄查找失敗,會到prefix+dir下查找
-Ldir
制定編譯的時候,搜索庫的路徑。比如你自己的庫,可以用它制定目錄,不然
編譯器將只在標准庫的目錄找。這個dir就是目錄的名稱。
-llibrary
制定編譯的時候使用的庫
例子用法
gcc -lcurses hello.c
使用ncurses庫編譯程序
3.調試選項
-g
只是編譯器,在編譯的時候,產生調試信息。
-gstabs
此選項以stabs格式聲稱調試信息,但是不包括gdb調試信息.
-gstabs+
此選項以stabs格式聲稱調試信息,並且包含僅供gdb使用的額外調試信息.
-ggdb
此選項將盡可能的生成gdb的可以使用的調試信息.
-glevel
請求生成調試信息,同時用level指出需要多少信息,默認的level值是2
4.鏈接方式選項:
-static 此選項將禁止使用動態庫。
優點:程序運行不依賴於其他庫
缺點:文件比較大
-shared (-G) 此選項將盡量使用動態庫,為默認選項
優點:生成文件比較小
缺點:運行時需要系統提供動態庫
-symbolic 建立共享目標文件的時候,把引用綁定到全局符號上.
對所有無法解析的引用作出警告(除非用連接編輯選項 `-Xlinker -z -Xlinker defs'取代)。
注:只有部分系統支持該選項.
5.錯誤與告警選項
-Wall 一般使用該選項,允許發出GCC能夠提供的所有有用的警告。也可以用-W{warning}來標記指定的警告。
-pedantic 允許發出ANSI/ISO C標准所列出的所有警告
-pedantic-errors 允許發出ANSI/ISO C標准所列出的錯誤
-werror 把所有警告轉換為錯誤,以在警告發生時中止編譯過程
-w 關閉所有警告,建議不要使用此項
6.預處理選項
-Dmacro
相當於C語言中的#define macro
-Dmacro=defn
相當於C語言中的#define macro=defn
-Umacro
相當於C語言中的#undef macro
-undef
取消對任何非標准宏的定義
7.其他選項
-o
制定目標名稱,缺省的時候,gcc 編譯出來的文件是a.out,很難聽,如果你和我有同感,改掉它,哈哈
例子用法
gcc -o hello.exe hello.c (哦,windows用習慣了)
gcc -o hello.asm -S hello.c
-O0
-O1
-O2
-O3
編譯器的優化選項的4個級別,-O0表示沒有優化,-O1為缺省值,-O3優化級別最高
-fpic 編譯器就生成位置無關目標碼.適用於共享庫(shared library).
-fPIC 編譯器就輸出位置無關目標碼.適用於動態連接(dynamic linking),即使分支需要大范圍轉移.
-v 顯示詳細的編譯、匯編、連接命令
-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(后兩個參數)
-imacros file
將file文件的宏,擴展到gcc/g++的輸入文件,宏定義本身並不出現在輸入文件中
-nostdinc
使編譯器不再系統缺省的頭文件目錄里面找頭文件,一般和-I聯合使用,明確限定頭文件的位置
-nostdin C++
規定不在g++指定的標准路經中搜索,但仍在其他路徑中搜索,.此選項在創建libg++庫使用
-C
在預處理的時候,不刪除注釋信息,一般和-E使用,有時候分析程序,用這個很方便的
-M
生成文件關聯的信息。包含目標文件所依賴的所有源代碼你可以用gcc -M hello.c來測試一下,很簡單。
-MM
和上面的那個一樣,但是它將忽略由#include造成的依賴關系。
-MD
和-M相同,但是輸出將導入到.d的文件里面
-MMD
和-MM相同,但是輸出將導入到.d的文件里面
-Wa,option
此選項傳遞option給匯編程序;如果option中間有逗號,就將option分成多個選項,然后傳遞給會匯編程序
-Wl.option
此選項傳遞option給連接程序;如果option中間有逗號,就將option分成多個選項,然后傳遞給會連接程序.
-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
====================================================
GCC有很多的編譯選項,警告選項;指定頭文件、庫路徑;優化選項。本文針整理一下GCC的警告選項,主要依據http://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html文檔,並加上自己的一點小小經驗。
一、總概
-w
禁止編譯警告的打印。這個警告不建議使用。大約2012年底,公司代碼進行一次大重構,另外從Codeblock集成開發環境轉向Makefile管理,Makefile里面默認使用了-w,因而代碼一直沒有警告,今年個別項目開發中發現一些代碼筆誤導致的BUG,而這些問題可以從編譯警告中知道。前幾個月,領導安排我來fix這些警告。為了自己,為了后人,不建議使用-w選項。
-Werror
將所有的警告當成錯誤處理。此選項謹慎建議加上。有的開源庫警告很多(大名鼎鼎的ffmpeg也有很多警告呢),一一改掉耗時耗人力,必要性也不大。最后,公司代碼加入了一個開源庫,里面有很多代碼警告,可能領導又安排我來fix了。
-Wfatal-errors
遇到第一個錯誤就停止,減少查找錯誤時間。建議加上。很多人遇到錯誤,沒有意識到從第一個開始排查。不管是編譯錯誤,還是程序運行出錯,從最開始的錯誤查起,是個好的做法。
-Wall開啟“所有”的警告。強烈建議加上,並推薦該選項成為共識。如case語句沒有default處理,有符號、無符號處理,未使用變量(特別是函數有大量未使用的數組,占用棧空間,測試發現,開辟一個未使用的8MB的數組,程序有coredump),用%d來打印地址,或%s打印int值,等,都可以發出警告。
-Wextra
除-Wall外其它的警告。建議加上。
在GCC編譯時,加上必要的警告選項,可以避免很多低級錯誤引發的問題,我就在實際工程代碼中遇到用“==”來賦值,我自己寫的代碼也出現過把“=”當成判斷的。但是,有些錯誤卻不是用GCC選項能解決的。比如一般項目都會自定義調試信息打印函數,但在處理可變參數類型時,往往不注意。可參考文章《一個可變參數類型檢查的示例》。
二、細化
上面只是大概講幾個重要的選項。由於GCC的警告選項太多了,下面盡自己能力寫一下。
-Wall選項,顧名思義,就是“所有”的意思,它包括:
-Wall包括:
-Waddress -Warray-bounds=1 (only with -O2) -Wc++11-compat -Wc++14-compat -Wchar-subscripts -Wenum-compare (in C/ObjC; this is on by default in C++) -Wimplicit-int (C and Objective-C only) -Wimplicit-function-declaration (C and Objective-C only) -Wbool-compare -Wduplicated-cond -Wcomment -Wformat -Wmain (only for C/ObjC and unless -ffreestanding) -Wmaybe-uninitialized -Wmissing-braces (only for C/ObjC) -Wnonnull -Wopenmp-simd -Wparentheses -Wpointer-sign -Wreorder -Wreturn-type -Wsequence-point -Wsign-compare (only in C++) -Wstrict-aliasing -Wstrict-overflow=1 -Wswitch -Wtautological-compare -Wtrigraphs -Wuninitialized -Wunknown-pragmas -Wunused-function -Wunused-label -Wunused-value -Wunused-variable -Wvolatile-register-var
但不要被它的表面意思迷惑,要不,怎么還會有-Wextra呢。-Wextra包括(有幾個選項重復了,不懂原因):
-Wclobbered -Wempty-body -Wignored-qualifiers -Wmissing-field-initializers -Wmissing-parameter-type (C only) -Wold-style-declaration (C only) -Woverride-init -Wsign-compare -Wtype-limits -Wuninitialized -Wshift-negative-value -Wunused-parameter (only with -Wunused or -Wall) -Wunused-but-set-parameter (only with -Wunused or -Wall)
-Wchar-subscripts:
使用char類作為數組下標(因為char可能是有符號數)
-Wcomment:
注釋使用不規范。如“/* */”注釋中還包括“/*”。我在項目源碼發現過,不止一處。
-Wmissing-braces
括號不匹配。在多維數組的初始化或賦值中經常出現。下面a沒有完整被初始化,b完整初始化:
int a[2][2] = { 0, 1, 2, 3 }; int b[2][2] = { { 0, 1 }, { 2, 3 } };
-Wparentheses
括號不匹配,在運算符操作或if分支語句中,可能會出現此警告。
如“a&&b||c^d”會出現警告。下面代碼片段也會有警告
{ if (a) if (b) foo (); else bar (); // 這個else實際是if (b)的分支,不是if (a),因此,要用括號來表明其屬於哪個分支 }
這類bug隱藏得深,建議顯式地加上括號。
-Wsequence-point
如出現i=i++這類代碼,則報警告。-Wall默認有該警告
-Wswitch-defaultcase
沒有default時,報警告
-Wunused-but-set-parameter
設置了但未使用的參數警告
-Wunused-but-set-variable
設置了但未使用的變量警告
-Wunused-function
聲明但未使用函數
-Wunused-label
未使用的標簽,比如用goto會使用label,但在刪除goto語句時,忘了刪除label。
-Wunused-variable
未使用的變量
-Wmaybe-uninitialized
變量可能沒有被初始化。特別是在有if語句或switch語句中,最好在聲明變量時加上初始化。
下面代碼片段中,當y不是1、2、3時,x沒有明確的值,是不安全的。
{ int x; switch (y) { case 1: x = 1; break; case 2: x = 4; break; case 3: x = 5; } foo (x); }
-Wfloat-equal
對浮點數使用等號,這是不安全的。
{ float d = 2.0; if (d == i) { ... } }
-Wreturn-type
函數有返回值,但函數體個別地方沒有返回值(特別是有if判斷,可能忘記在else添加返回值)。
int foo() { if(a==1) { return ok; } // no return here }
-Wpointer-sign
指針有符號和無符號的錯誤傳參。如函數使用unsigned char*,但傳入char*指針。
-Wsign-compare
有符號和無符號比較。
-Wconversion-null
-Wsizeof-pointer-memaccess
在sizeof中經常出現,下面代碼片段中,this為指針,4字節,無法保證完整初始化類。
memset(this, 0, sizeof(this));
-Wreorder
C++出現,構造函數中成員變量初始化與聲明的順序不一致。
-Woverflow
范圍溢出。
-Wshadow
局部變量覆蓋參數、全局變量,報警告
====================================================
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 版本號和版權信息 。 |
-o outfile | 輸出到指定的文件。 |
-x language | 指明使用的編程語言。允許的語言包括: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 當傳遞整數參數時,控制所使用寄存器的個數。
ANSI C標准的預定義宏
=======================================================================
ANSI C標准的預定義宏
__FILE__ 宏所在文件的源文件名
__LINE__ 宏所在行的行號
__DATE__ 代碼編譯的日期
__TIME__ 代碼編譯的時間
__STDC__ 指示編譯器是否執行ANSI C標准,如果是則其值為1
__cplusplus 編譯C++程序時該標識符被定義為1
=======================================================================
__func__ 函數名,C99引入了__func__,但其不是宏
__FUNCTION__ 函數名
__PRETTY_FUNCTION__ 函數名
標准預定義宏(Standard Predefined Macros)具體參考
http://gcc.gnu.org/onlinedocs/cpp/Standard-Predefined-Macros.html
http://gcc.gnu.org/onlinedocs/gcc/Function-Names.html#Function-Names
編譯器預定義宏(GNU-, Microsoft-Specific Predefined Macros)具體參考
http://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html#Common-Predefined-Macros
http://msdn.microsoft.com/en-us/library/b0084kay
gcc
編譯器是將易於編寫、閱讀和維護的高級計算機語言翻譯為計算機能解讀、運行的低級機器語言的程序。
GNU項目中的一個子項目GCC(GNU Compiler Collection)
是一個編譯器套裝,是GNU計划的關鍵部分,也是GNU最優秀的軟件之一。
GCC最初用於編譯C語言,隨着項目的發展GCC已經成為了能夠編譯C、C++、Java、Ada、fortran、Object C、
Object C++、Go語言的編譯器大家族。
GCC的組成
GCC由cpp(預處理器)、gcc(C編譯器)、g++(C++編譯器)、binutils(Binary Utilities二進制工具)等工具組成。
binutils是輔助gcc的主要軟件,常用的工具有:as(匯編器)、ld(鏈接器)、ar(ar工具)等等。
gcc僅僅作為真實的編譯器和鏈接器的入口。
它會在需要的時候調用其它組件(預處理器、匯編器、鏈接器),並且會傳一些額外的參數給編譯器和連接器。
輸入文件的類型和傳給gcc的參數決定了gcc調用哪些組件。
gcc識別的文件擴展名如下:
.c C語言文件
.i 預處理后的C語言文件
.C、.cc、.cp、.cpp、.c++、.cxx C++語言文件
.ii 預處理后的C++語言文件
.S 匯編文件
.s 預處理后的匯編文件
.o 編譯后的目標文件
.a 目標文件的靜態鏈接庫(鏈接時使用)
.so 目標文件的動態鏈接庫(鏈接、運行時使用)
編譯命令格式
gcc [-option1] ... <filename>
g++ [-option1] ... <filename>
1.命令、選項和源文件之間使用空格分隔
2.一行命令中可以有零個、一個或多個選項
3.文件名可以包含文件的絕對路徑,也可以使用相對路徑。
4.如果命令中不包含輸出可執行文件的文件名,可執行文件的文件名默認為a.out。
gcc、g++編譯選項
-o file 指定生成的輸出文件名為file
-E 只進行預處理
-S 只進行預處理和編譯
-c 只進行預處理、編譯和匯編
-Wall 生成所有級別的警告信息
-w 關閉所有警告,建議不使用此選項
-O[0-3] 編譯器優化級別
數值越大級別越高,0表示不優化
-include file 插入一個文件
等同於源代碼中的#include
-Dmacro[=def] 將名為marco的宏定義為def
等同於#define macro [def]
若[def]忽略不寫,則macro等於1
-Umacro 取消宏的定義
等同於源代碼中的#undef macro
-v 顯示制作gcc工具時的配置命令
顯示預處理器、編譯器的版本號
-Idir 將dir目錄加入頭文件搜索目錄列表
優先在dir目錄中查找包含的頭文件
-Ldir 將dir目錄加入庫文件目錄列表
優先在dir目錄中查找庫文件
-lname 鏈接庫為name的庫
-static 鏈接時使用靜態庫
-shared 編譯動態庫
-g 在可執行文件中加入標准調試信息
gcc、g++的編譯過程
gcc和g++編譯器的編譯過程:
1、預處理
2、編譯
3、匯編
4、鏈接
編譯過程示意圖:
gcc常用編譯應用實例:
例1:
gcc -E hello.c -o hello.i 預編譯
gcc -S hello.i -o hello.s 編譯
gcc -c hello.s -o hello.o 匯編
gcc hello.o -o hello_elf 鏈接
例2:
gcc hello.c -o hello_elf
gdb簡介
GNU工具集中的調試器是gdb,該程序是一個交互式工具,工作在字符模式。
除gdb外,linux下比較有名的調試器還有xxgdb,ddd, kgdb, ups。
gdb是功能強大的調試器,可完成如下調試任務:
1、設置斷點
2、監視程序變量的值
3、程序的單步執行
4、顯示/修改變量的值
5、顯示/修改寄存器
6、查看程序的堆棧情況
7、遠程調試
GDB調試精粹及使用實例
一:列文件清單
1. List
(gdb) list line1,line2
二:執行程序
要想運行准備調試的程序,可使用run命令,在它后面可以跟隨發給該程序的任何參數,包括標准輸入和標准輸出說明符(<和>)和外殼通配符(*、?、[、])在內。
如果你使用不帶參數的run命令,gdb就再次使用你給予前一條run命令的參數,這是很有用的。
利用set args 命令就可以修改發送給程序的參數,而使用show args 命令就可以查看其缺省參數的列表。
(gdb)set args –b –x
(gdb) show args
backtrace命令為堆棧提供向后跟蹤功能。
Backtrace 命令產生一張列表,包含着從最近的過程開始的所以有效過程和調用這些過程的參數。
三:顯示數據
利用print 命令可以檢查各個變量的值。
(gdb) print p (p為變量名)
whatis 命令可以顯示某個變量的類型
(gdb) whatis p
type = int *
print 是gdb的一個功能很強的命令,利用它可以顯示被調試的語言中任何有效的表達式。表達式除了包含你程序中的變量外,還可以包含以下內容:
l 對程序中函數的調用
(gdb) print find_entry(1,0)
l 數據結構和其他復雜對象
(gdb) print *table_start
$8={e=reference=’\000’,location=0x0,next=0x0}
l 值的歷史成分
(gdb)print $1 ($1為歷史記錄變量,在以后可以直接引用 $1 的值)
l 人為數組
人為數組提供了一種去顯示存儲器塊(數組節或動態分配的存儲區)內容的方法。早期的調試程序沒有很好的方法將任意的指針換成一個數組。就像對待參數一樣,讓我們查看內存中在變量h后面的10個整數,一個動態數組的語法如下所示:
base@length
因此,要想顯示在h后面的10個元素,可以使用h@10:
(gdb)print h@10
$13=(-1,345,23,-234,0,0,0,98,345,10)
四:斷點(breakpoint)
break命令(可以簡寫為b)可以用來在調試的程序中設置斷點,該命令有如下四種形式:
l break line-number 使程序恰好在執行給定行之前停止。
l break function-name 使程序恰好在進入指定的函數之前停止。
l break line-or-function if condition 如果condition(條件)是真,程序到達指定行或函數時停止。
l break routine-name 在指定例程的入口處設置斷點
如果該程序是由很多原文件構成的,你可以在各個原文件中設置斷點,而不是在當前的原文件中設置斷點,其方法如下:
(gdb) break filename:line-number
(gdb) break filename:function-name
要想設置一個條件斷點,可以利用break if命令,如下所示:
(gdb) break line-or-function if expr
例:
(gdb) break 46 if testsize==100
從斷點繼續運行:countinue 命令
五.斷點的管理
1. 顯示當前gdb的斷點信息:
(gdb) info break
他會以如下的形式顯示所有的斷點信息:
Num Type Disp Enb Address What
1 breakpoint keep y 0x000028bc in init_random at qsort2.c:155
2 breakpoint keep y 0x0000291c in init_organ at qsort2.c:168
(gdb)
2.刪除指定的某個斷點:
(gdb) delete breakpoint 1
該命令將會刪除編號為1的斷點,如果不帶編號參數,將刪除所有的斷點
(gdb) delete breakpoint
3.禁止使用某個斷點
(gdb) disable breakpoint 1
該命令將禁止斷點 1,同時斷點信息的 (Enb)域將變為 n
4.允許使用某個斷點
(gdb) enable breakpoint 1
該命令將允許斷點 1,同時斷點信息的 (Enb)域將變為 y
5.清除原文件中某一代碼行上的所有斷點
(gdb)clean number
注:number 為原文件的某個代碼行的行號
六.變量的檢查和賦值
l whatis:識別數組或變量的類型
l ptype:比whatis的功能更強,他可以提供一個結構的定義
l set variable:將值賦予變量
l print 除了顯示一個變量的值外,還可以用來賦值
七.單步執行
l next
不進入的單步執行
l step
進入的單步執行
如果已經進入了某函數,而想退出該函數返回到它的調用函數中,可使用命令finish
八.函數的調用
l call name 調用和執行一個函數
(gdb) call gen_and_sork( 1234,1,0 )
(gdb) call printf(“abcd”)
$1=4
l finish 結束執行當前函數,顯示其返回值(如果有的話)
九.機器語言工具
有一組專用的gdb變量可以用來檢查和修改計算機的通用寄存器,gdb提供了目前每一台計算機中實際使用的4個寄存器的標准名字:
l $pc : 程序計數器
l $fp : 幀指針(當前堆棧幀)
l $sp : 棧指針
l $ps : 處理器狀態
十.信號
gdb通常可以捕捉到發送給它的大多數信號,通過捕捉信號,它就可決定對於正在運行的進程要做些什么工作。例如,按CTRL-C將中斷信號發送給gdb,通常就會終止gdb。但是你或許不想中斷gdb,真正的目的是要中斷gdb正在運行的程序,因此,gdb要抓住該信號並停止它正在運行的程序,這樣就可以執行某些調試操作。
Handle命令可控制信號的處理,他有兩個參數,一個是信號名,另一個是接受到信號時該作什么。幾種可能的參數是:
l nostop 接收到信號時,不要將它發送給程序,也不要停止程序。
l stop 接受到信號時停止程序的執行,從而允許程序調試;顯示一條表示已接受到信號的消息(禁止使用消息除外)
l print 接受到信號時顯示一條消息
l noprint 接受到信號時不要顯示消息(而且隱含着不停止程序運行)
l pass 將信號發送給程序,從而允許你的程序去處理它、停止運行或采取別的動作。
l nopass 停止程序運行,但不要將信號發送給程序。
例如,假定你截獲SIGPIPE信號,以防止正在調試的程序接受到該信號,而且只要該信號一到達,就要求該程序停止,並通知你。要完成這一任務,可利用如下命令:
(gdb) handle SIGPIPE stop print
請注意,UNIX的信號名總是采用大寫字母!你可以用信號編號替代信號名
如果你的程序要執行任何信號處理操作,就需要能夠測試其信號處理程序,為此,就需要一種能將信號發送給程序的簡便方法,這就是signal命令的任務。該 命令的參數是一個數字或者一個名字,如SIGINT。假定你的程序已將一個專用的SIGINT(鍵盤輸入,或CTRL-C;信號2)信號處理程序設置成采 取某個清理動作,要想測試該信號處理程序,你可以設置一個斷點並使用如下命令:
(gdb) signal 2
continuing with signal SIGINT(2)
該程序繼續執行,但是立即傳輸該信號,而且處理程序開始運行.
十一. 原文件的搜索
search text:該命令可顯示在當前文件中包含text串的下一行。
Reverse-search text:該命令可以顯示包含text 的前一行。
十二.UNIX接口
shell 命令可啟動UNIX外殼,CTRL-D退出外殼,返回到 gdb.
十三.命令的歷史
為了允許使用歷史命令,可使用 set history expansion on 命令
(gdb) set history expansion on
小結:常用的gdb命令
backtrace 顯示程序中的當前位置和表示如何到達當前位置的棧跟蹤(同義詞:where)
breakpoint 在程序中設置一個斷點
cd 改變當前工作目錄
clear 刪除剛才停止處的斷點
commands 命中斷點時,列出將要執行的命令
continue 從斷點開始繼續執行
delete 刪除一個斷點或監測點;也可與其他命令一起使用
display 程序停止時顯示變量和表達時
down 下移棧幀,使得另一個函數成為當前函數
frame 選擇下一條continue命令的幀
info 顯示與該程序有關的各種信息
jump 在源程序中的另一點開始運行
kill 異常終止在gdb 控制下運行的程序
list 列出相應於正在執行的程序的原文件內容
next 執行下一個源程序行,從而執行其整體中的一個函數
print 顯示變量或表達式的值
pwd 顯示當前工作目錄
pype 顯示一個數據結構(如一個結構或C++類)的內容
quit 退出gdb
reverse-search 在源文件中反向搜索正規表達式
run 執行該程序
search 在源文件中搜索正規表達式
set variable 給變量賦值
signal 將一個信號發送到正在運行的進程
step 執行下一個源程序行,必要時進入下一個函數
undisplay display命令的反命令,不要顯示表達式
until 結束當前循環
up 上移棧幀,使另一函數成為當前函數
watch 在程序中設置一個監測點(即數據斷點)
whatis 顯示變量或函數類型
****************************************************
GNU的調試器稱為gdb,該程序是一個交互式工具,工作在字符模式。在 X Window 系統中,有一個gdb的前端圖形工具,稱為xxgdb。gdb 是功能強大的調試程序,可完成如下的調試任務:
* 設置斷點;
* 監視程序變量的值;
* 程序的單步執行;
* 修改變量的值。
在可以使用 gdb 調試程序之前,必須使用 -g 選項編譯源文件。可在 makefile 中如下定義 CFLAGS 變量:
CFLAGS = -g
運行 gdb 調試程序時通常使用如下的命令:
gdb progname
在 gdb 提示符處鍵入help,將列出命令的分類,主要的分類有:
* aliases:命令別名
* breakpoints:斷點定義;
* data:數據查看;
* files:指定並查看文件;
* internals:維護命令;
* running:程序執行;
* stack:調用棧查看;
* statu:狀態查看;
* tracepoints:跟蹤程序執行。
鍵入 help 后跟命令的分類名,可獲得該類命令的詳細清單。
gdb 的常用命令
命令 解釋
break NUM 在指定的行上設置斷點。
bt 顯示所有的調用棧幀。該命令可用來顯示函數的調用順序。
clear 刪除設置在特定源文件、特定行上的斷點。其用法為clear FILENAME:NUM
continue 繼續執行正在調試的程序。該命令用在程序由於處理信號或斷點而 導致停止運行時。
display EXPR 每次程序停止后顯示表達式的值。表達式由程序定義的變量組成。
file FILE 裝載指定的可執行文件進行調試。
help NAME 顯示指定命令的幫助信息。
info break 顯示當前斷點清單,包括到達斷點處的次數等。
info files 顯示被調試文件的詳細信息。
info func 顯示所有的函數名稱。
info local 顯示當函數中的局部變量信息。
info prog 顯示被調試程序的執行狀態。
info var 顯示所有的全局和靜態變量名稱。
kill 終止正被調試的程序。
list 顯示源代碼段。
make 在不退出 gdb 的情況下運行 make 工具。
next 在不單步執行進入其他函數的情況下,向前執行一行源代碼。
print EXPR 顯示表達式 EXPR 的值。
******gdb 使用范例************************
-----------------
清單 一個有錯誤的 C 源程序 bugging.c
代碼:
-----------------
1 #include
2
3 static char buff [256];
4 static char* string;
5 int main ()
6 {
7 printf ("Please input a string: ");
8 gets (string);
9 printf ("\nYour string is: %s\n", string);
10 }
-----------------
上面這個程序非常簡單,其目的是接受用戶的輸入,然后將用戶的輸入打印出來。該程序使用了一個未經過初始化的字符串地址 string,因此,編譯並運行之后,將出現 Segment Fault 錯誤:
$ gcc -o bugging -g bugging.c
$ ./bugging
Please input a string: asfd
Segmentation fault (core dumped)
為了查找該程序中出現的問題,我們利用 gdb,並按如下的步驟進行:
1.運行 gdb bugging 命令,裝入 bugging 可執行文件;
2.執行裝入的 bugging 命令 run;
3.使用 where 命令查看程序出錯的地方;
4.利用 list 命令查看調用 gets 函數附近的代碼;
5.唯一能夠導致 gets 函數出錯的因素就是變量 string。用print命令查看 string 的值;
6.在 gdb 中,我們可以直接修改變量的值,只要將 string 取一個合法的指針值就可以了,為此,我們在第8行處設置斷點 break 8;
7.程序重新運行到第 8行處停止,這時,我們可以用 set variable 命令修改 string 的取值;
8.然后繼續運行,將看到正確的程序運行結果。
Linux庫函數制作(靜態庫、動態庫)
gcc常用編譯應用實例:
例1:
gcc -E hello.c -o hello.i 預編譯
gcc -S hello.i -o hello.s 編譯
gcc -c hello.s -o hello.o 匯編
gcc hello.o -o hello_elf 鏈接
例2:
gcc hello.c -o hello_elf
靜態庫與動態庫
鏈接方式
鏈接分為兩種:靜態鏈接、動態鏈接
靜態鏈接:
由鏈接器在鏈接時將庫的內容加入到可執行程序中
靜態鏈接的特點是:
優點:
對運行環境的依賴性較小,具有較好的兼容性
缺點:
生成的程序比較大,需要更多的系統資源,在裝入內存時會消耗更多的時間
庫函數有了更新,必須重新編譯應用程序
動態鏈接:
連接器在鏈接時僅僅建立與所需庫函數的之間的鏈接關系,在程序運行時才將所需資源調入可執行程序
動態鏈接的特點:
優點:
在需要的時候才會調入對應的資源函數
簡化程序的升級;有着較小的程序體積
實現進程之間的資源共享(避免重復拷貝)
缺點:
依賴動態庫,不能獨立運行
動態庫依賴版本問題嚴重
動態鏈接庫的使用2:
1.庫函數、頭文件均在系統路徑下
#cp libtestlib.so /lib
#gcc mytest.c -o mytest -ltestlib
#./mytest
編譯運行都不會出錯
問題:有個問題出現了?
我們前面的靜態庫也是放在/lib下,那么連接的到底是動態庫還是靜態庫呢?
當靜態庫與動態庫重名時,系統會優先連接動態庫,或者我們可以加入-static指定使用靜態庫
示例:
gcc main_grade.c fun_grade.c -o grade_elf -static // 靜態編譯可執行文件
gcc main_grade.c fun_grade.c -o grade_elf // 默認動態編譯可執行文件
靜態庫編譯可執行文件
gcc -c ./src/fun_grade.c -o ./lib/fun_grade.o
ar rc ./lib/libfun_grade.a ./lib/fun_grade.o
gcc ./src/main_grade.c -o grade_elf -L./lib/ -lfun_grade-I./inc/
動態庫編譯可執行文件
gcc -shared ./src/fun_grade.c -o ./lib/libfun_grade.so
gcc ./src/main_grade.c -o grade_elf -L./lib/ -lfun_grade -I./inc/
解決無法打開動態庫的常用簡便方法:
聲明臨時變量環境
export LD_LIBRARY_PATH=./lib:$LD_LIBRARY_PATH
或者修改 /etc/ld.so.conf 文件 在其中添加庫的搜索路徑,一行一個路徑。
sudo ldconfig 更新 /etc/ld.so.cache 文件
那 ./etc/ld.so.conf 中所有路徑的庫文件都被緩存達到 /etc/ld.so.cache 中。