後綴名的說明:
.i 已經過預處理的C原始程序 .ii 已經過預處理的C++原始程序 .s/.S 彙編語言原始程序 .h 預處理文件(頭文件) .o 目標文件 .a/.so 編譯後的庫文件 |
GCC編譯流程分爲4個步驟
1. 預處理(Preprocessing)
2. 編譯(Compiling)
3. 彙編(Assembling)
4. 鏈接(Linking)
test.c
1 #include <stdio.h> 2 int main(void) 3 { 4 printf("Hello World!/n"); 5 return 0; 6 } |
1. 預處理(Preprocessing):在這個階段GCC將頭文件包含進test.c文件,並生成文件test.i。
執行下面命令生成test.i:
[root@localhost gccinfo]# gcc -E test.c -o test.i |
test.i的文件內容:
1 # 1 "test.c" 2 # 1 "<built-in>" 3 # 1 "<command-line>" 4 # 1 "test.c" 5 # 1 "/usr/include/stdio.h" 1 3 4 6 # 28 "/usr/include/stdio.h" 3 4 7 # 1 "/usr/include/features.h" 1 3 4 8 # 335 "/usr/include/features.h" 3 4 9 # 1 "/usr/include/sys/cdefs.h" 1 3 4 10 # 360 "/usr/include/sys/cdefs.h" 3 4 …………. 750 # 2 "test.c" 2 751 int main(void) 752 { 753 printf("Hello World!/n"); 754 return 0; 755 } |
2. 編譯(Compiling):在這個階段GCC檢查test.i代碼的規範性,確定有沒有語法錯誤。確認無誤後,則把test.i轉化爲彙編代碼文件test.s。
執行下面命令生成test.s:
[root@localhost gccinfo]# gcc -S test.i -o test.s |
test.s的文件內容:
1 .file "test.c" 2 .section .rodata 3 .LC0: 4 .string "Hello World!" 5 .text 6 .globl main 7 .type main, @function 8 main: 9 leal 4(%esp), %ecx 10 andl $-16, %esp 11 pushl -4(%ecx) 12 pushl %ebp 13 movl %esp, %ebp 14 pushl %ecx 15 subl $4, %esp 16 movl $.LC0, (%esp) 17 call puts 18 movl $0, %eax 19 addl $4, %esp 20 popl %ecx 21 popl %ebp 22 leal -4(%ecx), %esp 23 ret 24 .size main, .-main 25 .ident "GCC: (GNU) 4.3.0 20080428 (Red Hat 4.3.0-8)" 26 .section .note.GNU-stack,"",@progbits |
3. 彙編階段(Assembling):在這個階段GCC把test.s文件中的彙編代碼轉化爲目標機器的機器代碼文件test.o,爲二進制文件。
執行下面命令生成test.o:
[root@localhost gccinfo]# gcc -c test.s -o test.o |
執行以下命令產看test.o
[root@localhost gccinfo]# od -t c test.o |
test.o文件內容:
0000000 177 E L F 001 001 001 /0 /0 /0 /0 /0 /0 /0 /0 /0 0000020 001 /0 003 /0 001 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 0000040 354 /0 /0 /0 /0 /0 /0 /0 4 /0 /0 /0 /0 /0 ( /0 0000060 /v /0 /b /0 215 L $ 004 203 344 360 377 q 374 U 211 0000100 345 Q 203 354 004 307 004 $ /0 /0 /0 /0 350 374 377 377 0000120 377 270 /0 /0 /0 /0 203 304 004 Y ] 215 a 374 303 /0 0000140 H e l l o W o r l d ! /0 /0 G C 0000160 C : ( G N U ) 4 . 3 . 0 2 0000200 0 0 8 0 4 2 8 ( R e d H a t 0000220 4 . 3 . 0 - 8 ) /0 /0 . s y m t 0000240 a b /0 . s t r t a b /0 . s h s t 0000260 r t a b /0 . r e l . t e x t /0 . 0000300 d a t a /0 . b s s /0 . r o d a t 0000320 a /0 . c o m m e n t /0 . n o t e 0000340 . G N U - s t a c k /0 /0 /0 /0 /0 /0 0000360 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 * 0000420 /0 /0 /0 /0 037 /0 /0 /0 001 /0 /0 /0 006 /0 /0 /0 0000440 /0 /0 /0 /0 4 /0 /0 /0 + /0 /0 /0 /0 /0 /0 /0 0000460 /0 /0 /0 /0 004 /0 /0 /0 /0 /0 /0 /0 033 /0 /0 /0 0000500 /t /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 X 003 /0 /0 0000520 020 /0 /0 /0 /t /0 /0 /0 001 /0 /0 /0 004 /0 /0 /0 0000540 /b /0 /0 /0 % /0 /0 /0 001 /0 /0 /0 003 /0 /0 /0 0000560 /0 /0 /0 /0 ` /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 0000600 /0 /0 /0 /0 004 /0 /0 /0 /0 /0 /0 /0 + /0 /0 /0 0000620 /b /0 /0 /0 003 /0 /0 /0 /0 /0 /0 /0 ` /0 /0 /0 0000640 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 004 /0 /0 /0 0000660 /0 /0 /0 /0 0 /0 /0 /0 001 /0 /0 /0 002 /0 /0 /0 0000700 /0 /0 /0 /0 ` /0 /0 /0 /r /0 /0 /0 /0 /0 /0 /0 0000720 /0 /0 /0 /0 001 /0 /0 /0 /0 /0 /0 /0 8 /0 /0 /0 0000740 001 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 m /0 /0 /0 0000760 - /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 001 /0 /0 /0 0001000 /0 /0 /0 /0 A /0 /0 /0 001 /0 /0 /0 /0 /0 /0 /0 0001020 /0 /0 /0 /0 232 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 0001040 /0 /0 /0 /0 001 /0 /0 /0 /0 /0 /0 /0 021 /0 /0 /0 0001060 003 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 232 /0 /0 /0 0001100 Q /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 001 /0 /0 /0 0001120 /0 /0 /0 /0 001 /0 /0 /0 002 /0 /0 /0 /0 /0 /0 /0 0001140 /0 /0 /0 /0 244 002 /0 /0 240 /0 /0 /0 /n /0 /0 /0 0001160 /b /0 /0 /0 004 /0 /0 /0 020 /0 /0 /0 /t /0 /0 /0 0001200 003 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 D 003 /0 /0 0001220 022 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 001 /0 /0 /0 0001240 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 0001260 /0 /0 /0 /0 001 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 0001300 004 /0 361 377 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 0001320 003 /0 001 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 0001340 003 /0 003 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 0001360 003 /0 004 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 0001400 003 /0 005 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 0001420 003 /0 /a /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 0001440 003 /0 006 /0 /b /0 /0 /0 /0 /0 /0 /0 + /0 /0 /0 0001460 022 /0 001 /0 /r /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 /0 0001500 020 /0 /0 /0 /0 t e s t . c /0 m a i n 0001520 /0 p u t s /0 /0 /0 024 /0 /0 /0 001 005 /0 /0 0001540 031 /0 /0 /0 002 /t /0 /0 /n 0001551 |
4. 鏈接(Linking):在成功編譯之後,就進入了鏈接階段。在這裏涉及到一個重要的概念:函數庫。重新查看這個小程序時,在這個程序中並沒有定義“printf”的函數實現,且在預編譯中包含進的“stdio.h”中也只有該函數的聲明,而沒有定義函數的實現,那麼,是在哪裏實現“printf”函數的呢?最後的答案是:系統把這些函數實現都被做到名爲 libc.so.6 的庫文件中去了,在沒有特別指定時,Gcc 會到系統默認的搜索路徑“/usr/lib”下進行查找,也就是鏈接到 libc.so.6 庫函數中去,這樣就能實現函數“printf”了,而這也就是鏈接的作用。
函數庫一般分爲靜態庫和動態庫兩種。靜態庫是指編譯鏈接時,把庫文件的代碼全部加入到可執行文件中,因此生成的文件比較大,但在運行時也就不再需要庫文件了。其後綴名一般爲“.a” 。動態庫與之相反,在編譯鏈接時並沒有把庫文件的代碼加入到可執行文件中,而是在程序執行時由運行時鏈接文件加載庫,這樣可以節省系統的開銷。動態庫一般後綴名爲“.so”,如前面所述的 libc.so.6 就是動態庫。GCC 在編譯時默認使用動態庫。完成了鏈接之後,GCC 就可以生成可執行文件。
執行以下命令生成可執行文件test
[root@localhost gccinfo]# gcc test.o -o test |
同時也可以通過od命令產看其內容。
以上4個編譯步驟可以一次完成,執行下面命令即可:
[root@localhost gccinfo]# gcc test.c -o test |
編譯程序時,gcc會自動幫你連接所有編譯的文件名及標準函數庫. 首先GCC會把所有的原始文件都轉成目標文件,然後會自動調用linker 來連接相關文件名(事實上linker是個文件名爲ld的程序,而不是gcc 本身提供的功能,我們可以說gcc和ld的關係是相當密切的),GG同樣 也知道標準函數庫的位置,並且在調用ld時傳入相關信息。