編譯和連接

寫在前面

只討論C語言。編程

「二進制代碼」——指被可以計算機直接執行的代碼(至於到底是操做系統層的命令,仍是CPU的機器指令,這裏不作深刻討論)。函數

頭文件

通常是.h文件。編碼

頭文件的功能在於指示編程人員和編譯器:哪些函數能夠調用?這些函數應該怎麼調用(參數和返回值)?操作系統

頭文件不指示任何庫的存儲位置。全部須要的庫文件,應位於默認目錄或指定目錄。指針

中間代碼文件

通常是.o或.obj文件。code

中間代碼文件包括:接口

  • 通常語句的二進制編碼。
  • 用戶定義函數的二進制編碼和接口。
  • 函數調用的指示(能夠理解爲「連接」或「指針」)。

中間代碼文件的使命在連接完成後就結束了。進程

庫是一個文件。內存

庫的內容是造成庫的若干個中間代碼文件內容的集合。編譯器

庫和中間代碼文件的格式不同。

靜態庫

在Windows中被稱爲「靜態連接庫」。

靜態庫本質上是一個庫。在Linux中通常爲.a文件,在Windows中爲.lib文件。

假設若干個可執行文件在連接時使用了相同的靜態庫。實際上,靜態庫產生的二進制編碼被放進了可執行文件中。當這些可執行文件運行的時候,每一個可執行文件產生的進程,在內存中各有一份上述二進制編碼的副本。

靜態連接庫的使命在連接完成後就結束了。

同一個可執行文件產生的進程在內存中共用一份代碼段。這是Linux操做系統內存管理的特性,與靜態庫無關。

共享庫

在Windows中被稱爲「動態連接庫」。

共享庫本質上是一個庫。在Linux中通常爲.so文件,在Windows中爲.dll文件。

假設若干個可執行文件在連接時使用了相同的共享庫。實際上,共享庫被可執行文件所引用,其二進制編碼並無被放進可執行文件中。當這些可執行文件運行時,被不一樣可執行文件使用的、同一個由共享庫產生的二進制編碼,在內存中只有一個副本。這個副本由全部可執行文件的全部進程共用。

靜態庫和共享庫的格式不一樣,二者的大小不具備可比性。

編譯與連接

編譯

過程:由源文件生成中間代碼文件。

命令:gcc

選項:-c

打包靜態庫

過程:用中間代碼文件生成靜態庫。

命令:ar

選項:-r

打包共享庫

過程:用中間代碼文件生成共享庫。

命令:gcc

選項:-shared -fPIC

注意:

  • 在編譯中間代碼文件時和打包共享庫時,都必須添加上述選項!不然打包會出錯。
  • 若不加-fPIC,則生成「僞共享庫」。「僞共享庫」既不會被放入可執行文件,又不能被多個引用其的可執行文件共享。

靜態連接

過程:用中間代碼文件和靜態庫(可無)生成可執行文件。

結果:可執行文件包含運行所須要的所有二進制代碼(包括被調用的系統函數的二進制代碼)。可執行文件通常能夠獨立運行。

命令:gcc

選項:-static

動態連接

過程:用中間代碼文件、靜態庫(可無)和共享庫(可無)生成可執行文件。

結果:

  • 可執行文件包括中間代碼文件和靜態庫中的二進制代碼,這些二進制代碼來源於:a.基本語句;b.自定義函數的實現。
  • 可執行文件依賴於:連接時使用到的共享庫(全部層次的,所有的)。
  • 可執行文件通常不能獨立運行。

命令:gcc

選項:無

其餘

靜態編譯 = 編譯 + 靜態連接

動態編譯 = 編譯 + 動態連接

可使用ldd命令來查看可執行文件所依賴的共享庫。

gcc命令的其餘經常使用選項:

  • -Wl,-rpath,./:可執行文件運行時,將所在目錄加入共享庫的查找範圍。
  • -include:查找頭文件並加入編譯。
  • -I(大寫的i):將目錄的全部頭文件加入編譯。
  • -l(小寫的L):查找庫並加入編譯(後面緊跟庫的縮寫)。
  • -L:將目錄加入庫的查找範圍。應將-L放在gcc命令的最後,不然某些時候會出錯。

GCC會自動地將一些頭文件和庫加入編譯和連接:

  • 頭文件的默認查找目錄:當前目錄、/include//usr/include/
  • 庫的默認查找目錄:當前目錄、/lib//usr/lib/
相關文章
相關標籤/搜索