C高階 詳解編譯 鏈結

2022-08-18 07:39:19 字數 2488 閱讀 7733

被隱藏了的過程

現如今在流行的整合開發環境下我們很少需要關注編譯和鏈結的過程,而隱藏在程式執行期間的過程可不簡單,即使使用命令列來編譯乙個源**檔案,簡單的一句"gcc hello.c"命令就包含了非常複雜的過程。

1 #include3 int main()

4

在linux系統下使用gcc編譯程式時只須簡單的命令:

gcc 編譯過程分解

預編譯首先是將源**檔案hello.h和相關的標頭檔案,如stdio.h等被編譯器cpp預編譯成乙個.i檔案。主要處理那些原始檔中以「#」開始的預編譯指令,如「#include"、」#define「等,主要規則如下:

將所有的」#define「刪除,並且展開所有的巨集定義。

處理所有條件預編譯指令,比如」#if」、」#ifdef「、」#elif「等。

處理」#include「預編譯命令,將被包含的檔案插入到該預編譯指令的位置。注意,這個過程是遞迴進行的,也就是說被包含的檔案可能還包含其他檔案。

刪除所有的注釋」//「和」/**/「。

新增行號和檔名標識,比如#2」hello.c「2,以便於編譯器產生除錯用時的行號資訊及用於編譯時產生編譯錯誤或警告時能顯示行號。

保留所有的#pragma編譯器指令,因為預編譯器需要用他們。

編譯編譯過程就是把預處理完的檔案進行一系列詞法分析、語法分析、語義分析、生成彙編檔案,這個過程是是整個程式構建的核心部分,也是最複雜的部分之一。gcc將預編譯和編譯合併成乙個步驟,使用如下命令:

$gcc -s hello.c -o hello.s

可得到會變輸出檔案 hello.s 。實際上gcc這個命令只是這些後台程式的包裝,它會根據不同的引數要求去呼叫預編譯編譯程式cc1、彙編器as、鏈結器ld。            

編譯器職責

詞法分析  經過預編譯的源**程式被輸入到掃瞄器(scanner),掃瞄器對其進行簡單的詞法分析,運用一種類似於有限狀態機的演算法將源**的字元列分割成一系列的記號。如:關鍵字、識別符號、字面量(包含數字、字串等)和特殊符號(如加號、等號)。在標別記號的同時掃瞄器也完成了其他如將識別符號存放到符號表,將數字、字串常量存放到檔案表等的工作,以備後面的步驟使用。(lex程式可實現詞法掃瞄,按照一定的詞法規則完成標別記號等功能,所以無需為每個編譯器開發乙個獨立此法掃瞄器,而是根據需要改變語法規則即可。)

語法分析  語法分析器採用上下文無關語法的分析手段對掃瞄器產生的記號(token)進行語法分析,從而生成語法樹,即一表示式為節點的樹。同時很多運算子的含義和優先順序也被確定下來。編譯器也會報告出語法分析階段的錯誤。(如詞法分析有像lex一樣語法分析有現成工具ycc,它可根據語法規則對輸入的記號序列構建出一顆語法樹。對不同的程式語言只須改變語法規則即可。)

語義分析  語義分析由語義分析器完成,它所能分析的語義是靜態語義,即編譯期間可以確定的語義,執行期間才能確定的語義是指動態語義。靜態語義通常包括生命和型別匹配,型別轉換,如浮點型到整型轉換。經過語義分析以後整個語法樹都被標識了型別,如果有些型別需要做隱式轉換,語義分析程式會在語法樹中插入相應的轉換節點。語義分析器對符號表裡的符號型別也做了更新。語法分析僅僅完成對表示式語法層面的分析, 該語句是否有意義不進行檢測。

符號彙總  原始碼優化器會在源**級別進行優化,它往往將整個語法樹轉換成中間**,它是語法樹的順序表示,已非常接近目標**。中間**有多種型別,常見的有三位址碼,p-**。中間**使得編譯器可分成前端和後端,前段即產生中間**,後端將中間**轉換成目標機器**。編輯器主要包括**生成器和目標**生成器。**生成器將中間**轉換成目標機器**。目標**優化器再對其進行優化,如選擇合適的定址方式、使用位移來代替乘法運算、刪除多餘指令等。

彙編彙編器是將彙編**變成機器可以執行的指令,每一條彙編指令幾乎都對應一條機器指令,根據其對照表一一翻譯即可。目標檔案中還包括鏈結是所需要的一些除錯資訊: 比如符號表、 除錯資訊、 字串等。

鏈結人們把每個源**模組獨立的進行編譯,然後按照需要將它們組裝起來,這個組裝的過程就是鏈結(linking)。

未解決的符號表: 列出本單元裡有引用但是不在本單元定義的符號以及位址。匯出符號表: 本單元中定義的一些符號(全域性、靜態變數和函式) 和位址的對映表。位址重定向表: 提供了本編譯單元所有對自身位址的引 用記錄。聯結器的工作順序:當聯結器鏈結的時候, 首先決定各個目標檔案在最終可執行檔案裡的位置。然後訪問所有目標檔案的位址重定義表, 對其中記錄的位址進行重定向 (加上乙個偏移量, 即該編譯單元在可執行檔案上的起始位址) 。然後遍歷所有目標檔案的未解決符號表, 並且在所有的匯出符號表裡查詢匹配的符號, 並在未解決符號表中所記錄的位置上填寫實際位址。最後把所有的目標檔案的內容寫在各自的位置上,和庫(library)一起鏈結,形成最終的可執行檔案。

總結:

CSS高階詳解

css注意巢狀規則 補充 僅需要了解 初始化css 需要了解 雅虎工程師提供的css初始化示例 body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blo...

指標高階詳解2

一.函式指標 首先看一段 include void test intmain 輸出的是兩個位址,這兩個位址是test函式的位址,那我們的函式位址想要儲存起來,怎麼儲存?首先,能夠儲存位址,就需要乙個指向函式的指標變數,即函式指標 void fun1 fun1先與指標結合,說明fun1是指標,指標指向...

編譯鏈結詳解

因為這相當於在標頭檔案裡定義了const物件。作為例外,int char等可以進行就地初始化,是因為這些變數可以直接被優化為立即數,就和巨集一樣。此外,對於類的const 非static 成員變數,只能在建構函式的初始化列表中初始化,不能在類內部直接賦值,也不能在類外部賦值。而對於static co...