洽談穿針引線的「鏈結」

2021-09-27 12:44:54 字數 2139 閱讀 8936

鏈結(linking)是將各種**和資料片段收集並組合成乙個單一檔案的過程,這個檔案可被載入到記憶體並執行。(引自教材p464)。鏈結的操作檔案是xx.o檔案,即可重定位的目標檔案。它將多個xx.o 檔案合併起來並將它們轉化成乙個可執行的目標檔案。

乙個較為簡單的例子:當我們初學c語言時會碰到那個hello程式,這個程式要求輸出「hello world!」。所以我們用了printf函式來輸出,其實函式printf就是需要引進的「客人」。這個時候我們的鏈結器就發揮作用了:它將printf.o和hello.o合併在一起,最後程式才能執行。

具體我們來看乙個例項

m.c的**如下:

void

swap()

;int buf[2]

=int

main()

swap.c的**如下:

extern

int buf;

//extern是引進外源

int*bufp0 =

&buf[0]

;int

*bufp1;

void

swap()

我們用gcc將m.c和swap.c進行編譯變為m.o和swap.o,在linux控制台執行命令:linux> gcc -c m.c swap.c。然後分別用命令readedf -h m.o和readedf -h swap.o檢視這兩個檔案的elf頭。

可以看出:majic和class用於標識這個檔案,data採用二進位制補碼小端法表示。

下面通過命令readelf -s m.o和readelf -s swap.o檢視這兩個檔案的符號表。

我們重點關注ndx和name兩項,可以看到:

在兩個程式中,檔名都對應為abs,所以其實檔名是不需要重定位的。然後我們來看函式名swap:可以看到在m.o中對應為und,按教材上指出的und表示模組中未定義但已引用的符號,這是因為在m.o只是宣告了乙個無返回值函式,但並未初始化;而在swap.o中對應是1,1是.text節,所以初始化後的函式名放在.text節裡。再看變數buf:在m.o中對應3,3是.data節,buf是個全域性變數並且賦了初值,所以放在這裡;而在swap.o中對應und,這是因為它沒在這裡賦值但卻引用了(extern)。最後看swap.o裡面的*bufp0和*bufp1這兩「位」指標變數:*bufp0對應5,*bufp1對應common,這時問題來了,前者不是已初始化的全域性變數嗎?為什麼不對應.data位元組呢。原因筆者認為如此:由於buf在該模組中引用卻在別的模組定義,所以它不能判斷*bufp0所賦的&buf[0]到底有沒有具體的值,所以或者是.bss節或者是.data節,但不管怎樣還是在sysmtab條目中,所以對應5。具體關於這些符號的分配請參考教材470頁的**。

在此,我們來總結幾個重要的考點:

(1)函式名一般放在.text節中;

(2)common:未出始的全域性變數,

.bss:未初始化的靜態變數,以及初始化為0的全域性或靜態變數(要搞清全域性變數和靜態變數);

(3)已經初始化的全域性變數放在.data節裡面;

(4).radata中存放唯讀資料,如printf函式中的格式串。

(5)區域性變數存在棧中,不是符號表裡面的,全域性變數由於靜態分配放到記憶體區域,故在符號表中。

下面我們再選擇兩個c語言程式向教材470頁那樣的**那樣列出每個符號進行分析,然後用符號表檢測結果。

#include

voidf(

void);

int x =

15213

;int

main()

int x;

voidf(

)

鍊錶 在節點間穿針引線

例題 反轉鍊錶 反轉鍊錶原理很簡單,就是將原本指向下乙個結點的指標,指向上乙個結點。但是,需要儲存前面和後面的結點,要不就找不到了。所以使用pre cur next三個結點,儲存著上乙個結點 當前結點 下乙個結點,然後一起向後遍歷。在這期間,最需要注意的就是順序以及邊界。class solution...