程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> C程序編譯過程淺析,編譯淺析

C程序編譯過程淺析,編譯淺析

編輯:C++入門知識

C程序編譯過程淺析,編譯淺析


前幾天看了《程序員的自我修養——鏈接、裝載與庫》中的第二章“編譯和鏈接”,主要根據其中的內容簡單總結一下C程序編譯的過程吧。

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

hello.c源代碼如下:

/* 何問起 hovertree.com */
 int main()
 { printf(“Hello, world.\n”); return 0; }

通常我們使用gcc來生成可執行程序,命令為:gcc hello.c,默認生成可執行文件a.out

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

    • 預處理(Preprocessing)
    • 編譯(Compilation)
    • 匯編(Assembly)
    • 鏈接(Linking)

 

gcc compilation

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

或者

$ /usr/lib/gcc/i486-linux-gnu/4.4/cc1 hello.c

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

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

.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.       匯編(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)等。

鏈接分為靜態鏈接和動態鏈接。

靜態鏈接是指在編譯階段直接把靜態庫加入到可執行文件中去,這樣可執行文件會比較大。

動態鏈接則是指鏈接階段僅僅只加入一些描述信息,而程序執行時再從系統中把相應動態庫加載到內存中去。

靜態鏈接的大致過程如下圖所示:

static linking

static linking

 

參考資料:

《程序員的自我修養——鏈接、裝載與庫》

 

推薦:http://www.cnblogs.com/roucheng/p/3454292.html

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved