基本步驟
預處理 -> 編譯 -> 彙編 -> 鏈接
預編譯:將源文件hello.c
和相關的頭文件被預編譯器編譯成一個hello.ii
文件(c語言的話.c
文件預編譯成.i
文件)
- 將#define 刪除,展開所有的宏定義
- 處理條件預編譯指令,把包含的文件插入到預編譯指令的位置(遞歸的過程)
- 刪除註釋
- 添加行號,文件名標示
- 保留#progma編譯器指令
編譯:把預處理完的文件進行一系列的詞法分析、語法分析以及優化後生成相應的彙編代碼文件。
彙編:彙編器將彙編代碼轉爲機器可以執行的指令。每個彙編語句都對應一個機器指令。其實就是將.c
文件轉換爲.o
目標文件。
鏈接:得到.out
文件。用符號來實現。當一個程序很複雜的時候,我們把每個源代碼模塊獨立的編譯,然後按照要求進行“組裝”,這個組裝的過程就是鏈接。
編譯器
將高級語言翻譯成機器語言的工具。
步驟:
掃描-> 語法分析 -> 語義分析 -> 源代碼優化 -> 代碼生成 -> 目標文件優化
鏈接
定義:當一個程序很複雜的時候,我們把每個源代碼模塊獨立的編譯,然後按照要求進行“組裝”,這個組裝的過程就是鏈接。
主要內容:將每個模塊之間相互引用的部分都處理好,使得各個模塊之間能夠正確的銜接。
包括: 地址和空間分配、 符號決議、 重定位
例子: 在一個程序模塊main.c中要用到另一個模塊fun.c的foo()函數,那麼在main.c每個用到fun()函數的時候都要知道這個函數的地址。但是因爲每個程序都是單獨編譯的,編譯器並不知道foo()的地址,所以編譯器暫時用一些符號來替代,暫時將它擱置,等到鏈接時,再將這些指令的地址修正。使用鏈接器,可以直接引用其他模塊的函數和全局變量,而不需要知道他們的地址。
靜態鏈接與動態鏈接
靜態鏈接
就是在編譯鏈接時直接將需要的執行代碼拷貝到調用處,優點就是在程序發佈的時候就不需要的依賴庫,也就是不再需要帶着庫一塊發佈,程序可以獨立執行,但是體積可能會相對大一些。
優點
代碼裝載速度快,執行速度比動態鏈接快
只需要保證開發者的計算機有正確的lib文件
缺點
生成可執行文件較大,可能包含相同的代碼拷貝
動態鏈接:
就是在編譯的時候不直接拷貝可執行代碼,而是通過記錄一系列符號和參數,在程序運行或加載時將這些信息傳遞給操作系統,操作系統負責將需要的動態庫加載到內存中,然後程序在運行到指定的代碼時,去共享執行內存中已經加載的動態庫可執行代碼,最終達到運行時連接的目的。優點是多個程序可以共享同一段代碼,而不需要在磁盤上存儲多個拷貝,缺點是由於是運行時加載,可能會影響程序的前期執行性能。
優點
節省內存並減少頁面交換
dll文件與exe文件獨立,只需要保證接口不變,更換dll文件對exe無影響,極大地提高可維護性和可拓展性
不同的編譯語言可以用同一個dll文件
適用於大規模的軟件開發,是的開發過程獨立,耦合度小,便於不同開發者和開發組織者之間進行開發和測試。
缺點
如果程序依賴的dll文件不存在,載入動態鏈接時,程序會終止並給出錯誤提示,運行時動態鏈接程序會加載失敗
速度慢
區別
- 多次調用一個外部的函數(dll文件orlib文件),靜態鏈接可能會有多份拷貝,動態鏈接只有一份拷貝
- 靜態鏈接的話lib文件中的指令會被包含到exe執行文件中,動態鏈接的話dll文件不必包含在exe執行文件中,exe可以隨時動態的卸載和引用這個dll文件。
- 靜態鏈接庫不能再包含其他動態鏈接庫和靜態庫,而在動態鏈接庫中可以再包含其他動態鏈接庫和靜態庫。