深挖程序編譯過程

A ship in port is safe. but that is not what ships are builts for.--------船舶在港灣裏當然是安全的,但是船不是爲了停泊在港灣裏而建造的。

1, 全過程圖示

在這裏插入圖片描述

2, 預處理

預編譯主要處理源代碼文件中的以“#”開頭的預編譯指令。

  • 1、刪除所有的#define,展開所有的宏定義。

  • 2、處理所有的條件預編譯指令,如“#if”、“#endif”、“#ifdef”、“#elif”和“#else”。

  • 3、處理“#include”預編譯指令,將文件內容替換到它的位置,這個過程是遞歸進行的,文件中包含其他文件。

  • 4、刪除所有的註釋,“//”和“/**/”。

  • 5、保留所有的#pragma 編譯器指令,編譯器需要用到他們,如:#pragma once 是爲了防止有文件被重複引用。

  • 6、添加行號和文件標識,便於編譯時編譯器產生調試用的行號信息,和編譯時產生編譯錯誤或警告是能夠顯示行號。

3, 編譯

編譯把預編譯之後生成的xxx.i或xxx.ii文件,進行一系列詞法分析、語法分析、語義分析及優化後,生成相應的彙編代碼文件。

  • 1、詞法分析:利用類似於“有限狀態機”的算法,將源代碼程序輸入到掃描機中,將其中的字符序列分割成一系列的記號。

  • 2、語法分析:語法分析器對由掃描器產生的記號,進行語法分析,產生語法樹。由語法分析器輸出的語法樹是一種以表達式爲節點的樹。

  • 3、語義分析:語法分析器只是完成了對錶達式語法層面的分析,語義分析器則對錶達式是否有意義進行判斷,其分析的語義是靜態語義——在編譯期能分期的語義,相對應的動態語義是在運行期才能確定的語義。

  • 4、優化:源代碼級別的一個優化過程。

  • 5、目標代碼生成:由代碼生成器將中間代碼轉換成目標機器代碼,生成一系列的代碼序列——彙編語言表示。

  • 6、目標代碼優化:目標代碼優化器對上述的目標機器代碼進行優化:尋找合適的尋址方式、使用位移來替代乘法運算、刪除多餘的指令等。

4, 彙編

彙編將彙編代碼轉變成機器可以執行的指令(機器碼文件)。

彙編器的彙編過程相對於編譯器來說更簡單,沒有複雜的語法,也沒有語義,更不需要做指令優化,只是根據彙編指令和機器指令的對照表一 一翻譯過來,彙編過程有彙編器as完成。

經彙編之後,產生目標文件(與可執行文件格式幾乎一樣)

xxx.o(Windows下)、
xxx.obj(Linux下)。

5, 鏈接

鏈接將不同的源文件產生的目標文件進行鏈接,從而形成一個可以執行的程序。鏈接分爲靜態鏈接和動態鏈接:

在這裏插入圖片描述

上圖中後面的圖就是分別是靜態鏈接(每個程序都需要拷貝一份靜態庫),動態鏈接(共享動態庫)

5.1 靜態鏈接:

函數和數據被編譯進一個二進制文件。

在使用靜態庫的情況下,在編譯鏈接可執行文件時,鏈接器從庫中複製這些函數和數據並把它們和應用程序的其它模塊組合起來創建最終的可執行文件。

缺點:

空間浪費:因爲每個可執行程序中對所有需要的目標文件都要有一份副本,所以如果多個程序對同一個目標文件都有依賴,會出現同一個目標文件都在內存存在多個副本;

更新困難:每當庫函數的代碼修改了,這個時候就需要重新進行編譯鏈接形成可執行程序。

優點:

運行速度快:但是靜態鏈接的優點就是,在可執行程序中已經具備了所有執行程序所需要的任何東西,在執行的時候運行速度快。

5.2 動態鏈接:

動態鏈接的基本思想是把程序按照模塊拆分成各個相對獨立部分,在程序運行時纔將它們鏈接在一起形成一個完整的程序,而不是像靜態鏈接一樣把所有程序模塊都鏈接成一個單獨的可執行文件。

共享庫:就是即使需要每個程序都依賴同一個庫,但是該庫不會像靜態鏈接那樣在內存中存在多分,副本,而是這多個程序在執行時共享同一份副本;

優點:

更新方便:更新時只需要替換原來的目標文件,而無需將所有的程序再重新鏈接一遍。當程序下一次運行時,新版本的目標文件會被自動加載到內存並且鏈接起來,程序就完成了升級的目標。

缺點:

性能損耗:因爲把鏈接推遲到了程序運行時,所以每次執行程序都需要進行鏈接,所以性能會有一定損失。

最後,我寫了一篇關於靜態庫和動態庫的博客:
https://blog.csdn.net/weixin_46027505/article/details/105223091

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章