Gcc编译链接及常用选项总结
前言
GNU CC(簡稱Gcc)是GNU項目中符合ANSI C標準的編譯系統,能夠編譯用C、C++和Object- C等語言編寫的程序。Gcc不僅功能強大,而且可以編譯如C、C++、Object C、Java等多種語言,而且Gcc又是一個交叉平臺編譯器,它能夠在當前CPU平臺上為多種不同體系結構的硬件平臺開發軟件。本章中的示例均采用Gcc版本為4.8.2。
Gcc編譯鏈接流程
Gcc編譯鏈接流程分為四個步驟:
Gcc指令的一般格式為:
gcc [option1] compile-files [option2] object-files其中目標文件可缺省,Gcc默認生成的可執行文件命名為:編譯文件名.out
下面以簡單的hello world程序為例說明Gcc編譯的四個過程:
| 123456 | void main(int argc, char* argv[]){ printf("hello world"); return;} |
預處理過程
option1?為-E,生成的目標文件為.i(c)或.ii(c++)后綴的經過預處理的編譯輸入文件,Gcc指令為:
tly@ubuntu ~> gcc -E test.c -o test.i生成的預編譯文件內容為:
| 1234567891011121314151617181920212223242526 | # 1 "test.c"# 1 "<command-line>"# 1 "/usr/include/stdc-predef.h" 1 3 4# 1 "<command-line>" 2# 1 "test.c"# 1 "/usr/include/stdio.h" 1 3 4# 27 "/usr/include/stdio.h" 3 4# 1 "/usr/include/features.h" 1 3 4# 374 "/usr/include/features.h" 3 4# 1 "/usr/include/i386-linux-gnu/sys/cdefs.h" 1 3 4... 省略...extern char *ctermid (char *__s) __attribute__ ((__nothrow__ , __leaf__));# 913 "/usr/include/stdio.h" 3 4extern void flockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));extern int ftrylockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ;extern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));# 943 "/usr/include/stdio.h" 3 4# 2 "test.c" 2void main(int argc, char* argv[]){printf("hello world");return;} |
Gcc預處理過程把<stdio.h>的內容插入到hello.i文件中了
編譯
option1?為-S,生成的目標文件為.s或.S后綴的經過編譯但是沒有匯編過的匯編文件,Gcc編譯過程首先要檢查代碼的規范性、是否有語法錯誤等,以確定代碼的實際要做的工作,在檢查無誤后,Gcc把代碼翻譯成匯編語言Gcc指令為:
tly@ubuntu ~> gcc -S test.i -o test.s生成的編譯之后的匯編文件內容為:
| 123456789101112131415161718192021222324252627282930 | "test.c" .rodata.LC0: "hello world" main main, @functionmain:.LFB0:pushl %ebp 8 5, -8movl %esp, %ebp 5andl $-16, %espsubl $16, %espmovl $.LC0, (%esp) call printf nop leave 5 4, 4 ret.LFE0: main, .-main "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2" .note.GNU-stack,"",@progbits~ |
Gcc編譯過程已經將其轉化為匯編語言了
匯編
option1?為-c,生成的目標文件為以.o為后綴的二進制目標代碼文件,Gcc指令為:
tly@ubuntu ~> gcc -c test.s -o test.o生成的匯編之后的目標文件內容為:
| 123 | ^?ELF^A^A^A^@^@^@^@^@^@^@^@^@^A^@^C^@^A^@^@^@^@^@^@^@^@^@^@^@^X^A^@^@^@^@^@^@4^@^@^@^@^@(^@^M^@^@U<89>?<83>?e<83>ì^P?^D$^@^@^@^@èü???<90>é?hello world^@^@GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2^@^@^@^@^T^@^@^@^@^@^@^@^AzR^@^A|^H^A^[^L^D^D<88>^A^@^@^\^@^@^@^\^@^@^@^@^@^@^@^X^@^@^@^@A^N^H<85>^BB^M^ET?^L^D^D^@^@^@.symtab^@.strtab^@.shstrtab^@.rel.text^@.data^@.bss^@.rodata^@.comment^@.note.GNU-stack^@.rel.eh_frame^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^_^@^@^@^A^@^@^@^F^@^@^@^@^@^@^@4^@^@^@^X^@^@^@^@^@^@^@^@^@^@^@^A^@^@^@^@^@^@^@^[^@^@^@ ^@^@^@^@^@^@^@^@^@^@^@?^C^@^@^P^@^@^@^K^@^@^@^A^@^@^@^D^@^@^@^H^@^@^@%^@^@^@^A^@^@^@^C^@^@^@^@^@^@^@L^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^A^@^@^@^@^@^@^@+^@^@^@^H^@^@^@^C^@^@^@^@^@^@^@L^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^A^@^@^@^@^@^@^@0^@^@^@^A^@^@^@^B^@^@^@^@^@^@^@L^@^@^@^L^@^@^@^@^@^@^@^@^@^@^@^A^@^@^@^@^@^@^@8^@^@^@^A^@^@^@0^@^@^@^@^@^@^@X^@^@^@%^@^@^@^@^@^@^@^@^@^@^@^A^@^@^@^A^@^@^@A^@^@^@^A^@^@^@^@^@^@^@^@^@^@^@}^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^A^@^@^@^@^@^@^@U^@^@^@^A^@^@^@^B^@^@^@^@^@^@^@<80>^@^@^@8^@^@^@^@^@^@^@^@^@^@^@^D^@^@^@^@^@^@^@Q^@^@^@ ^@^@^@^@^@^@^@^@^@^@^@?^C^@^@^H^@^@^@^K^@^@^@^H^@^@^@^D^@^@^@^H^@^@^@^Q^@^@^@^C^@^@^@^@^@^@^@^@^@^@^@?^@^@^@_^@^@^@^@^@^@^@^@^@^@^@^A^@^@^@^@^@^@^@^A^@^@^@^B^@^@^@^@^@^@^@^@^@^@^@ ^C^@^@°^@^@^@^L^@^@^@ ^@^@^@^D^@^@^@^P^@^@^@ ^@^@^@^C^@^@^@^@^@^@^@^@^@^@^@D^C^@^@^T^@^@^@^@^@^@^@^@^@^@^@^A^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^A^@^@^@^@^@^@^@^@^@^@^@^D^@??^@^@^@^@^@^@^@^@^@^@^@^@^C^@^A^@^@^@^@^@^@^@^@^@^@^@^@^@^C^@^C^@^@^@^@^@^@^@^@^@^@^@^@^@^C^@^D^@^@^@^@^@^@^@^@^@^@^@^@^@^C^@^E^@^@^@^@^@^@^@^@^@^@^@^@^@^C^@^G^@^@^@^@^@^@^@^@^@^@^@^@^@^C^@^H^@^@^@^@^@^@^@^@^@^@^@^@^@^C^@^F^@^H^@^@^@^@^@^@^@^X^@^@^@^R^@^A^@^M^@^@^@^@^@^@^@^@^@^@^@^P^@^@^@^@test.c^@main^@printf^@^L^@^@^@^A^E^@^@^Q^@^@^@^B^@^@ ^@^@^@^B^B^@^@ |
Gcc匯編成的.o目標文件是亂碼,不過可以通過nm命令查看其符號表:
tly@ubuntu ~> nm test.o 00000000 T mainU printf鏈接
在成功編譯之后,就進入了鏈接階段,這個hello world小程序的鏈接過程主要是查找包含的stdio.h頭文件的printf()函數的實現(因為stdio.h頭文件只包含函數聲明),這個函數實現是在libc.so.6的庫文件中。在沒有特別指定時,Gcc會到系統默認的搜索路徑/usr/lib下進行查找,也就是鏈接到libc.so.6庫函數中去,這樣就能實現函數printf()了,而這也就是鏈接的作用。
函數庫一般分為靜態庫和動態庫兩種。
靜態庫:是指編譯鏈接時,把庫文件的代碼全部加入到可執行文件中,因此生成的文件比較大,但在運行時也就不再需要庫文件了。其后綴名一般為.a(linux)或.lib(windows)。
動態庫: 與之相反,在編譯鏈接時并沒有把庫文件的代碼加入到可執行文件中,而是在程序執行時由運行時鏈接文件加載庫,這樣可以節省系統的開銷。動態庫一般為.so(linux)或.dll(windows),如前面所述的libc.so.6就是動態庫。
Gcc在編譯時默認使用動態庫
當然,也可以一次性使用-c選項,直接生成目標文件test.o,Gcc指令為:
tly@ubuntu ~> gcc -c test.c -o test.o完成了鏈接之后,Gcc就可以生成可執行文件test,Gcc指令為:
tly@ubuntu ~> gcc test.o -o test運行該可執行文件test:
tly@ubuntu ~> ./test hello world?Gcc編譯選項分析
Gcc有超過100個的可用選項,一般主要包括以下五種類型選項:
總體選項
| -E | 只是編譯不匯編,生成匯編代碼.s |
| -S | 只進行預編譯生成.i,不做其他處理 |
| -c | 只是編譯不鏈接,生成目標文件.o |
| -g | 在可執行程序中包含標準調試信息 |
| -o file | 把輸出文件輸出到file里 |
| -v | 打印出編譯器內部編譯各過程的命令行信息和編譯器的版本 |
| -I dir | 在頭文件的搜索路徑列表中添加dir目錄 |
| -L dir | 在庫文件的搜索路徑列表中添加dir目錄 |
| -static | 鏈接靜態庫 |
| -llibrary | 鏈接名為library的庫文件庫 |
對于-I dir選項可在頭文件的搜索路徑列表中添加dir目錄。由于Linux中頭文件都默認放到了/usr/include/目錄下,因此,當用戶希望添加放置在其他位置的頭文件時,就可以通過-I dir選項來指定(-L dir類似),這樣,Gcc就會到相應的位置查找對應的目錄
<>表示在標準路徑中搜索頭文件,“ ”表示在本目錄中搜索,如果把自定義的頭文件#include<my.h>改為#include “my.h”,就不需要加上“-I”選項了
-I dir和-L dir都只是指定了路徑,而沒有指定文件,因此不能在路徑中包含文件名
對于-llibrary選項,省去了前綴lib,它實際上是指示Gcc去連接庫文件liblibrary.so。由于在Linux下的庫文件命名時有一個規定:必須以lib三個字母開頭。因此在用-l選項指定鏈接的庫文件名時可以省去lib三個字母。也就是說Gcc在對-llibrary進行處理時,會自動去鏈接名為liblibrary.so的文件
告警和出錯選項
| -ansi | 支持符合ANSI標準的C程序 |
| -pedantic | 允許發出ANSI C標準所列的全部警告信息 |
| -pedantic-error | 允許發出ANSI C標準所列的全部錯誤信息 |
| -w | 關閉所有告警 |
| -Wall | 允許發出Gcc提供的所有有用的報警信息 |
修改上述的helloworld測試程序為:
| 1234567 | void main(int argc, char* argv[]){ long long tmp; // 增加非ANSI-C類型long long 未使用的臨時變量tmp printf("hello world"); return 0; // 返回錯誤類型int} |
1.默認無告警和出錯選項情況:
tly@ubuntu ~> gcc -c test.c -o test.o test.c: In function ‘main’: test.c:6:3: warning: ‘return’ with a value, in function returning void [enabled by default]return 0;^只識別了main的錯誤返回類型int
2.增加-ansi選項情況:
tly@ubuntu ~> gcc -c test.c -o test.o -ansi test.c: In function ‘main’: test.c:6:3: warning: ‘return’ with a value, in function returning void [enabled by default]return 0;^只識別了main的錯誤返回類型int
3.增加-pedantic選項情況:
tly@ubuntu ~> gcc -c test.c -o test.o -pedantic test.c:2:6: warning: return type of ‘main’ is not ‘int’ [-Wmain]void main(int argc, char* argv[])^ test.c: In function ‘main’: test.c:4:8: warning: ISO C90 does not support ‘long long’ [-Wlong-long]long long tmp;^ test.c:6:3: warning: ‘return’ with a value, in function returning void [enabled by default]return 0;^識別了main的錯誤返回類型int 和 long long 非 ISO C90 支持類型
4.增加-pedantic-errors選項情況:
tly@ubuntu ~> gcc -c test.c -o test.o -pedantic-errors test.c:2:6: error: return type of ‘main’ is not ‘int’ [-Wmain]void main(int argc, char* argv[])^ test.c: In function ‘main’: test.c:4:8: error: ISO C90 does not support ‘long long’ [-Wlong-long]long long tmp;^ test.c:6:3: error: ‘return’ with a value, in function returning voidreturn 0;^識別了main的錯誤返回類型int 和 long long 非 ISO C90 支持類型
5.增加-w選項情況:
tly@ubuntu ~> gcc -c test.c -o test.o -w屏蔽了告警和出錯信息
6.增加-Wall選項情況:
tly@ubuntu ~> gcc -c test.c -o test.o -Wall test.c:2:6: warning: return type of ‘main’ is not ‘int’ [-Wmain]void main(int argc, char* argv[])^ test.c: In function ‘main’: test.c:6:3: warning: ‘return’ with a value, in function returning void [enabled by default]return 0;^ test.c:4:13: warning: unused variable ‘tmp’ [-Wunused-variable]long long tmp;^識別了main的錯誤返回類型int 和臨時變量tmp未使用的告警信息
優化選項
| -On | n是一個代表優化級別的整數,典型的范圍是從0變化到2或3 |
不同的優化級別對應不同的優化處理工作。
-O 提供基礎級別的優化
-O2 提供更加高級的代碼優化,會占用更長的編譯時間
-O3 提供最高級的代碼優化
進行調試的時候,最好關閉編譯優化,否則程序自動優化,執行的步驟可能有變化
體系結構相關選項
| -mcpu=type | 對不同的CPU使用相應的CPU指令。可選擇的有i386、i486、pentium等 |
| -mieee-fp | 使用IEEE標準進行浮點數的比較 |
| -mno-ieee-fp | 不使用IEEE標準進行浮點數的比較 |
| -msoft-float | 輸出包含浮點庫調用的目標代碼 |
| -mshort | 把int類型作為16位處理,相當于short int |
| -mrtd | 將函數參數個數固定的函數用ret NUM返回,節省調用函數的一條指令 |
福利答謝大家! 感謝您閱讀本篇文章,對此特別發放一個無門檻的現金紅包,打開支付寶掃碼領取,可以領到錢的哦!
總結
以上是生活随笔為你收集整理的Gcc编译链接及常用选项总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 英伟达jetson tx1开发套件配置t
- 下一篇: Aria2 使用手札