­

C程式從編譯到運行

第一篇文章

一、前言

最近在看CSAPP(深入理解電腦系統)然後以前也學過C語言,但是從來沒有深究寫好的C程式碼是怎麼編譯再到執行的。

所以現在自己學習,然後記錄下來。

以最常用的hello world!程式為例  程式名:  main.c

#include <stdio.h>

int main()
{
    printf("Hello world!\n");
    return 0;
}

 

二、C程式編譯過程

hello程式的生命周期是從一個高級C語言程式開始的,為了能夠運行hello.c程式,每一條C語句都被其他程式轉化為一系列的低級機器語言指令。然後這些指令按照一種稱為可執行目標程式的格式打包,以二進位磁碟文件的形式存放起來。目標程式也稱為可執行目標文件


 

編譯一個 C程式可以分為四階段:預處理階段 —> 生成彙編程式碼階段 —> 彙編階段 —> 鏈接階段


 

各個階段的程式碼可以通過gcc指令來生成

如果沒有gcc可以用下面指令安裝

sudo apt-get  build-dep  gcc

安裝完之後可以根據以下指令查看是否安裝成功

gcc --version

 

安裝好後用下面指令生成中間文件

gcc main.c                      直接生成可執行文件 a.out
gcc -E main.c -o hello.i        生成預處理後的程式碼
gcc –S main.c -o hello.s        生成彙編程式碼
gcc –c main.c -o hello.o        生成目標程式碼

 

三、階段過程

  1、預處理階段

gcc -E main.c -o hello.i        生成預處理後的程式碼

 

  預處理器(cpp)根據以字元 # 開頭的命令,修改原始的C程式。比如mian.c中第一行的 #include<stdio.h> 命令就告訴預處理器讀取系統頭文件stdio.h的內容,並且把它直接插入程式文本中。同時刪除注釋行,添加行號和文件名標識。這樣就得到了另一個C程式,通常是以  .i  作為文件擴展名。 所以經過預編譯的 .i 文件是不包含宏定義的。

  處理完後我們來看看 hello.i 文件。發現原來的7行程式碼變成了700多行,我們的程式碼在最後面。而前面多出來的程式碼就是 .c 中#include<stdio.h>展開的程式碼。

 

 

 

  2、編譯階段

 

gcc –S main.c -o hello.s     生成彙編程式碼

  編譯是將源文件(hello.i)翻譯成彙編文件(hello.s)的過程。中間包含詞法、語法分析等步驟,具體過程可以參考《編譯原理》。

  打開彙編程式碼我們會發現裡面有很多以   . 開頭的行,所有這些以 . 開頭的行都是指導彙編器和鏈接器工作的偽指令。 我們通常可以忽略這些行。

 

  去掉這些行後剩下的部分。

 

  3、彙編階段

  

gcc –c main.c -o hello.o        生成目標程式碼

彙編階段是把編譯階段生成的  .s 文件轉成 .o 的二進位目標程式碼。彙編器(as)將 hello.s 翻譯成機器語言指令,把這些指令打包成一種叫做可重定位目標程式的格式,並將結果保存在目標文件hello.o中。hello.o文件是一個二進位文件,它的位元組編碼是機器語言指令而不是字元。如果我們在文本編譯器中打開 hello.o 文件,看到的將是一堆亂碼。

你非要看就是這樣

 

 

  4、鏈接階段

   這個階段就是把彙編後的機器指令集變成可以直接運行的文件,而對目標文件進行鏈接主要是因為在目標文件中可能用到了在其他文件當中定義的欄位(或者函數),通過鏈接來把多個不同目標文件關聯到一起。

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

這一部分可以參考《程式設計師的自我修養》

 

Tags: