靜態鏈結 編譯和鏈結(一)

2021-10-21 18:18:00 字數 3139 閱讀 8415

彙編鏈結

小結本文主要是《程式設計師的自我修養》一書的內容摘要和梳理,如有需要並且沒有被本文涵蓋的內容,建議讀者自行**原書。

靜態鏈結——目標檔案(二)

靜態鏈結——靜態鏈結(三)

裝載與動態鏈結——裝載與程序(一)

裝載與動態鏈結——動態鏈結(二)

本部分主要是介紹關於程式源**是如何到可執行檔案的,及這其中涉及到的主要步驟(例如本書的主題——編譯)的相關基本概念。

預處理部分主要是處理源**以 「#」 開頭的預編譯指令:

預處理過程可以看到大部分都是文字的替換工作,所以我們無法在預處理階段就判斷出巨集定義是否正確或者是標頭檔案包含正確,唯一可能的方法就是肉眼檢查預處理之後的檔案內容 ?。

編譯命令:

gcc -e hello.c -o hello.i
編譯部分要把預處理部分的內容進行一系列的操作:詞法分析語法分析語義分析相關優化,最後生成彙編**檔案。

下面以書中的一行原始碼來進行上述步驟的說明:

array[index]

=(index +4)

*(2+

6)

詞法分析

首先詞法分析是執行一種類似有限狀態機的演算法,把源**的字串行分割成一系列的記號(token),一般可以分為:關鍵字,識別符號,字面量和特殊符號。

上述可以得到:

將對應的類別放入對應的符號表/檔案表中以備後續使用。

語法分析

語法分析顧名思義是對詞法分析的結果進行語法分析,生成上下文無關語法樹,其是以表示式(expression)為節點的樹。上述語句就是由賦值表示式、加法表示式、乘法表示式、陣列表示式、括號表示式組成的複雜語句,經過語法分析以後可以得到下述語法樹:

注意,一般符號和數字是最小的表示式,所以它們一般是葉節點,然後在語法分析的同時,很對符號的優先順序和含義就被確定了下來,例如 × 比 + 優先順序高,()比 × 優先順序高,這裡 * 符號代表乘法而不是取指標內容等。如果表示式不合法,括號不匹配,表示式缺少操作符等,都是在這個階段編譯器就會報相關錯誤了。

語義分析

語法分析並不了解這個語句是否真正有意義,例如語法上將兩個指標相乘是合法的,但是在語義上肯定是非法的,因此編譯器需要進一步進行語義分析。編譯器可以做的是靜態語義:值在編譯器就可以確定的語義,與之對應的動態語義則是只有執行期才可以確定的語義,比如將 0 作為除數了。

靜態語義通常包括宣告和型別匹配,型別轉換。

比如運算需要將 float 轉換成 int,或者將 float 數複製給乙個指標,語義分析發現型別不匹配,編譯器就要報錯。

經過語義分析以後整個語法樹就會被表示上型別,如果需要做隱式轉換,語義分析程式會在語法書中插入相應轉換節點。經過語義分析的語法樹為:

中間語言生成

在語義分析完成以後,以及進行相關優化之前,還有一步中間步驟,中間語言生成,其目的主要還是對語法樹進行初步優化,方便後面的進一步優化編譯,而直接在語法樹上進行優化比較困難,所以需要中間**,這個**與機器和執行環境是無關的。以上面的語法樹為例,轉化成中間語言的一種——三位址碼後是:

t1 =2+

6t2 = index +

4t3 = t2 * t1

array[index]

= t3

這裡在編譯期 t1 的值就可以直接確定為 8 ,所以可以直接將 t1 變數替換為 8,且變數可以重複使用,優化後為:

t2 = index +

4t2 = t2 *

8array[index]

= t2

中間**的存在使編譯器可以分為前端和後端,前端只要是負責產生與機器無關的中間**,後端負責將中間**轉換成目標機器的**,這樣對於一些跨平台的編譯器,它們可以針對不同平台使用同乙個前端 + 針對不同機器的多個後端。

相關優化

前面提到編譯器前端主要是負責生成與機器無關的**,後端細化主要是包括**生成器和目標**優化器。

**生成器:將中間**轉化成目標機器**。

目標**優化器:選擇合適的定址,使用位移代替乘法,刪除多餘指令等。

最終經由詞法分析、語法分析、語義分析、源**優化、中間語言生成、目標**優化之後,源**程式設計了目標**(彙編**),交由後面的彙編器來編譯成真正能在機器上執行的指令。

gcc -s hello.c -o hello.s
這裡針對不同的程式,gcc 後台會呼叫不同的預編譯和編譯的程式,例如 c 語言需要呼叫 cc1,c++ 語言需要呼叫 cc1 plus。

彙編部分就是將彙編**轉換成機器可以執行的指令,基本只要根據對照表一一翻譯就可以了。翻譯之後輸出的就是目標檔案(object file)。

gcc -c hello.c -o hello.o
在上述目標檔案中,可能會包含對外部符號的引用,在經過彙編以後這些外部的符號都是未定義的乙個狀態,需要鏈結器來最終計算確定並把這些庫中未定義的外部符號的位址換成正確的絕對位址,所有依賴庫中的位址都確定了,最後將目標檔案都鏈結起來之後才會生成可執行檔案,這是乙個很複雜的過程,也是 《程式設計師的自我修養》這本書的重點所在。這個大話題會貫穿整個系列文章,這裡先簡單介紹。

鏈結的主要內容就是把各個模組之間的內容都處理好,是各個模組之間可以正常銜接。主要過程包括了 位址和空間分配、符號決議和重定位等。

本文主要是介紹編譯器的編譯階段對源**所做的內容,如標題順序,對源**以此經過:詞法分析,語法分析,語義分析,中間**生成,相關優化,最後彙編器翻譯成機器語言,再交給鏈結器來進行變數/函式位址的重新計算和修正。

編譯和鏈結 一

先從乙個最簡單的案例入手 helloworld.c,這裡我就不寫了,大家都寫過,我把它編譯成可執行程式gcc o helloworld helloworld.c 預處理 將所有的巨集定義替換 include檔案展開 刪除所有的注釋 新增行號和檔名標示 當然還有保留 pragma編譯指令等 編譯 把檔...

靜態鏈結和動態鏈結

靜態載入dll dll工程b 專案屬性 配置屬性 常規 配置型別 動態庫 dll 在宣告檔案中,宣告匯出函式 declspec dllexport int xx 如果是c檔案,要在c 檔案中被呼叫,註明extern c 可以 ifdef cplusplus extern c endif 呼叫dll的...

靜態鏈結和動態鏈結

1 靜態鏈結庫只包含 lib檔案 動態鏈結庫包含 lib檔案和dll檔案,靜態鏈結庫中不能再包含其他的動態鏈結庫或者靜態庫,而在動態鏈結庫中還可以再包含其他的動態或靜態鏈結庫。此外他們都會用到定義了函式和相關資料結構的.h標頭檔案,其中 h標頭檔案是編譯時必須的,lib是鏈結時需要的,dll是執行時...