1. 程式人生 > >程式設計師的自我修養(2)----靜態連結

程式設計師的自我修養(2)----靜態連結

程式設計師的自我修養(2)—-靜態連結

本章介紹,靜態連結的過程,首先總體介紹了,四個基本步驟,預編譯,編譯,彙編,連結,接著詳細介紹了每個過程做了什麼,以及如何做到的.

Created with Raphaël 2.1.0startsource file預編譯".i"檔案編譯彙編程式碼檔案彙編中間目標檔案(".o")連結可執行檔案(".out")end

1 預編譯

預編譯過程很簡單,就是一些簡單的預處理過程,我們使用的巨集和條件預編譯指令都在這個階段被處理.

  • 將巨集展開 即替換所有#define 定義的巨集
  • 處理#if #else #elif等預編譯處理命令,刪除掉,不需要編譯的程式碼
  • 處理#include 將,包含的標頭檔案遞迴,插入到該語句坐在的位置
  • 刪除所有註釋

最後生成”.i”檔案

2 編譯

編譯過程簡單來說就是,將預處理完的”.i”檔案經過
詞法分析—->語法分析—->語義分析及優化—->彙編程式碼檔案

2.1 詞法分析

原始碼輸入掃描器,就原始碼分割成一系列的記號,有如下幾類:關鍵字 識別符號 字面量(數字 字串等) 特殊符號(加號 減號)

2.2 語法分析

對詞法分析產生的記號進行語法分析,按照符號表達式的優先順序生成語法樹,符號和數字樹語法樹的葉子節點.

2.3 語義分析

編譯器只能做到靜態語義分析,包括,宣告和型別匹配和轉換,語義分析會對,語法書的每個表示式標識型別.

2.4 中間程式碼生成

這裡生成最接近目的碼的中間程式碼,例如三位元組碼,中間程式碼還是與目標機器無關的,編譯器其實分為前端和後端,前端負責生成與目標機器無關的程式碼,後端根據機器生成目標機器程式碼,不同的硬體機器程式碼標準不一樣,跨平臺的編譯器,就是一個前端,和多個不同的後端來組成.

2.5 目的碼生成和優化

現在的編譯器優化非常複雜,,舉個例子,常量的相加,可以非常方便的直接優化,具體的優化細節這裡不講,這一步就是編譯器的後端部分了,他會生成目標機器程式碼,也就是彙編程式碼,就是mov jmp之類的.這裡有一個問題,一個專案是分模組的,A模組用的B模組的變數和函式等,編譯器對每個模組是單獨編譯的,那麼編譯A模組的時候,這個時候是不知道使用B中的函式foo_B的地址的,怎麼在彙編程式碼中表示呢?這就是後面要講的連結要解決的事情了.

3 彙編

彙編這個操作也很簡單,就是根據機器將彙編程式碼按照彙編程式碼與機器碼對應的一個表,一個個將彙編程式碼對映為機器碼的過程.

4 連結

回到剛才2.5中提出的問題,因為工程複雜,各個工能模組有自己的實現函式和介面,其他模組呼叫的時候並不知道呼叫的函式的地址的,這裡採取的解決方案,就是在這些不知道地址的地方,放上一個標記,這個標記就是表示,這個地方會在連結的時候進行替換,替換為呼叫模組中那個函式實際的地址,這個過程叫做重定位,舉個例子,就是呼叫其他模組的地方留出一個坑,連結的時候,就拿正確的東西來填這個坑.

5 靜態連結

回到本章主題,靜態連結,主要工作如下
1. 地址和空間分配:給每個函式和變數分配地址
2 .符號決議:我的理解是給每個函式和變數命名為獨一無二可區分的符號
3. 重定位:就是更新函式中用到的函式對應的地址,就是第一條中所分配的地址
注意,在靜態連結前,各個模組引用其他模組的函式或者變數,是可以不關注定義的,只需要包含其他模組的標頭檔案,而靜態庫.a就是多個.o檔案的集合,這樣的靜態庫也是合法的,但是在連結(包括靜態連結和動態連結)的過程中,如果之前應用的函式或者變數沒有找到定義,會報錯某某函式或者變數未定義錯誤!(靜態庫一定要連結成功之後才能保證可用)

本章結束,時間不夠,要更加好好的利用時間,不要浪費時間!!