庫
庫是寫好的現有的,成熟的,可以復用的代碼。現實中每個程序都要依賴很多基礎的底層庫,不可能每個人的代碼都從零開始,因此庫的存在意義非同尋常
本質上來說庫是一種可執行代碼的二進制形式,可以被操作系統載入內存執行。庫有兩種:靜態庫(.a、.lib)和動態庫(.so、.dll)
靜態庫
之所以稱為“靜態庫”,是因為在鏈接階段,會將匯編生成的目標文件.o與引用到的庫一起鏈接打包到可執行文件中。因此對應的鏈接方式稱為靜態鏈接。
靜態庫與匯編生成的目標文件一起鏈接為可執行文件,那么靜態庫必定跟.o文件格式相似。其實一個靜態庫可以簡單看成是一組目標文件(.o/.obj文件)的集合,即很多目標文件經過壓縮打包后形成的一個文件。
特點
- 可以實現共享代碼
- 不能再包含其他靜態鏈接庫
- 本身包含了代碼,地址符號表等,靜態鏈接庫的使用中,連接器從靜態鏈接庫獲取所有被引用的函數,並將庫同代碼一起放到可執行文件中
- 代碼裝載速度快,執行速度略比動態鏈接庫快
- 只需保證在開發者的計算機中有正確的 .a 文件,在以二進制形式發布程序時不需考慮在用戶的計算機上 .a 文件是否存在及版本問題
- 使用靜態鏈接生成的可執行文件體積較大,包含相同的公共代碼,造成浪費;
創建
- 編寫源文件,通過 gcc -c xxx.c 生成目標文件
- 使用 ar 命令歸檔目標文件,生成靜態庫
- 配合靜態庫,寫一個使用靜態庫中的函數的頭文件
- 鏈接靜態庫,生成可執行文件
例子
源文件
add.c 文件:
int add(int a, int b) { return a + b; }
subtract.c 文件:
int subtract(int a, int b) { return a - b; }
編譯兩個文件,生成目標文件 add.o subtract.o
gcc -c add.c subtract.c
歸檔目標文件,生成靜態鏈接庫 libmath.a
使用 ar 命令將目標文件壓縮到一起,並且對其進行編號和索引,以便於查找和檢索
ar rcv libmath.a add.o subtract.o
有關 ar 命令的使用,makepage 有詳細的說明,常用的選項是:
c -- 如果庫文件不存在,生成新的庫文件,並不打印警告 r -- 代替庫中已有的文件或者插入新的文件 v -- 輸出詳細信息
d -- 刪除庫中的某個目標文件
t -- 列出歸檔文件中包含的目標文件
我們生成的庫文件名字必須形如 libxxx.a,這樣我們在鏈接庫的時候,就可以使用 -lxxx。
當我們鏈接時,指定 -lxxx,鏈接器就會在指定的目錄查找 libxxx.a 或 libxxx.so 文件
編寫頭文件 mymath.h
#ifndef _MYMATH_H_ #define _MYMATH_H_
int add(int, int); int subtract(int, int); #endif
使用靜態庫
編寫文件 main.c
#include <stdio.h> #include "mymath.h"
int main() { printf("%d, %d\n", add(2, 3), subtract(9, 8)); return 0; }
編譯鏈接
gcc main.c -L. -lmath
這里,-L 指定搜索鏈接庫的包含當前目錄
使用Makefile
.PHONY: all all: build target build: libmath.a libmath.a: add.o subtract.o ar rc $@ add.o subtract.o add.o: add.c gcc -c $< subtract.o: subtract.c gcc -c $< target: a.out a.out: main.c gcc main.c -L. -lmath .PHONY: clean clean: rm *.o libmath.a a.out