C程式編譯執行過程

2021-08-08 18:27:33 字數 4038 閱讀 4664

c語言從源**到可執行檔案的過程:

1 編譯預處理

讀取c源**,對其中的偽指令(以#開頭的指令)和特殊符號進行處理

偽指令主要包括以下四個方面:

1.1 巨集定義指令

#define  定義巨集

#undef  取消巨集的定義

預定義巨集:

標準c中定義了一些物件巨集,這些巨集的名稱以「__」(兩個下劃線)開頭和結尾,並且都是大寫字元。這些巨集可以被#undef,也可以被重定義。

__line__  當前語句所在的行號,以十進位制數標註

__file__   當前原始檔的檔名,以字串標註 _

_date_

_  程式被編譯日期,以「mmm dd yyyy」格式的字串標註 _

_time_

_   程式被編譯時間,以「hh:mm:ss」格式字串標註

1.2 條件編譯指令

可以通過定義不同的巨集來決定編譯程式對哪些**進行處理,預編譯將根據有關檔案,將不必要的**過濾掉

(1) #if 常量表示式: 與if…else…執行過程類似,表示式為真則中間程式段被編譯,否則跳過去不編譯

— 常量表示式可以是巨集,算術運算,邏輯運算等

— 如果表示式是未定義的巨集,那它的值將被視為0,但一般避免使用#if 巨集的形式,因為該巨集值也有可能是被定義為0

— 使用它們可以提公升**的可移值性-針對不同平台執行不同的語句,也經常用於大段**注釋:

#if 0

一大段**;

#endif

(2) #ifdef和#ifndef:判斷巨集是否被定義

— 它們經常用於避免標頭檔案的重複引用

#ifndef _file_h_

#define _file_h_

#include "file.h"

#endif

(3)defined(name) :若巨集被定義,則返回1,否則返回0

通常與#if結合使用判斷巨集是否被定義

1.3 標頭檔案包含指令

(1)系統提供的標頭檔案,在程式中要使用尖括號包含,一般被放在/usr/include目錄下

eg:   #include

(2)使用者自定義的標頭檔案,在程式中要使用雙引號包含,與c源程式放在同一路徑下

eg:   #include "filename"

1.4 特殊符號

#line  標誌該語句所在的行號

#pragma   說明編譯器資訊

#warning   顯示編譯警告資訊

#error   顯示編譯錯誤資訊

1.4.1 用#line 來修改__line__和__file_

_         

printf("line:%d,file:%s\n",_line_, _file_);

#line 100 "urc"

printf("line:%d,file:%s\n",_line_, _file_);

printf("line:%d,file:%s\n",_line_, _file_);

結果:line:6,file:e:\data\1.c

line:100,file:urc

line:101,file:urc

總結:預編譯程式所完成的基本上是對源程式的「替代」工作— 展開所有的#define巨集定義

— 處理所有條件預編譯指令

— 處理「#include」預編譯指令,將被包含的檔案插入到預編譯指令的位置。

— 刪除所有的注釋「//」等

— 新增行號和檔名標識

— 保留所有的#pragma編譯器指令,因為編譯器需要使用它

2 編譯階段

編譯程式所要作得工作就是通過詞法分析和語法分析,在確認所有的指令都符合語法規則之後,將其翻譯成等價的中間**表示或彙編**。

3 優化階段

優化處理是編譯系統中一項比較艱深的技術。

優化一種是對中間**的優化,這種優化不依賴於具體的計算機;另一種優化則主要針對目標**的生成而進行的。

對於前一種優化,主要的工作是刪除公共表示式、迴圈優化(**外提、強度削弱、變換迴圈控制條件、已知量的合併等)

、複寫傳播,以及無用賦值的刪除,等等。

後一種型別的優化同機器的硬體結構密切相關,最主要的是考慮是如何充分利用機器的各個硬體暫存器存放的有關變

量的值,以減少對於記憶體的訪問次數。

3.1 源**優化

源**級別的優化是主要的一類優化,主要優化工作為:

●常量摺疊

編譯時計算出常量表示式的值

●常量傳播

如果變數被賦值以常量,常量傳播會將所有變數替換成常量。

eg:t1=2+6

t2=index + 4

t3=t2*t1

優化後:

t2=index + 4

t3=t2*8

編譯器將常數運算結果計算後賦值,變數t1被優化為常量數值

●死**刪除

刪除不會被執行的目標**

●公共子表示式消除

表示式在函式過個地方出現,如果變數值沒有改變,則僅計算一次。

●強度削弱

識別出運算量大的操作,並將其替換為開銷較小的等價機器指令序列。

比如,把乘法優化為位移+加法

●歸納當乙個變數的值依賴於另外乙個時,通常省去計算中間變數的過程,直接引用原值本身。

eg: 函式內的static區域性變數作為中間變數使用而被優化為區域性變數

●迴圈優化

—把迴圈體內不變的運算提到迴圈體外

—變換迴圈控制條件,刪除純粹為控制迴圈而設立的語句

3.2 目標**生成與優化

偏向於硬體執行的優化,主要考慮的是:

—利用機器的各個硬體暫存器存放的有關變數的值,以減少對於記憶體的訪問次數

—根據機器硬體執行指令的特點(如流水線、risc、cisc、vliw等)而對指令進行一些調整使目標**比較短,執行的效率比較高

4 彙編過程

彙編過程實際上是指把組合語言翻譯成目標機器指令(2進製檔案)的過程。

5 鏈結程式

由匯程式設計序生成的目標檔案並不能立即就被執行,其中可能還有許多沒有解決的問題。例如,某個原始檔中的函式可能引用了另乙個原始檔中定義的某個符號(如變數或者函式呼叫等);在程式中可能呼叫了某個庫檔案中的函式,等等。所有的這些問題,都需要經鏈結程式的處理方能得以解決。

(1)靜態鏈結(編譯時鏈結)

在這種鏈結方式下,函式的**將從其所在地靜態鏈結庫中被拷貝到最終的可執行程式中。這樣該程式在被執行時這些**將被裝入到該程序的虛擬位址空間中。靜態鏈結庫實際上是乙個目標檔案的集合,其中的每個檔案含有庫中的乙個或者一組相關函式的**。

(2)動態鏈結(裝載和執行時鏈結)

在此種方式下,函式的**被放到稱作是動態鏈結庫或共享物件的某個目標檔案中。鏈結程式此時所作的只是在最終的可執行程式中記錄下共享物件的名字以及其它少量的登記資訊。在此可執行檔案被執行時,動態鏈結庫的全部內容將被對映到執行時相應程序的虛位址空間。動態鏈結程式將根據可執行程式中記錄的資訊找到相應的函式**。

總結:靜態庫的鏈結在編譯時會被編譯進彙編檔案,這樣的操作會改變檔案大小;而動態庫則是在執行時當需要動態庫中的檔案時才被鏈結到可執行檔案的。

6 輸出檔案

執行頭部:包含核心將二進位制檔案載入記憶體並執行所需的引數,也包含對鏈結編輯器ld的指引。

文字段:包含執行時被載入記憶體的機器碼的相關資料,可能是唯讀的。

資料段:包含已初始化的資料,總是可寫的。

文字重定位:包含鏈結編輯器在合併二進位制檔案時修改檔案段指標的記錄。

資料重定位:與文字重定位類似,但是給資料段指標用。

符號表:包含鏈結編輯器用於交叉引用不同二進位制檔案中變數和函式的記錄。

字串表:包含對應於符號表的字串。

C程式編譯執行過程

認識c編譯執行過程,是c學習的開端。簡單說c語言從編碼編譯到執行要經歷一下過程 c源 編譯 形成目標 目標 是在目標機器上執行的 連線 將目標 與c函式庫相連線,並將源程式所用的庫 與目標 合併,並形成最終可執行的二進位制機器 程式 執行 在特定的機器環境下執行c程式。如果用乙個圖 來表示 以上過程...

C 程式的編譯執行過程

總體來說包含四步 預處理 編譯 彙編 鏈結 g 編譯器可以一步執行到底,直接生成可執行檔案,如 g o helloworld helloworld.cpp g g 表示生成除錯相關的資訊也可以分布執行,生成預處理檔案,預處理 g o helloworld.ii e helloworld.cpp 此時...

C程式編譯過程

題記 前幾天去華為面試實習生,面試官問了個問題,讓我說出乙個程式的詳細編譯過程,當時磕磕絆絆說了一堆東西,事後自己都不知道當時說了什麼,慚愧。c語言編譯過程 編譯,編譯程式讀取源程式 字元流 對之進行詞法和語法的分析,將高階語言指令轉換為功能等效的彙編 再由匯程式設計序轉換為機器語言,並且按照作業系...