Gcc編譯鏈接及常用選項總結


轉載文章:http://www.franktly.com

前言


GNU CC(簡稱Gcc)是GNU項目中符合ANSI C標準的編譯系統,能夠編譯用C、C++和Object- C等語言編寫的程序。Gcc不僅功能強大,而且可以編譯如C、C++、Object C、Java等多種語言,而且Gcc又是一個交叉平臺編譯器,它能夠在當前CPU平臺上爲多種不同體系結構的硬件平臺開發軟件。本章中的示例均採用Gcc版本爲4.8.2。

Gcc編譯鏈接流程


Gcc編譯鏈接流程分爲四個步驟:

  1. 預處理(Pre-Processsing)
  2. 編譯(Compiling)
  3. 彙編(Assembling)
  4. 鏈接(Linking)

Gcc指令的一般格式爲:

gcc [option1] compile-files [option2] object-files

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

下面以簡單的hello world程序爲例說明Gcc編譯的四個過程:

1
2
3
4
5
6
#include <stdio.h>
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

生成的預編譯文件內容爲:

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
# 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 4
extern 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" 2
void 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

生成的編譯之後的彙編文件內容爲:

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
.file "test.c"
.section .rodata
.LC0:
.string "hello world"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
andl $-16, %esp
subl $16, %esp
movl $.LC0, (%esp)
call printf
nop
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2"
.section .note.GNU-stack,"",@progbits
~

Gcc編譯過程已經將其轉化爲彙編語言了

彙編

option1 爲-c,生成的目標文件爲以.o爲後綴的二進制目標代碼文件,Gcc指令爲:

tly@ubuntu ~> gcc -c test.s -o test.o

生成的彙編之後的目標文件內容爲:

1
2
3
^?ELF^A^A^A^@^@^@^@^@^@^@^@^@^A^@^C^@^A^@^@^@^@^@^@^@^@^@^@^@^X^A^@^@^@^@^@^@4^@^@^@^@^@(^@^M^@
^@U<89>å<83>äð<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^@^@^@^@^@^@^@^@^@^@^@Ð^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 main
         U 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個的可用選項,一般主要包括以下五種類型選項:

  1. 總體選項
  2. 告警和出錯選項
  3. 優化選項
  4. 體系結構相關選項

總體選項


選項名 選項意義
-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測試程序爲:

1
2
3
4
5
6
7
#include <stdio.h>
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 void
   return 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返回,節省調用函數的一條指令

福利答謝大家!
感謝您閱讀本篇文章,對此特別發放一個無門檻的現金紅包,打開支付寶掃碼領取,可以領到錢的哦!



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章