鏈接器年齡比編譯器長。。。
我仔細想了一下,程序進化的四個步驟,發(fā)現(xiàn)預處理和匯編很簡單,難點在于編譯和鏈接,然而最難的編譯已經(jīng)由專門開發(fā)gcc類似的大牛給解決了,那么留下來給普通開發(fā)者最大的難點可能就是鏈接了。因此鏈接的原理必須好好了解。
為了更好地理解計算機程序的編譯和鏈接的過程,我們簡單地回顧計算機程序開發(fā)的歷史一定會非常有益。有句話怎么說的,要學習一項東西,首先要了解其歷史,再學習其語言嘛。
計算機的程序開發(fā)并非從一開始就有著這么復雜的自動化編譯、鏈接過程。原始的鏈接概念遠在高級程序語言發(fā)明之前就已經(jīng)存在了。在最開始的時候,程序員先把程序在紙上寫好,當時沒有高級語言,也沒有匯編語言,用的是機器語言。。。當程序需要被運行時,程序員人工將他們寫好的程序寫入到存儲設備上,最原始的存儲設備之一就是紙袋,即在紙帶上打相應的孔。

假設有一種計算機,它的每條指令都是1字節(jié),也就是8位。我們假設有一種跳轉指令,它的高4位0001,表示這是一個跳轉指令,低4位存放的是跳轉目的地的絕對地址。如上圖所示,第0條指令是一個跳轉指令,它的目的地址是第4條指令。
現(xiàn)在問題來了,程序并不是一寫好就永遠不變的,它可能會經(jīng)常被修改。比如我們在第0條和第4條指令間插入一條或多條指令,那么第4條及其之后的指令位置都將會相應地往后移動。原先第0條的低4位數(shù)字也將做相應的調整。在這個過程中,程序員需要人工重新計算每個子程序或跳轉的目標地址。當程序修改的時候,這些位置都要重新計算,十分繁瑣又耗時,并且容易出錯。這種重新計算各個目標的地址過程被叫做重定位relocation。
如果我們有多條紙帶的程序,這些程序間可能會有類似跨紙帶之間的跳轉,這種程序經(jīng)常修改導致跳轉目標地址變化在程序擁有多個模塊時更為嚴重。人工綁定進行指令的修正以確保所有的跳轉目標地址都正確,在程序規(guī)模越來越大以后變得越來越復雜和繁瑣。
后來。。。實在沒辦法忍受了,先驅們就發(fā)明了匯編語言,這相比機器語言來說是巨大的進步!匯編語言使用接近人類的各種符號和標記來幫助記憶,比如指令采用兩個或三個字母的縮寫,記?。骸癹mp”比記住0001xxxx是跳轉(jump)指令要容易多啦。匯編語言還可以用符號來標記位置,比如一個符號“divide”表示一個除法子程序的起始地址,比記住從某個位置開始的第幾條指令是除法子程序要方便得多。最重要的是,這種符號的方法使得人們從具體的指令地址中逐步解放出來。比如前面紙帶程序中,我們把剛開始第4條指令開始的子程序命名為“foo”,那么第0條指令的匯編就是:
jmp foo
當然人們可以使用這種符號命名子程序或跳轉目標以后,不管這個“foo”之前插入或減少了多少條指令導致“foo”目標地址發(fā)生了什么變化,匯編器在每次匯編程序的時候會重新計算“foo”這個符號的地址,然后把所有引用到“foo”的指令修正到這個正確的地址。整個過程不需要人工參與,對于一個有成百上千個類似的符號的程序,程序員終于擺脫了這種低級的繁瑣的調整地址的工作。符號symbol這個概念隨著匯編語言的普及迅速被使用,它用來表示一個地址這個地址可能是一個函數(shù)的起始地址,也可以是一個變量的起始地址。
在c中,最小單位是變量和函數(shù),若干個變量和函數(shù)組成一個模塊,存放在一個.c的源文件里,在一個程序被分割成多個模塊以后,這些模塊之間如何組合成一個單一的程序是必須要解決的問題。模塊間如何組合的問題可以歸結于模塊間如何通信的問題,通信的兩種方式一種是模塊間的函數(shù)調用,一種是模塊間的變量訪問。函數(shù)訪問需要知道目標函數(shù)的地址,變量訪問需要知道目標變量的地址,所以這兩個方式可以歸結于一種:模塊間符號的引用。這種模塊間的通信和拼接,就是鏈接linking!
摘抄自《程序員的自我修養(yǎng)》