編譯選項學習總結(原創,禁止轉載)
基本選項
一般來講,C/C++從原始碼到可執行程式之間要經歷四個步驟:
預處理:展開標頭檔案/巨集替換/去掉註釋/條件編譯
編譯:檢查語法,生成彙編
彙編:彙編程式碼轉換機器碼
連結:連結到一起生成可執行程式
-E:只進行預處理,不編譯
執行指令
gcc -E test.c
命令列顯示出預處理資訊,但不生成檔案
gcc -E hello.c > 1.txt
將相關預處理資訊重定向到檔案1.txt中,檔案內容顯示如下
typedef signed char __int8_t; typedef unsigned char __uint8_t; typedef short __int16_t; typedef unsigned short __uint16_t; typedef int __int32_t; typedef unsigned int __uint32_t; typedef long long __int64_t; typedef unsigned long long __uint64_t; typedef long __darwin_intptr_t; typedef unsigned int __darwin_natural_t; typedef int __darwin_ct_rune_t; typedef union { char __mbstate8[128]; long long _mbstateL; } __mbstate_t; typedef __mbstate_t __darwin_mbstate_t; . . .
-S:只編譯,不彙編
執行指令
gcc -S test.c
生成檔案test.s,檔案內容如下:
.section __TEXT,__text,regular,pure_instructions .build_version macos, 11, 0 sdk_version 11, 3 .globl _main ## -- Begin function main .p2align 4, 0x90 _main: ## @main .cfi_startproc ## %bb.0: pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset %rbp, -16 movq %rsp, %rbp .cfi_def_cfa_register %rbp subq $16, %rsp movl $0, -4(%rbp) leaq L_.str(%rip), %rdi movb $0, %al callq _printf xorl %ecx, %ecx movl %eax, -8(%rbp) ## 4-byte Spill movl %ecx, %eax addq $16, %rsp popq %rbp retq .cfi_endproc ## -- End function .section __TEXT,__cstring,cstring_literals L_.str: ## @.str .asciz "hello\n" .subsections_via_symbols
表明此時根據原始檔生成了彙編程式碼
-c:只編譯、彙編,不連結
執行指令
gcc -c test.c
此時原始檔經過編譯和彙編直接生成二進位制檔案test.o,沒有其他中間檔案生成
-g:包含除錯資訊
執行指令
gcc -g test.c
可見不光生成了可執行檔案a.out,同時還在當前資料夾下生成了具備除錯資訊的檔案a.out.dSYM,此時可以使用除錯工具gdb對可執行檔案進行除錯
-I:指定include包含檔案的搜尋目錄
執行指令
gcc -I include/ -c test.c
表明在gcc執行過程中,除了標準庫中的標頭檔案,還可以從指定的目錄去搜索標頭檔案
-o:輸出成指定檔名
執行指令
gcc -o b test.c
表明指定生成的二進位制檔名稱為b,之後執行程式b,和a.out執行是同樣的結果
-lpthread:多執行緒程式設計
執行指令
gcc test.c -lpthread
指明gcc在連結階段連結pthread庫,從而編譯包含多執行緒標頭檔案的程式
-lm:程式使用了math.h中宣告的庫函式
執行指令
gcc test.c -lm
高階選項
-v:詳細輸出編譯過程中所採用的每一個選項
執行指令
gcc -v
得到結果
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/4.2.1
Apple clang version 12.0.5 (clang-1205.0.22.11)
Target: x86_64-apple-darwin20.5.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
-C:預處理時保留註釋資訊
執行指令
gcc -E -C test.c
得到結果
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. The rights granted to you under the License
* may not be used to create, or enable the creation or redistribution of,
* unlawful or unlicensed copies of an Apple operating system, or to
* circumvent, violate, or enable the circumvention or violation of, any
* terms of an Apple operating system software license agreement.
*
* Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this file.
.
.
void clearerr(FILE *);
int fclose(FILE *);
int feof(FILE *);
int ferror(FILE *);
int fflush(FILE *);
int fgetc(FILE *);
int fgetpos(FILE * restrict, fpos_t *);
char *fgets(char * restrict, int, FILE *);
.
.
.
可見和單純執行-E選項時相比,還多加了註釋資訊
-ggdb:在可執行檔案中包含可供GDB使用的除錯資訊
-g選項和-ggdb選項只有細微的差別:
-g以OS本地格式(stabs,COFF,XCOFF或DWARF 2)產生除錯資訊。
-ggdb生成專門用於gdb的除錯資訊。
-fverbose-asm:在編譯成組合語言時,把C變數的名稱作為組合語言中的註釋
執行指令
gcc test.c -S -fverbose-asm
得到結果
.section __TEXT,__text,regular,pure_instructions
.build_version macos, 11, 0 sdk_version 11, 3
.globl _main ## -- Begin function main
.p2align 4, 0x90
_main: ## @main
.cfi_startproc
## %bb.0:
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset %rbp, -16
movq %rsp, %rbp
.cfi_def_cfa_register %rbp
subq $16, %rsp
movl $0, -4(%rbp)
movl $0, -8(%rbp)
movl $1, -12(%rbp)
leaq L_.str(%rip), %rdi
movb $0, %al
callq _printf
xorl %ecx, %ecx
movl %eax, -16(%rbp) ## 4-byte Spill
movl %ecx, %eax
addq $16, %rsp
popq %rbp
retq
.cfi_endproc
## -- End function
.section __TEXT,__cstring,cstring_literals
L_.str: ## @.str
.asciz "hello\n"
.subsections_via_symbols
-save-temps:自動輸出預處理檔案、彙編檔案、物件檔案,編譯正常進行
執行指令
gcc -save-temps test.c
之後使用ls命令檢視目錄中檔案狀態
a.out test.bc test.c test.i test.o test.s
可見保留了所有的中間檔案
-fsyntax-only:只測試原始檔語法是否正確,不會進行任何編譯操作
執行指令
gcc -fsyntax-only test.c
當原始檔沒有語法錯誤時,沒有任何輸出,當故意修改出一處語法錯誤時,得到結果
test.c:4:21: error: expected ';' at end of declaration
int i = 0, j = 1 printf("hello\n");
^
;
1 error generated.
顯示出原始檔語法中存在的錯誤
-llibrary:連線時搜尋指定的函式庫
例如:多執行緒程式設計例子
gcc test.c -lpthread
-Idirectory:指定額外的標頭檔案搜尋路徑
例如:
gcc -I ../include test.c
新增目錄../include為標頭檔案搜尋路徑
-Ldirectory:指定額外的函式庫搜尋路徑
例如:
gcc -L ../lib test.c
新增目錄../iib為函式庫搜尋路徑
-static:禁止使用動態庫
詳見下節
-shared:儘量使用動態庫
詳見下節
庫的建立與使用
靜態庫
以建立靜態庫static_lib.a為例
1、編寫C原始檔static_lib.c,其中寫入需要重複呼叫的函式,執行命令
gcc -c static_lib.c
生成目標檔案static_lib.o
2、使用ar工具建立靜態庫
ar rcs static_lib.a static_lib.o
3、編寫C標頭檔案static_lib.h,其中寫入這些函式的原型宣告
4、編寫主函式app.c,引入標頭檔案static_lib.h,這樣就可以正常使用那些自定義的可複用函數了
5、執行命令
gcc app.c -static ./static_lib.a -o app
編譯生成可執行檔案app
動態庫
以建立動態庫share_lib.so為例
1、編寫C原始檔share_lib.c,寫入需要重複呼叫的函式,執行命令
gcc -shared -fPIC -o share_lib.so share_lib.c
生成動態庫檔案share_lib.so
2、編寫C標頭檔案share_lib.h,寫入函式的原型宣告
3、編寫主函式app.c,引入標頭檔案share_lib.h,之後就可以呼叫在動態庫中自定義的函數了
4、執行命令
gcc app.c ./share_lib.so -o app
編譯生成可執行檔案app
出錯提示選項
-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:如果在同一個可見域內某定義多次宣告,編譯器就發出警告,即使這些重複宣告有效並且毫無差別。
gcc常見警告含義
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時有效;
其它選項
語言標準
-ansi:ANSI標準
-std=c99:C99標準
-std=gnu89:ISO/IEC 9899:1990 以及GNU擴充
-std=gnu99:ISO/IEC 9899:1999 以及GNU擴充
-trigraphs:支援ISO C三字元組
優化選項
-O0:關閉所有優化選項
-O1:第一級別優化,使用此選項可使可執行檔案更小、執行更快,並不會增加太多編譯時間,可以簡寫為-O
-O2:第二級別優化,採用了幾乎所有的優化技術,使用此選項會延長編譯時間
-O3:第三級別優化,在-O2的基礎上增加了產生inline函式、使用暫存器等優化技術
-Os:此選項類似於-O2,作用是優化所佔用的空間,但不會進行效能優化,常用於生成最終版本
自定義副檔名
-x:使用此選項可以指定自定義的原始檔副檔名,型別有c、c-header、cpp-output、assembler、assembler-with-cpp、none
凡-x後面所列的所有檔案都會被視為其指定的型別,要想改變型別可以再一次使用-x選項,或者使用-x none回到預設設定
示例:gcc -o test test.c -x assembler test.asm -x c test2.c