? ? ? ? Linux下的靜態庫為lib*.a格式的二進制文件(目標文件),對應于Windows下的.lib格式的文件。
(1)命名規則
? ? ? ? lib+庫名字+ .a?? libMytest.a ,則庫名字為mytest。下面以具體的代碼為例介紹如何制作靜態庫。
//main.c
#include <stdio.h>
#include "head.h"
int main(void)
{int sum = add(2, 24);printf("sum = %d\n", sum);return 0;
}//head.h
#ifndef __HEAD_H_
#define __HEAD_H_
int add(int , int);
int sub(int , int);
int mul(int , int);
int div(int , int);
#endif//add.c
int add(int a, int b)
{int result = a + b;return result;
}//div.c
int div(int a, int b)
{int result = a / b;return result;
}//mul.c
int mul(int a, int b)
{int result = a * b;return result;
}//sub.c
int sub(int a, int b)
{int result = a - b;return result;
}
? ? ? ? 以上有5段代碼:main.c、head.h、add.c、div.c、mul.c和sub.c。要求將add.c、div.c、mul.c和sub.c制作成庫文件提供給客服使用,這4個文件都是關于頭文件head.h中函數的詳細實現,因此為了不想讓客服知道函數實現的細節和方法,不能將源碼提供給客服,而是以庫文件(二進制文件)的形式提供給客服使用即可。至于如何使用,庫文件已經將函數接口留在了頭文件head.h中,即4個函數聲明。用戶看了頭文件就知道如何使用庫文件了,即如何使用函數。因此最后只需要將main.c、head.h和庫文件給客服即可。(因此,一般庫文件與相對應的頭文件是同一個人來完成的)
? ? ? ? 先強調一下gcc的一個使用。-c參數是用來生成目標文件.o的,但是不鏈接。如: gcc -c zsx.s -o zsx.o? ? gcc -c zsx.c -o zsx.o? ? 對于上面4個.c文件,若工作目錄中只有這4個.c文件,可以用*.c表示這四個文件:? gcc -c *.c? ? 則會生成4個.o文件:add.o? div.o? mul.o和sub.o(在不指出輸出文件名字時,默認是將.c文件的.c改為.o)?。?? gcc -c *.c? ?等價于對每一個單獨的.c文件進行預處理、編譯、匯編后生成各自的 .o文件(檔案庫文件)。同理,對于gcc a.c b.c c.c d.c 最后生成一個a.out文件,其先對每一個源文件生成目標文件,然后將這些目標文件與需要的靜態庫文件鏈接形成可執行文件,至于需要的動態庫文件則是在程序運行時才會加載進去。? ? ??
(2)制作靜態庫
? ? ? ? 1)生成對應的.o文件? 2)將生成的.o文件打包。 ar rcs + 靜態庫的名字(libmytest.a)+ 生成的所有的.o??
? ? ? ??一個頭文件(head.h,/mnt/hgfs/share/gcc/Calc/include)和四個.c文件(add.c、div.c、mul.c、sub.c, /mnt/hgfs/share/gcc/Calc/src):? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?[root@localhost src]# gcc -c *.c
[root@localhost src]# ls
add.c ?add.o ?div.c ?div.o ?mul.c ?mul.o ?sub.c ?sub.o
[root@localhost src]# ar rcs libMytest.a *.o
[root@localhost src]# ls
add.c ?add.o ?div.c ?div.o ?libMytest.a ?mul.c ?mul.o ?sub.c ?sub.o
此時已經生成了靜態庫文件libMytest.a,該庫文件包含了4個庫函數add.o、div.o 、mul.o和sub.o。ar 工具不包含在gcc中,r --> 將文件插入靜態庫中;c --> 創建靜態庫,不管庫是否存在(存在就不創建);s --> 寫入一個目標文件索引到庫中,或者更新一個存在的目標文件索引(即方便找到需要的庫函數)。ar類似于命令,rcs是三個參數。
(3)使用靜態庫
? ? ? ? 將生成的libMytest.a庫文件和頭文件head.h發送給客服,客服就可以根據頭文件中的接口情況,來知道庫文件的功能 (具體怎么實現的他也不知道),從而客服就可以使用庫文件來完成自己的工作了(main.c):
[root@localhost Calc]# gcc -pedantic -pipe -Wall main.c -I include/ -L src/ -lMytest -o zsx? ? 也等價于:
[root@localhost Calc]# gcc -pedantic -pipe -Wall main.c -I include/ src/libMytest.a -o zsx
[root@localhost Calc]# ./zsx?
sum = 26
分析main.c可以知道,還另外有一個頭文件stdio.h,其對應的是printf函數的聲明,其庫文件是printf函數實現的庫文件,該庫文件有C提供,它們是標準頭、庫文件,因此不需要指明路徑和名稱,且該庫函數為動態庫函數,當程序在運行過程中需要該庫函數時,才會根據頭文件找到相應的庫函數并加載進入內存空間。而,libMytest.a不一樣,為靜態庫文件,因此在載入內存之前就已經鏈接在了一起成為程序代碼的一部分。注意:main函數只是用了add函數,因此在鏈接時只是鏈接了add.o庫函數,并非鏈接了整個庫文件,只需要鏈接使用到的庫函數即可。 例如,一個庫文件libZsx.a包含了qw.o(其有a( )和b( )兩個函數的實現)、qe.o(其有c( )和d( )兩個函數的實現)和qr.o(其有e( )和f( )兩個函數的實現)。當mani函數只是用到a( )和d( )兩個函數時,則最終在鏈接生成可執行程序時,只是會鏈接qw.o和qe.o兩個庫函數,即main函數和這兩個庫函數都在代碼區。
? ? ? ? 庫函數與可執行程序只是差最后一步,都具有前面是哪個階段,且各種規定都完全一樣,都是二進制代碼。
? ? ? ? 調試通過之后,加上-O參數,對代碼進行優化,可以減小最后可執行文件的體積。
(3)靜態庫的優缺點
nm命令可以查看靜態庫文件(.a)和最后生成的可執行文件的詳細屬性。nm?ibMytest.a? 可以查看該靜態庫文件有哪些庫函數(.o文件)。? ?nm zsx 顯示的信息中有:
000000000040056c T add
0000000000400530 T main? //main函數在鏈接的時候加進去的啟動代碼
參數T代表add和main在代碼區。即參數T表示在代碼區的內容。
靜態庫優點:1.發布程序的時候不需要提供對應的庫(動態庫需要);2.加載庫的速度快(庫函數就在代碼區)。缺點:庫被打包到代碼中,增加了代碼的體積;2.庫一旦發生了改變,需要對整個程序進行重新編譯。