經常看到文件編譯,編譯到底是干嘛?
1、什么是編譯?
對於同一個語句,有高級語言、低級語言、機器語言的方式來表示。
C語言:
a=b+1;
匯編語言:
mov -0xc(%ebp),%eax add $0x1,%eax mov %eax,-0x8(%ebp)
機器語言:
8b 45 f4 83 c0 01 89 45 f8
因為機器是只能做數字計算的,能夠讓機器去運算的,數字的語言就是機器語言。除此之外的所有計算機語言都是非機器語言。為了讓機器可理解,相對於機器語言的高級語言都需要一個轉換,從高級、機器不可理解,轉換為機器可理解的機器語言,這樣的一個轉換過程就叫做 編譯(Compile),由 編譯器(Compiler)來完成。
由 C 轉換為 匯編語言 這一過程是由 匯編器(Assembler)來執行的。
C 和 匯編語言 轉換為 機器語言 都是由 編譯器 來完成的。
2、將C語言編譯為機器語言的整個過程
在Linux下,使用gcc編譯器:
1、預編譯hello.c文件:
gcc -E -o hello.i hello.c
執行成功后,會生成一個新的hello.i 的文件,可以用編輯器(Vim)查看它的內容,這個文件就是經過預編譯后的內容。
預編譯又稱為預處理,是做些代碼文本的替換工作。預編譯可以處理#開頭的指令,比如拷貝#include包含的文件代碼,#define的宏定義的替換,條件編譯等。
預處理的過程主要處理包括以下過程:
將所有的#define刪除,並且展開所有的宏定義;
處理所有的條件預編譯指令,比如 #if #ifdef #elif #else #endif等;
處理#include預編譯指令,將被包含的文件插入到該預編譯指令的位置;
刪除所有注釋 "//" 和 "/**/";
添加行號和文件標識,以便編譯時產生調試用的行號及編譯錯誤警告行號;
保留所有的#pragma編譯器指令,因為編譯器需要使用它們;
通常使用以下命令來處理預處理:
gcc -E hello.c -o hello.i
參數-E表示只進行預處理,或者也可以使用以下指令完成預處理過程:
cpp hello.c > hello.i /* cpp - The C Preprocessor*/
直接cat hello.i 你就可以看到預處理后的代碼。
2、純粹的進行編譯:
gcc -S -o hello.s hello.i
把.i文件寫為hello.c也行,就是跳過手動預編譯,直接完成預編譯和編譯兩個過程。這時會得到一個hello.s文件,打開看一下,里面是編譯好的適用於當前體系結構的匯編代碼。
編譯過程就是把預處理完的文件進行一系列的詞法分析、語法分析、語義分析及優化后生成相應的匯編代碼。
gcc -S hello.i -o hello.s
或者:
/usr/lib/gcc/i486-linux-gnu/4.4/cc1 hello.c
注:現在版本的gcc把預處理和編譯兩個步驟合成一個步驟,用cc1工具來完成。gcc其實是后台程序的一些包裝,根據不同參數去調用其他的實際處理程序,比如:預編譯編譯程序cc1、匯編器as、連接器ld。可以看到編譯后的匯編代碼(hello.s)如下:
.file "hello.c" .section .rodata .LC0: .string "Hello, world." .text .globl main .type main, @function main: pushl %ebp movl %esp, %ebp andl $-16, %esp subl $16, %esp movl $.LC0, (%esp) call puts movl $0, %eax leave ret .size main, .-main .ident "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3" .section .note.GNU-stack,"",@progbits
3、把匯編代碼進行匯編可執行:
gcc -c -o hello.o hello.s
把.s文件換成.c也行,就是自動完成預編譯、編譯和匯編三個過程。現在得到一個hello.o文件,這是一個二進制文件,但不是最后的可執行二進制文件,因為它還缺少最后一步鏈接處理。
匯編器是將匯編代碼轉變成機器可以執行的命令,每一個匯編語句幾乎都對應一條機器指令。匯編相對於編譯過程比較簡單,根據匯編指令和機器指令的對照表一一翻譯即可。
gcc -c hello.c -o hello.o
或者:
as hello.s -o hello.co
由於hello.o的內容為機器碼,不能以普通文本形式查看(vi打開看到的是亂碼)。
4、鏈接
最后對.o文件進行鏈接,這里就一個.o文件所以簡單,經常是需要有多個.o文件需要鏈接。鏈接執行:
gcc -o hello hello.o
如果把最后的.o文件寫成.c,那就和最開始我們用hello.c編譯時示范的那樣了。實際上那樣是完成了預編譯、編譯、匯編和鏈接一連串的過程。
通過調用鏈接器ld來鏈接程序運行需要的一大堆目標文件,以及所依賴的其它庫文件,最后生成可執行文件。
ld -static crt1.o crti.o crtbeginT.o hello.o -start-group -lgcc -lgcc_eh -lc-end-group crtend.o crtn.o (省略了文件的路徑名)