天天看點

編譯系統(預處理、編譯、彙編、連結)-詳解

讓我們來用最簡單的程式了解一下我們的編譯系統

#include <hello.c>

int main()
{
    printf("hello world\n");
    return ;
}
           

hello程式的生命周期是從一個進階語言C語言程式開始,因為這種形式能夠人讀懂。然而,為了在系統上運作hello.c程式,每條C語句都必須被其他程式轉化為一系列的低級機器語言指令。然後這些指令按照一種稱為可執行目标程式的格式打包好,并以二進制磁盤檔案的形式存放起來。目标程式也稱為可執行目标檔案

在Linux系統上,從源檔案到目标檔案的轉化是由編譯器驅動程式完成的:

在這裡,GCC編譯器驅動程式讀取源程式檔案hello.c,并把它翻譯成為一個可執行目标檔案hello。這個翻譯過程可分為四個階段完成,稱為編譯系統、如下圖。

編譯系統(預處理、編譯、彙編、連結)-詳解

預處理階段:

預處理器(cpp)根據以字元#開頭的指令,修改原始的C程式。比如hello.c中第一行的

#include<stdio.h>

指令告訴預處理器讀取系統頭檔案stdio.h的内容,并且把它直接插入到程式文本中,結果就得到了另一個C程式,通常是以 .i 作為檔案擴充名。

編譯階段:

編譯器(ccl)将文本檔案hello.i翻譯成文本檔案hello.s,它包含一個彙編語言程式。彙編語言程式中的每條語句都以一種标準的文本格式确切的描述了一條低級機器語言指令。彙編語言是非常有用的,因為它為不同的進階語言的不同編譯器提供了一個通用的輸出語言。例如,C編譯器和Fortran編譯器産生的輸出檔案用的都是一樣的彙編語言。

彙編階段 :

接下來,彙編器(as)将hello.s翻譯成機器語言指令,把這些指令打包成一種叫做可重定位目标程式的格式,并将結果儲存在目标檔案hello.o中。hello.o檔案是一個二進制檔案,它的位元組編碼是機器語言指令而不是字元。如果我們在文本編譯器中打開hello.o檔案,看到的将是一堆亂碼。

連結階段:

請注意:hello程式調用了一個printf函數,它是每個C編譯器都會提供的标準C庫中的一個函數,printf函數存在于一個名為printf.o的單獨預編譯好了的标準檔案中,而這個檔案必須以某種方式合并到我們的hello.o程式中,連結器(ld)就負責處理這種合并,結果就得到hello檔案,他是一個可執行目标檔案(簡稱:可執行檔案),可以被加載到記憶體中,有系統執行。