靜動態庫與項目管理

靜動態庫與項目管理

靜態庫與動態庫的比較

函數庫本質是一組函數,具有相近的功能或操作同一數據結構。根據鏈接時期的不同,函數庫分爲靜態庫和動態庫。

靜態庫是指編譯鏈接時,把庫文件的代碼全部加入到可執行文件中,因此生成的文件比較大,但在運行時也就不再需要庫文件了。其後綴名一般爲.a。動態庫與之相反,在編譯鏈接時並沒有把庫文件的代碼加入到可執行文件中,而是在程序執行時由運行時鏈接文件加載庫,這樣可以節省系統的開銷,在編譯過程中僅簡單地引用函數聲明。動態庫一般後綴名爲.so,如前面所述的 libc.so.6 就是動態庫。gcc在編譯時默認使用動態庫。

靜態庫和動態庫的最大區別在於,在靜態情況下,把庫直接加載到程序中;而動態庫鏈接的時候,它只是保留接口聲明,將動態庫與程序代碼獨立,這樣就可以提高代碼的可複用度,和降低程序的耦合度。

  • 靜態庫可以降低運行依賴,但是會增大程序體積,並且耦合性高。例如,100 份程序都用到靜態庫 a,靜態庫 a 會被拷貝 100 份。並且一旦靜態庫需要修改,需要修改 100 份。
  • 動態庫提高代碼的複用度,減少程序體積,但是程序運行時需要安裝相應的動態庫。例如,100 份程序都用到動態庫 a,它們會共用 1 份動態庫 a,並且修改方便。但是需要運行程序時,系統必須安裝有動態庫 a。

gcc 介紹

Linux 系統下的 gcc 是 GNU 推出的功能強大、性能優越的多平臺編譯器。gcc 是可以在多種硬體平臺上編譯出可執行程序的超級編譯器,其執行效率與一般的編譯器相比平均效率要高 20%~30%。gcc 編譯器能將 C、C++語言源程序、匯程式化序和目標程序編譯、連接成可執行文件,如果沒有給出可執行文件的名字,gcc 將生成一個名爲 a.out 的文件。

gcc 編譯流程

gcc 編譯一個源代碼時,會依次經過預處理-編譯-彙編-鏈接這 4 個流程。

  • 預處理,對源代碼中的文件包含、預編譯語句等進行分析,生成預編譯文件:gcc -E hello.c -o hello.i
  • 編譯,生成彙編文件:gcc -S hello.i -o hello.s
  • 彙編,生成目標文件:gcc -c hello.s -o hello.o
  • 鏈接,生成可執行文件:gcc hello.o -o hello

gcc 參數表

參數 含義
-E 只預處理,不編譯,生成編譯代碼.i
-S 只編譯,不彙編,生成彙編代碼.s
-c 只編譯,不鏈接,生成目標文件.o
-o file 把輸出文件輸出到 file 裏
-v 打印 gcc 的版本
-I dir 在頭文件的搜索路徑列表中加入 dir 目錄
-L dir 在庫文件的搜索路徑列表中加入 dir 目錄
-static 顯示鏈接靜態庫(靜態庫也可以用動態庫的方式鏈接)
-llibrary 顯示鏈接名爲 library 的動態庫

靜態庫

靜態庫的名字是libxxx.a,利用靜態函數庫編譯成的可執行文件比較大,因爲整個函數庫的全部代碼都會被整合進目標代碼中,他的優點就顯而易見了,即編譯後的執行程序不需要外部的函數庫支持,因爲所有使用的函數都已經被編譯進去了。當然這也會成爲他的缺點,因爲如果靜態函數庫改變了,那麼你的程序必須重新編譯。

靜態庫的代碼在編譯時鏈接到應用程序中,因此編譯時庫文件必須存在,並且需要通過-L參數傳遞給編譯器,應用程序在開始執行時,庫函數代碼將隨程序一起調入進程內存段直到進程結束,其執行過程不需要原靜態庫存在。

生成靜態庫

在 UNIX 中,使用ar libxx.a xx.o命令創建或者操作靜態庫。

參數 含義
-r 將目標文件插入靜態庫尾或者替換靜態庫中同名文件
-c 創建靜態庫文件

使用靜態庫的例子

// add.h
#ifndef _ADD_H
#define _ADD_H
int add(int a, int b);
#endif
// add.cpp
#include "add.h"
int add(int a, int b)
{
	return a + b;
}
// sub.h
#ifndef _SUB_H
#define _SUB_H
int sub(int a, int b);
#endif
// sub.cpp
#include "sub.h"
int sub(int a, int b)
{
	return a-b;
}
// main.cpp
#include "add.h"
#include "sub.h"
#include <iostream>

int main()
{
	std::cout << add(1, 2) << std::endl;
	std::cout << sub(1, 2) << std::endl;
}
  • g++ -c add.cpp, g++ -c sub.cpp,將源代碼編譯成.o文件
  • ar cr libmymath.a add.o sub.o,將.o文件生成靜態庫
  • g++ main.cpp -L ./ -static -lmymath,說明是靜態庫,默認是加載動態庫的
  • 或者g++ main.cpp libmymath.a,此時不用指定靜態庫

動態庫

動態庫的名字是libxxx.so,相對於靜態函數庫,動態函數庫在編譯的時候 並沒有被編譯進目標代碼中,而是等到程序執行到相關函數時才調用該函數庫裏的相應函數,因此動態函數庫所產生的可執行文件比較小。由於函數庫沒有被整合進程序,而是程序運行時動態的申請並調用,所以程序的運行環境中必須提供相應的庫。動態函數庫的改變並不影響程序,所以動態函數庫的升級比較方便。

當需要載入動態庫代碼時,Linux 會按照某種路徑查找動態庫,一般有兩種方法。

  • 帶編譯路徑,g++ main.cpp -L ./ -lmymath或者g++ main.cpp libmymath.so
  • 更改環境變量,LD_LIBPARY_PATH=./, export LD_LIBPARY_PATH,但是這樣做,退出終端就失效了。
  • 更改配置文件,將上面更改環境變量的方法寫入到~/.bashrc文件中。
  • 還可以將自己的動態庫文件複製到/usr/lib, /usr/local/lib中。

生成動態庫

不同的 Linux 系統,鏈接動態庫方法,實現細節不一樣。編譯 PIC 型.o中間文件的方法一般是採用 C 語言編譯器的-KPIC或者-fpic選項,有的 Linux 版本 C 語言編譯器默認帶上了 PIC 標準。創建最終動態庫的方法一般採用 C 語言編譯器的-G 或者-shared 選項。g++ -fPIC -shared -o libmymath.so add.cpp sub.cpp

參數 含義
-shared 指定生成動態連接庫
-fPIC 編譯爲位置獨立的代碼
-L dir 表示要連接的庫在 dir 目錄中
-llibrary 顯示鏈接名爲 liblibrary.so 的動態庫
LD_LIBRARY_PATH 動態庫的路徑的環境變量

使用動態庫的例子

源代碼同靜態庫。

  • g++ -fPIC -shared -o libmymath.so add.cpp sub.cpp,生成動態庫
  • g++ main.cpp -L ./ -lmymath
  • 或者g++ main.cpp libmymath.so

Makefile 項目管理

一個項目,它的程序大體步驟爲:

  1. 用編輯器編寫源代碼,如.c文件。
  2. 用編譯器編譯代碼生成目標文件,如.o文件。
  3. 用鏈接器連接目標代碼生成可執行文件,如.exe文件。

但如果一個項目源文件太多,一個一個編譯時就會特別麻煩,於是人們想到,爲什麼不設計一種類似批處理的程序,來批處理編譯源文件呢,於是就有了 make 工具,它是一個自動化編譯工具,你可以使用一條命令實現完全編譯。但是你需要編寫一個規則文件,make 依據它來批處理編譯,這個文件就是 makefile,所以編寫 makefile 文件也是一個程序員所必備的技能。

但對於一個大項目,編寫 makefile 實在是件複雜的事,於是人們又想,爲什麼不設計一個工具,讀入所有源文件之後,自動生成 makefile 呢,於是就出現了 cmake 工具,它能夠輸出各種各樣的 makefile 或者 project 文件,從而幫助程序員減輕負擔。但是隨之而來也就是編寫 cmakelist 文件,它是 cmake 所依據的規則。所以在編程的世界裏沒有捷徑可走,還是要腳踏實地的。所以總的流程如下:

流程圖

發佈了24 篇原創文章 · 獲贊 12 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章