天天看點

C程式編譯過程淺析【轉】

我現在一般都是用gcc,是以自然以GCC編譯hellworld為例,簡單總結如下。

hello.c源代碼如下:

<span style="color:#339933">#include <stdio.h></span>  

<span style="color:#993333">int</span> main<span style="color:#009900">(</span><span style="color:#009900">)</span>  

<span style="color:#009900">{</span>  

    <a href="http://www.opengroup.org/onlinepubs/009695399/functions/printf.html"><span style="color:#000066">printf</span></a><span style="color:#009900">(</span>“Hello<span style="color:#339933">,</span> world.\n”<span style="color:#009900">)</span><span style="color:#339933">;</span>  

    <span style="color:#b1b100">return</span> <span style="color:#0000dd">0</span><span style="color:#339933">;</span>  

<span style="color:#009900">}</span>  

通常我們使用gcc來生成可執行程式,指令為:gcc hello.c,預設生成可執行檔案a.out

其實編譯(包括連結)的指令:gcc hello.c 可分解為如下4個大的步驟:

預處理(Preprocessing)

編譯(Compilation)

彙編(Assembly)

連結(Linking)

C程式編譯過程淺析【轉】

gcc compilation

1.       預處理(Preproceessing)

預處理的過程主要處理包括以下過程:

将所有的#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.       編譯(Compilation)

編譯過程就是把預處理完的檔案進行一系列的詞法分析,文法分析,語義分析及優化後生成相應的彙編代碼。

$gcc –S hello.i –o hello.s

或者

注:現在版本的GCC把預處理和編譯兩個步驟合成一個步驟,用cc1工具來完成。gcc其實是背景程式的一些包裝,根據不同參數去調用其他的實際處理程式,比如:預編譯編譯程式cc1、彙編器as、連接配接器ld

可以看到編譯後的彙編代碼(hello.s)如下:

3.       彙編(Assembly)

彙編器是将彙編代碼轉變成機器可以執行的指令,每一個彙編語句幾乎都對應一條機器指令。彙編相對于編譯過程比較簡單,根據彙編指令和機器指令的對照表一一翻譯即可。

$ gcc –c hello.c –o hello.o

$ as hello.s –o hello.co

由于hello.o的内容為機器碼,不能以普通文本形式的檢視(vi 打開看到的是亂碼)。

4.       連結(Linking)

通過調用連結器ld來連結程式運作需要的一大堆目标檔案,以及所依賴的其它庫檔案,最後生成可執行檔案。

ld -static crt1.o crti.o crtbeginT.o hello.o -start-group -lgcc -lgcc_eh -lc-end-group crtend.o crtn.o (省略了檔案的路徑名)。

helloworld的大體編譯和連結過程就是這樣了,那麼編譯器和連結器到底做了什麼呢?

編譯過程可分為6步:掃描(詞法分析)、文法分析、語義分析、源代碼優化、代碼生成、目标代碼優化。

詞法分析:掃描器(Scanner)将源代的字元序列分割成一系列的記号(Token)。lex工具可實作詞法掃描。

文法分析:文法分析器将記号(Token)産生文法樹(Syntax Tree)。yacc工具可實作文法分析(yacc: Yet Another Compiler Compiler)。

語義分析:靜态語義(在編譯器可以确定的語義)、動态語義(隻能在運作期才能确定的語義)。

源代碼優化:源代碼優化器(Source Code Optimizer),将整個文法書轉化為中間代碼(Intermediate Code)(中間代碼是與目标機器和運作環境無關的)。中間代碼使得編譯器被分為前端和後端。編譯器前端負責産生機器無關的中間代碼;編譯器後端将中間代碼轉化為目标機器代碼。

目标代碼生成:代碼生成器(Code Generator).

目标代碼優化:目标代碼優化器(Target Code Optimizer)。

連結的主要内容是把各個子產品之間互相引用的部分處理好,使得各個子產品之間能夠正确地銜接。

連結的主要過程包括:位址和空間配置設定(Address and Storage Allocation),符号決議(Symbol Resolution),重定位(Relocation)等。

連結分為靜态連結和動态連結。

靜态連結是指在編譯階段直接把靜态庫加入到可執行檔案中去,這樣可執行檔案會比較大。

而動态連結則是指連結階段僅僅隻加入一些描述資訊,而程式執行時再從系統中把相應動态庫加載到記憶體中去。

靜态連結的大緻過程如下圖所示:

C程式編譯過程淺析【轉】

<dl><dd>static linking</dd></dl>

參考資料:

《程式員的自我修養——連結、裝載與庫》

<a href="http://www.stackpop.org/blog/html/y2011/53_cpp_compile_linking.html" target="_blank">http://www.stackpop.org/blog/html/y2011/53_cpp_compile_linking.html</a>

<a href="http://blog.chinaunix.net/space.php?uid=20196318&amp;do=blog&amp;id=28797" target="_blank">http://blog.chinaunix.net/space.php?uid=20196318&amp;do=blog&amp;id=28797</a>

本文轉自張昺華-sky部落格園部落格,原文連結:http://www.cnblogs.com/sky-heaven/p/6947171.html,如需轉載請自行聯系原作者

繼續閱讀