程式由原始檔編譯得到可執行檔案看起來好像是很簡單的過程,windows的ide環境下,點一下bulid就可以生成可執行檔案,在linux環境下,gcc編譯器也提供了很多選項可以很方便的從原始檔生成可執行檔案。事實上程式的編譯和鏈結是乙個非常複雜的過程,ide幫我們隱藏了大量的細節。下面我們以最經典的hello,world程式來分析一下,從源程式到生成可執行檔案都發生了哪些事情。
檔案內容
#includeint main()
##1. 預處理
預處理的主要作用有三個:
處理原始檔**中以」#」開頭的那些**,比如#include,#define等。將所有的巨集定義全部展開。
處理條件編譯
刪除所有注釋
通過gcc的-e選項來預處理main.c檔案。
檢視一下生成的main.i的檔案內容,可以看到stdio.h檔案內的內容已經全部被插入到檔案當中了,同時所有的巨集定義也被展開(main.i檔案很長,擷取的是一部分)。在檔案的最後是我們main函式的**。
#2. 編譯
編譯就是將預處理得到的*.i檔案生成彙編**檔案(.s檔案)。注意:通常所說的編譯是指程式從源**到生成可執行檔案的全過程,在這裡指的由.i檔案生成*.s檔案的過程。輸入下面的命令來實現編譯過程,得到main.s檔案。
gcc –s main.i –o main.s#3. 彙編
彙編就是將彙編**變成機器碼。每一條彙編語句幾乎都對應了一條機器碼。所以這一步驟非常簡單,根據彙編指令和機器指令對照表一對一翻譯就ok。輸入下面的指令實現這一過程。
gcc –c main.s –o main.o
此時,main.o檔案已經生成,這個時候main.o已經是乙個二進位制檔案,不能用cat檢視,用hexdump來看一下main.o檔案內容。
生成main.out可執行檔案。
以上就是從原始檔到可執行檔案的全部過程。這是乙個很簡單的範例,在main.c檔案中僅包含了標準標頭檔案,沒有包含自定義的標頭檔案。下面介紹乙個簡單的例子。
首先準備三個檔案
檔案內容
#include#include "fun.h"
int main()
檔案內容
#ifndef fun_h
#define fun_h
int calc(int a,int b);
#endif
檔案內容
#include"fun.h"
int calc(int a,int b)
在main.c檔案中包含了fun.h檔案,並呼叫了fun.h中定義的calc函式,calc函式的實現在calc.c檔案中。
#1.預處理、編譯、彙編main.c檔案
此時main.o檔案是不能生成可執行檔案的。
編譯器提示undefined reference to `calc』,因為我們沒有鏈結庫檔案。
#2.建立靜態庫檔案
1.首先,編譯fun.c得到fun.o目標檔案
2.然後,建立靜態庫檔案
3.在某些系統中,還要為靜態庫生成乙個內容表
ranlib libfun.a
4.庫檔案建立完成後,就可以鏈結了。
#3.程式鏈結靜態庫檔案
前面已經生成了main.o文和libfun.a檔案,現在我們鏈結一下,生成可執行檔案。
gcc main.o -o main.out -l. -l fun
注意一下這條語句的規則,-l表示指定庫檔案目錄,」.」表示當前資料夾,這裡也可以寫出目錄完整路徑。-l fun表示指定庫檔名,庫檔案的完整名是libfun.a,前面的lib和字尾.a均省略。
可見,main.out檔案已經生成,可以執行
程式編譯,鏈結過程
c語言的編譯鏈結過程要把我們編寫的乙個c程式 源 轉換成可以在硬體上執行的程式 可執行 需要進行編譯和鏈結。編譯就是把文字形式源 翻譯為機器語言形式的目標檔案的過程。鏈結是把目標檔案 作業系統的啟動 和用到的庫檔案進行組織形成最終生成可載入 可執行 的過程。過程 如下 預處理器 將.c 檔案轉化成 ...
程式的編譯鏈結過程
一段源 到可執行性程式需要經歷預處理 編譯彙編和鏈結等步驟,接下來詳細介紹這些過程 假設檔案main.czhong有如下 include int main 1 巨集定義指令 2 條件編譯 3 標頭檔案包含指令 4 特殊符號處理 不能在標頭檔案中定義全域性變數,因為在標頭檔案中定義全域性變數將會使所有...
c 編譯鏈結過程
llinux下編譯乙個c 程式的典型過程 1.編譯預處理 預編譯程式完成的工作,可以說成是對源程式的 替換 工作。經過這個過程,生成乙個沒有巨集定義 沒有條件編譯指令 沒有特殊符號的輸出檔案。2.編譯 優化階段 通過詞法分析 語法分析,在確認所有的指令都符合語法規則之後,將其翻譯成等價的中間 或彙編...