一、前言
上次寫了一篇關(guān)于Makefile的文章。有很多粉絲留言,有的粉絲想進一步了解cmake的使用方法,還有的粉絲想知道如何將一些函數(shù)編譯成動態(tài)庫或者靜態(tài)庫,然后再將該庫編譯到內(nèi)存中。
一口君必須安排,本篇先講如何將一些函數(shù)編譯成動態(tài)庫或者靜態(tài)庫。
這就涉及到一個庫的概念,關(guān)于制作的庫的基礎(chǔ)知識,一口君已經(jīng)在下面這篇文章中詳細的講述了相關(guān)概念,建議大家先看下面這篇文章。
《Linux庫概念,動態(tài)庫和靜態(tài)庫概念》
本文,一口君將繼續(xù)以之前的 電話號碼管理系統(tǒng)的項目為基礎(chǔ),給大家詳細講解如何將該項目中的函數(shù)制作成動態(tài)庫和靜態(tài)庫。
二、 基礎(chǔ)知識
1) 靜態(tài)庫
所謂靜態(tài)庫,就是在靜態(tài)編譯時由編譯器到指定目錄尋找并且進行鏈接,一旦鏈接完成,最終的可執(zhí)行程序中就包含了該庫文件中的所有有用信息,包括代碼段、數(shù)據(jù)段等。
2)動態(tài)庫
所謂動態(tài)庫,就是在應(yīng)用程序運行時,由操作系統(tǒng)根據(jù)應(yīng)用程序的請求,動態(tài)到指定目錄下尋找并裝載入內(nèi)存中,同時需要進行地址重定向。
3)庫文件命名
靜態(tài)庫的名字一般為libxxxx.a,其中xxxx是該lib的名稱;動態(tài)庫的名字一般為libxxxx.so.x.y.z,含義如下圖所示:
4)制作庫文件常用參數(shù)
首先需要了解gcc編譯庫要用到一些參數(shù),很重要。
參數(shù) | 含義 |
---|---|
-shared | 指定生成動態(tài)鏈接庫。 |
-static | 指定生成靜態(tài)鏈接庫。 |
-fPIC | 表示編譯為位置獨立的代碼,用于編譯共享庫。目標文件需要創(chuàng)建成位置無關(guān)碼,概念上就是在可執(zhí)行程序裝載它們的時候,它們可以放在可執(zhí)行程序的內(nèi)存里的任何地方。 |
-L | 表示要連接的庫在當前目錄中。 |
-l | 指定鏈接時需要的動態(tài)庫。編譯器查找動態(tài)連接庫時有隱含的命名規(guī)則,即在給出的名字前面加上lib,后面加上.so來確定庫的名稱。 |
-Wall | 生成所有警告信息。 |
-ggdb | 此選項將盡可能的生成gdb的可以使用的調(diào)試信息。 |
-g | 編譯器在編譯的時候產(chǎn)生調(diào)試信息。 |
-c | 只激活預(yù)處理、編譯和匯編,也就是把程序做成目標文件(.o文件)。 |
-Wl,options | 把參數(shù)(options)傳遞給鏈接器ld。如果options中間有逗號,就將options分成多個選項,然后傳遞給鏈接程序。 |
三、 制作靜態(tài)庫
原始文件目錄如下:
peng@ubuntu:/mnt/hgfs/code/phone3$?tree?.
.
├──?main.c
├──?phone.c
└──?phone.h
0?directories,?3?files
其中
phone.c包含了對鏈表的所有的操作函數(shù)
phone.h 是phone.c中所有函數(shù)的原型說明
main.c是主程序
下面我們將phone.c制作成靜態(tài)庫。
1. 把 listd.c 編譯成.o文件
?peng@ubuntu:/mnt/hgfs/code/phone3$?gcc?-c?phone.c
2. 使用 ar 命令生成靜態(tài)庫libadd.a
靜態(tài)庫名字遵循靜態(tài)庫命名的規(guī)則 lib + 名字 + .a
peng@ubuntu:/mnt/hgfs/code/phone3$?ar?-rc?libphone.a?phone.o?
3. 將庫和頭文件拷貝到其他目錄下
將庫文件移動到lib目錄下
peng@ubuntu:/mnt/hgfs/code/phone3$?mkdir?lib
peng@ubuntu:/mnt/hgfs/code/phone3$?mv?libphone.a?lib
移動頭文件到include目錄下
peng@ubuntu:/mnt/hgfs/code/phone3$?mkdir?include
peng@ubuntu:/mnt/hgfs/code/phone3$?mv?phone.h?include/
刪除phone.c
peng@ubuntu:/mnt/hgfs/code/phone3$?rm?phone.c
此處可不刪除,下面的的編譯已經(jīng)用不到該文件
刪除僅僅是為了排除干擾,有些同學(xué)會以為這個文件還會被編譯進去
最終文件結(jié)構(gòu)如下:
peng@ubuntu:/mnt/hgfs/code/phone3$?tree?./
./
├──?include
│???└──?phone.h
├──?lib
│???└──?libphone.a
├──?main.c
└──?run
2?directories,?6?files
lib include 目錄也可以是其他目錄,實際項目中庫文件和頭文件都會放到一些指定目錄下
4.編譯
值編譯main.c,會有以下錯誤提示,主要是因為phone.h
peng@ubuntu:/mnt/hgfs/code/phone3$?gcc?main.c?
main.c:3:19:?致命錯誤:phone.h:沒有那個文件或目錄
編譯中斷。
制定頭文件位置,編譯結(jié)果如下,可以看到錯誤提示,“沒有定義create”,這是因為在鏈接的時候找打不到這些函數(shù)的定義的地方
peng@ubuntu:/mnt/hgfs/code/phone3$?gcc?main.c?-I?./include
/tmp/cctUUKm9.o:?In?function?`management':
main.c:(.text+0x109):?undefined?reference?to?`create'
main.c:(.text+0x120):?undefined?reference?to?`delete'
main.c:(.text+0x137):?undefined?reference?to?`search'
main.c:(.text+0x14e):?undefined?reference?to?`display'
main.c:(.text+0x167):?undefined?reference?to?`allfree'
/tmp/cctUUKm9.o:?In?function?`main':
main.c:(.text+0x2e3):?undefined?reference?to?`init'
collect2:?ld?返回?1
最終我們執(zhí)行
peng@ubuntu:/mnt/hgfs/code/phone3$?gcc?main.c?-I?./include?./lib/libphone.a?
指定了頭文件和庫文件位置,執(zhí)行結(jié)果如下:
與之前運行現(xiàn)象是一樣的。
可見,使用庫的時候我們必須制定頭文件目錄以及庫目錄。
四、 制作動態(tài)庫
原始文件
peng@ubuntu:/mnt/hgfs/code/phone3$?tree?.
.
├──?main.c
├──?phone.c
└──?phone.h
0?directories,?3?files
1. 把phone.c編譯成動態(tài)鏈接庫libphone.so
gcc?-fPIC?-o?libphone.o?-c?phone.c
gcc?-shared?-o?libphone.so?libphone.o
也可以直接使用一條命令
gcc?-fPIC?-shared?-o?libphone.so?phone.c
2. 動態(tài)庫的安裝
通常動態(tài)庫拷貝到/lib下:
peng@ubuntu:/mnt/hgfs/code/phone4$?sudo?mv?libphone.so?/lib/
[sudo]?password?for?peng:?
刪除phone.c
peng@ubuntu:/mnt/hgfs/code/phone3$?rm?phone.c
3. 編譯執(zhí)行
編譯動態(tài)庫:
peng@ubuntu:/mnt/hgfs/code/phone4$?gcc?main.c?-lphone?-o?run
此時使用我們制作的動態(tài)庫,只需要加上 -lphone即可
注意觀察編譯時動態(tài)庫的名字與庫文件對應(yīng)關(guān)系
libphone.so<--------->-lphone?
執(zhí)行結(jié)果如下:
五、重新建立工程
下面我們將文件重新放置
當前文件目錄如下:
./include
└──?phone.h
./Makefile?
./obj
└──?Makefile
./src
├──?main.c
└──?Makefile
0?directories,?5?files
并添加3個Makefile
編譯步驟如下:
-
- 聲明環(huán)境變量
CC???????編譯名稱
LIBS?????用到的動態(tài)庫
SUBDIRS??子目錄
OBJS?????src下所有的目標文件
BIN??????最終生成的可執(zhí)行程序名字
OBJS_DIR?目標文件存放目錄
BIN_DIR??可執(zhí)行程序存儲目錄
-
-
- 執(zhí)行make的默認目標all,依賴CHECK_DIR $(SUBDIRS)
-
-
-
- 執(zhí)行目標CHECK_DIR ,創(chuàng)建目錄bin
-
-
-
- 執(zhí)行目標@ ,進入子目錄src、obj執(zhí)行子目錄的Makefile,
-
-
-
- 打印語句
-
echo begin compile phone!
-
-
- 進入子目錄src執(zhí)行Makfile,
-
-
- 執(zhí)行命令
@$(CC)?-c?main.c?-I../include?-o?../$(OBJS_DIR)/main.o??
@?:打印該條命令
-I../include?頭文件在上一級目錄下的include中
-o?../$(OBJS_DIR)/main.o?生成的目標文件存放在../obj/main.o
-
-
- 進入子目錄obj執(zhí)行Makfile,
-
-
-
- 目標為../bin/phone:main.o
-
-
- 執(zhí)行命令
@$(CC)?-o?$@?$^??$(LIBS)
@$(CC)?同上
$@?表示生成的目標文件,即../bin/phone
$^?表示所有的依賴文件,即上面:后面目標文件main.o
編譯完成后就會在bin目錄下創(chuàng)建可執(zhí)行程序文件phone,
運行結(jié)果如下:
所有源碼:公眾號【一口Linux】后臺回復(fù):電話號碼管理