C/C++ 動態庫與靜態庫的製作和使用

靜態庫的用法

靜態庫的文件名 libxxx.a
做靜態庫的命令:

ar rcs libxxx.a file1.o file2.o file.o

使用靜態庫:

gcc main.c -L lib/ -lxxx

注意:-L的作用是告訴gcc你的libxxx.a放在了哪個目錄裏;-l的作用是告訴gcc使用哪個靜態庫。
舉個例子,目錄結果如下:

├── include
│   └── head.h
├── lib
│   
├── main.c
└── src
    ├── add.c
    ├── mul.c
    └── sub.c

head.h

int add(int, int);
int sub(int, int);
int mul(int, int);

add.c

int add(int a, int b){
  return a + b;
}

sub.c

int sub(int a, int b){
  return a - b;
}

mul.c

int mul(int a, int b){
  return a * b;
}

步驟1:在src目錄執行下面的命令,生產.o文件

gcc -c *.c   

執行後,在src目錄下生產了add.o,sub.o,mul.o三個文件

步驟2:在src目錄執行下面的命令,生產libCalc.a文件

ar rcs libCalc.a *.o

執行後,在src目錄下生產了libCalc.a

步驟3:在src目錄執行下面的命令,把libCalc.a移動到上層的lib文件夾

mv libCalc.a ../lib/

執行後,在lib文件夾出現了libCalc.a

main.c

#include <stdio.h>
#include "head.h"

int main(){
  int a = 10, b = 5;
  printf("add:%d\n",add(a, b));
  printf("sub:%d\n",sub(a, b));
  printf("mul:%d\n",mul(a, b));
}

步驟4:在src的上層目錄,執行下面命令生產a.out文件

gcc main.c -I include/ -L lib/ -lCalc

-I:指定頭文件所在的路徑

-L:指定靜態庫文件所在的路徑

-l:指定使用哪個靜態庫

最後目錄結構如下:

├── a.out
├── include
│   └── head.h
├── lib
│   └── libCalc.a
├── main.c
└── src
    ├── add.c
    ├── add.o
    ├── mul.c
    ├── mul.o
    ├── sub.c
    └── sub.o

動態庫的用法

動態庫的文件名 libxxx.so

編譯時,需要加【-fPIC】選項。

它的作用是,編譯出與位置無關的代碼。因爲動態庫在加載到下圖的共享庫區的時候,不一定加載到哪個位置,

所以加了-fPIC後,就記錄了每個函數相對於這個動態庫頭地址的偏移,加載後動態庫在內存裏的頭地址是知道的,又知道了每個函數相對於頭的偏移量,自然而然地就能夠找到每個函數在內存裏的位置了。
在這裏插入圖片描述

[root@HC-25-68-165 CDynamic]# cat func.c
#include <stdio.h>
void func(void)
{
	printf("In the dynamic libbbbbb\n");
}
[root@HC-25-68-165 CDynamic]# 
[root@HC-25-68-165 CDynamic]# 
[root@HC-25-68-165 CDynamic]# cat main.c
#include <stdio.h>
#include <unistd.h>

void func(void);

int main(void)
{
	int i = 0;
	// 爲了測試在  for 循環過程中,更新 func 函數重新生成動態庫,
	// 看看在不重啓 main 的情況下新版 func 能否立刻生效,答案是不能!參考後面的:靜態庫和動態庫的特點
	for(i = 0; i < 1000; i++)
	{
		printf("In the main: i = %d\n", i);
		func();
		sleep(1);
	}	
	return 0;
}
[root@HC-25-68-165 CDynamic]# 
[root@HC-25-68-165 CDynamic]# gcc func.c -o func.o -c -fPIC
[root@HC-25-68-165 CDynamic]# ls
func.c  func.o  main.c
[root@HC-25-68-165 CDynamic]# 
[root@HC-25-68-165 CDynamic]# gcc func.o -shared -o libfunc.so 
[root@HC-25-68-165 CDynamic]# ls
func.c  func.o  libfunc.so  main.c
[root@HC-25-68-165 CDynamic]# 
[root@HC-25-68-165 CDynamic]# 上面兩句可以合併爲  gcc func.c  -fPIC  -shared -o libfunc.so 
[root@HC-25-68-165 CDynamic]# 
[root@HC-25-68-165 CDynamic]# gcc main.c -o main -L . -l func
[root@HC-25-68-165 CDynamic]# ls
func.c  func.o  libfunc.so  main  main.c
[root@HC-25-68-165 CDynamic]# 
[root@HC-25-68-165 CDynamic]# 
[root@HC-25-68-165 CDynamic]# ./main
./main: error while loading shared libraries: libfunc.so: cannot open shared object file: No such file or directory
[root@HC-25-68-165 CDynamic]# 
[root@HC-25-68-165 CDynamic]# export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
[root@HC-25-68-165 CDynamic]# ./main 
In the main: i = 0
In the dynamic libbbbbb
In the main: i = 1
In the dynamic libbbbbb
^C
[root@HC-25-68-165 CDynamic]# 
[root@HC-25-68-165 CDynamic]# 
[root@HC-25-68-165 CDynamic]# ldd main
linux-vdso.so.1 =>  (0x00007fff8d5ff000)
libfunc.so => ./libfunc.so (0x00007f6422533000)
libc.so.6 => /lib64/libc.so.6 (0x00007f6422195000)
/lib64/ld-linux-x86-64.so.2 (0x00007f6422735000)
[root@HC-25-68-165 CDynamic]# 
[root@HC-25-68-165 CDynamic]# 
[root@HC-25-68-165 CDynamic]# rm -fr libfunc.so 
[root@HC-25-68-165 CDynamic]# ldd main
	linux-vdso.so.1 =>  (0x00007ffff45ff000)
	libfunc.so => not found
	libc.so.6 => /lib64/libc.so.6 (0x00007fbfa9c34000)
	/lib64/ld-linux-x86-64.so.2 (0x00007fbfa9fd3000)
[root@HC-25-68-165 CDynamic]# 

對於 “ error while loading shared libraries: libfunc.so: cannot open shared object file: No such file or directory”的解法方法?

辦法1:把做好的動態庫libCalc.so拷貝到【/lib】或者【/usr/lib】下,也可以用ln做軟硬連接
      但是這種方法不推薦使用。理由是,放在了系統的動態庫目錄裏,容易和別的庫重複,發生衝突。
      也可以臨時修改環境變量 LD_LIBRARY_PATH: export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.

辦法2:告訴系統,給我加一個動態庫的路徑,這個是最好的辦法。
      首先編輯/etc/ld.so.conf文件,添加你自己的庫的路徑
     然後,執行:【sudo ldconfig】,就OK了。

靜態庫和動態庫的特點:

靜態庫:
    編譯的時候,直接把靜態庫的代碼直接編譯到目標文件
    執行速度快,不需要在執行的時候加載動態庫
    庫文件修改後,必須重新編譯使用庫的代碼
動態庫
    編譯的時候,不把靜態庫的代碼直接編譯到目標文件
    執行速度慢,在執行的時候需要加載動態庫
    庫文件修改後,一般不需要重新編譯使用庫的代碼,只有在庫的接口改變時,才需要重新編譯使用庫的代碼。

對於動態庫需要仔細理解一點:如果 ./main 啓動後,把動態庫 libfunc.so刪除掉,那麼已啓動的 main 是不會報錯的。
也可以理解爲如果在 ./main 運行過程中,你更新了 libfunc.so 也是不會立刻生效的,需要重新啓動 main 纔可以。
這也是很好理解的:因爲在啓動 ./main 的瞬間會把動態庫立刻加載到內存裏,而不是 ./main 的執行過程中一直實時去檢測動態庫函數。

最後感謝: https://www.cnblogs.com/xiaoshiwang/p/10750141.html 和 https://blog.csdn.net/nanfeibuyi/article/details/81203021 的分享。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章