標籤:

c/cpp 中從源代碼到可執行文件的過程,鏈接是必須的嗎?

c/cpp 中從源代碼到可執行文件的過程中有預編譯,編譯,彙編和鏈接四個過程,前三個必須很好理解,但是如果一個源代碼沒有引用任何的其他庫,只有空的int main(){return 0},還需要鏈接這個過程嗎?


是。因為main函數並不是入口,他要被真正的入口鏈接。


這個談一點個人認識,我認為這套工具鏈和歷史是有關係的,以前C是可以和彙編混合編程的(應該說現在也可以),最早彙編就有彙編和鏈接兩個部分,理由很簡單,稍微大一點的項目肯定都需要多人合作,那麼我們不可能大家都把代碼寫到一個文件裡面,而是需要分文件處理,彙編那個代碼印象當中是這樣的,先聲明不同的段,有代碼段、有數據段,然後代碼段裡面是彙編,數據段裡面可以放些靜態數據,但是每個文件裡面都有這樣的段,相互之間還可能要互相調用,相互調用的時候需要知道地址,而地址只有最終把所有段合到一起了才知道。再加上代碼很多的時候,當時機器慢,內存也小,不可能每次都把所有代碼都處理一遍,而是希望改多少就只處理多少,所以有這麼個過程:

1. 先將每個文件都轉化為段的二進位格式.obj。如果其中有需要調用其他段的部分,先用一個符號占著位置。

2. 把所有段的代碼拼起來。

3. 回頭去找相應的符號,把符號替換成真正的地址。

C/C++是從這個工具鏈繼承下來的,一個.c, .cpp文件就是替代原來的.asm,許多工作原理都是一樣的,也一直保持著可以跟彙編混合使用的特點,雖然編譯C/C++比彙編過程複雜了不少,但中間都有一個轉化為彙編的過程。之後的鏈接其實是繼承了彙編時代遺留下來的工具。雖然有可能只生成了一個obj文件,其中完全沒有外部符號,但是鏈接這一步畢竟還有一些轉換成可執行格式之類的功能,而且可能還要鏈接外部庫之類的。


年輕人還是要提高自己的姿勢水平~

你需要看深入理解計算機系統的第7章或者《程序員的自我修養》 附第一本書對應的視頻鏈接~https://www.coursera.org/learn/jisuanji-xitong/~這位老師講解編譯鏈接elf結構講解的很不錯~~~看完可以完美解答你的疑問


取決於你從哪個角度看,畢竟只有一個編譯單元,並且不引用外部代碼的程序是極個別的情況(並且你寫的這個一般來說不屬於這種情況)。生成工具沒必要專門為這種情況寫一個專門的沒有鏈接的編譯流程。

另一個角度看,如果一定要一次性生成最終結果也沒人攔著你(去寫這樣一個生成工具自己用)。因為是可以做到的。


可視化一下 @vczh的回答,如下

閑得蛋疼的話可以調教下編譯器的,比如

ps:

更多的調教方法需要

C++大神 @atyuwen 講解4k demo所用的技術。


毛遂自薦一篇文章:C/C++編譯鏈接與裝載深入淺出 C/C++編譯鏈接與裝載深入淺出其二

重點在第二篇

鏈接器的由來

C/C++模塊之間通信的方式有兩種, 一種是模塊間函數調用, 另一種是模塊之間的變數訪問. 在編譯成目標文件的時候, 由於沒有辦法得知所引用的外部函數或者外部變數的地址, 所以會先置0. 所以問題本質上就是, 如何得知目標函數或者目標變數的地址呢?

手動查找修改自然不是我們的方法, 這就是鏈接器(Link Editor)與它的工作-重定位 的由來.

重定位表

鏈接器在處理目標文件時, 對目標文件中引用外部函數或者變數的位置進行重定位, 即代碼段和數據段中那些對絕對地址的引用的位置. 這些重定位的信息都記錄在ELF文件的重定位表裡面. 其實目標文件中, 除了.data, .text這些段外, 若用到了其他目標文件中的外部變數或者函數, 就會多了對應的重定位表.

假如.data用了外部的全局變數, 就會多了一個.reldata段記錄了所引用的外部變數的地址偏移值(offset)以及在符號表中的下標等. 假如.text沒有用了外部的全局變數, 就不會有.reltext段.


現在看看編譯*.o和鏈接成可執行文件就是map reduce啊


鏈接最重要的作用是處理掉你程序裡面的一堆符號……即使是腳本語言「鏈接」也是必要的(也許不叫鏈接就是了?)


推薦閱讀:

c++中有現成的string hash函數么?
大家能談談是怎麼學習windbg的嗎?
同一個指針,作為父類類型被delete,但作為子類類型仍能訪問成員變數,為什麼?
這次dmlc發布的深度學習框架mxnet比之caffe有什麼優缺點?
C++什麼情況下,需要重載一個成員函數的const和非const版本?

TAG:C | 編譯 |