一個c語言程序從源文件到生成可執行文件,編譯器需要共經歷4個步驟:
1)預處理:把c文件中預處理命令掃描處理完畢,即對源代碼文件中的文件包含(#include)、預編譯語句(如宏定義#define等)進行分析,此時生成的文件仍然是可讀的。
2) 編譯:把預處理後的結果編譯成彙編或者目標模塊,即生成彙編語言文件,此時生成的文件仍然是可讀的彙編文件。
3)彙編:把編譯出來的結果彙編成具體CPU上的目標代碼模塊,也即此時轉換成具體的機器語言代碼,此時生成的文件是不可讀的非文本文件。
4)連接:把多個目標代碼模塊連接生成一個大的目標模塊,即將多個上面產生的機器代碼文件(與其它的機器代碼文件和庫文件)彙集成一個可執行的二進制代碼文件。
gcc作爲c語言在linux下很著名的編譯軟件,分別有如下option來支持4個步驟:
名稱 gcc選項 英文名稱 gcc調用的程序
示例
預處理 -E Pre-Processing cpp gcc
-E test.c -o test.i
編譯 -S Compiling ccl gcc
-S test.i -o test.s
彙編 -c Assembling as gcc
-c test.s -o test.o
連接 無 Linking ld gcctest.o
-o test
說明:
gcc在編譯c語言文件時,首先調用cpp進行預處理,在預處理過程中,對源代碼文件中的文件包含(#include)、預編譯語句(如宏定義#define等)進行分析;其次調用ccl進行編譯工作,將文件編譯成彙編語言文件,此時文件依舊是可讀的;之後調用as進行彙編工作,將具體的彙編語言文件編譯成cpu可執行的目標代碼,此時文件不可讀了;當所有的目標文件都生成之後,gcc就調用ld來完成最後的關鍵性工作,鏈接。在鏈接階段,所有的目標文件被安排在可執行程序中的恰當的位置,同時,該程序所調用到的庫函數也從各自所在的庫中鏈接到合適的地方。下面對上面4個過程做下分別的說明:
1、在預處理階段,如果不用“-o”指定文件名,那麼會默認將預處理結果輸出到標準終端設備。
[root@dbbak tmp]# cat a.c
int main()
{
printf("shengtong test!\n");
}
[root@dbbak tmp]#
gcc -E a.c
# 1 "a.c"
# 1 "<built-in>"
# 1 "<command line>"
# 1 "a.c"
int main()
{
printf("shengtong test!\n");
}
[root@dbbak tmp]# gcc -E a.c -oa.i
[root@dbbak tmp]# cat a.i
# 1 "a.c"
# 1 "<built-in>"
# 1 "<command line>"
# 1 "a.c"
int main()
{
printf("shengtong test!\n");
}
2、在編譯階段,如果不用“-o”指定文件名,那麼默認會生成一個“*.s”的彙編語言文件。
[root@dbbak tmp]#
gcc -S a.i
[root@dbbak tmp]# ls
a.c a.i a.s
[root@dbbaktmp]# gcc -S a.i -oa1.s
[root@dbbak tmp]# ls
a.c a.i a.s a1.s
[root@dbbak tmp]#
[root@dbbak tmp]# cat a.s
.file "a.c"
.section .rodata
.LC0:
.string "shengtong test!\n"
.text
.globl main
.type main,@function
main:
pushl %ebp
movl %esp,%ebp
subl $8,%esp
andl $-16,%esp
movl $0,%eax
subl %eax,%esp
subl $12,%esp
pushl $.LC0
call printf
addl $16,%esp
leave
ret
.Lfe1:
.size main,.Lfe1-main
.section .note.GNU-stack,"",@progbits
.ident "GCC: (GNU) 3.2.3 20030502 (Red Hat Linux3.2.3-42)"
[root@dbbak tmp]# cat a1.s
.file "a.c"
.section .rodata
.LC0:
.string "shengtong test!\n"
.text
.globl main
.type main,@function
main:
pushl %ebp
movl