GCC的學習(二)頭文件及其庫製作

一、-I 頭文件路徑

1.1 工程結構簡單不需要-I選項

工程若源代碼及頭文件數量較少根本不需要-I選項,將他們放在同一級目錄下,源文件就可正常找到頭文件:

//ttt.cpp
#include <stdio.h>
#include "data.h"
int main()
{
	printf("Hello world\n");
	printf("Data in header is %lf\n",pi);
}
// data.h
double pi=3.1415;

在終端鍵入:

gcc ttt.cpp

即可正常編譯連接形成可執行文件。

1.2 工程結構較複雜

隨着工程的進展,爲了更清晰的結構,人們常常把頭文件和源文件,編譯結果分開放在不同的文件夾。僅爲演示,將ttt.cppdata,h分別放入兩個文件夾src和include中,如果仍然使用(1.1)方法將會發生錯誤:

junwuli@ubuntu:~/Desktop/gcctest/build$ gcc ../src/ttt.c 
../src/ttt.c:2:10: fatal error: data.h: No such file or directory
 #include "data.h"
          ^~~~~~~~
compilation terminated.

此時就需要用到-I選項:

junwuli@ubuntu:~/Desktop/gcctest/build$ gcc ../src/ttt.c -I ../include/
junwuli@ubuntu:~/Desktop/gcctest/build$ ./a.out
Hello world
Data in header is 3.141500

假設我們的主程序不想寫相對路徑的,只想寫一個簡潔的“head.h”而不是“./include/”就需要用到這個參數。

gcc hello.c -Iinclude -o app

或者是

gcc hello.c -I include -o app

二、庫的製作和使用

無論是靜態庫還是動態庫,都需要將源文件編譯成目標文件,再將目標文件整合在一起形成庫。這裏的這裏的庫,類似於生活中的倉庫,不過存放的內容是目標文件,倉庫裏有什麼,靠的就是頭文件。

大概是這樣的過程:

graph LR
源文件-->目標文件
目標文件-->庫的打包

2.1 庫的命名規則

和變量命名一樣,Linux庫的命名也要揭示庫的屬性、名稱。

libxxx.so.x.y.z
libxxx.a.x.y.z
部分 含義
lib 文件屬性
xxx 庫名稱
.so/.a 庫類型
.x.y.z 版本號[2]

2.2 靜態庫的製作和使用

2.2.1 製作靜態庫

以a.c b.c爲例,生成目標文件

gcc a.c b.c -c 

默認生成同名目標文件a.o b.o

根據上一步產生的a.o b.o材料,打包目標文件

ar rcs libtest.a a.o b.o

注:rcs 代表插入目標文件創建歸檔文件及引索。

2.2.2 使用靜態庫

libtest.a + 頭文件,頭文件加庫使用靜態庫

gcc main.c -I ./include/ -L ./lib/ -ltest -o app

注:nm(name)可以查看lib中的內容

2.3 動態庫製作和使用

2.3.1 製作動態庫

以a.c b.c爲例,-fPIC選項,生成目標文件[3]

gcc a.c b.c -c -fPIC(-fpic)

根據上一步產生的a.o b.o材料,–shared選項,打包目標文件

gcc -shared a.o b.o -o libxxx.so
2.3.2 使用動態庫

頭文件加庫設置使用動態庫

gcc main.c -I ./include/ -L ./lib/ -lxxx -o app

和靜態庫一樣的,指定庫所在路徑,庫的名稱。如果他和源文件在同一級目錄,OK,完全沒有問題,但是,如果它在下一級的lib當中,就會提示找不到了!

ldd 用靜態庫編譯的可執行文件

上面這個可以查看依賴庫,以及其是否被找到。

2.4 動態庫搜索路徑

動態庫和可執行程序、和.o文件他們的格式都爲ELF,系統使用ld-linux.so.X[1]來完成。按順序分別是:

  • ELF文件的DT_PRATH段
  • 環境變量LD_LIBRARY_PATH
  • 文件列表 /etc/ld.so.cache
  • /lib usr/lib
    在任意步驟找到,都將會載入內存正常使用,反之報找不到庫的錯誤。

註釋:

[1] Glibc安裝的庫中有一個爲ld-linux.so.X,其中X爲一個數字,在不同的平臺上名字也會不同,如ubuntu18.04就爲ld-linux-x86-64.so.2

[2]x表示主版本號,不同主版本一般不兼容;y次版本號,庫的增量升級,原接口不變但是新增了一些接口,同時兼容舊版本;z發佈版本號,一些庫功能錯誤、性能改善,接口不增加也不更改。

[3] 爲了兼容各個系統,在生成位置無關的代碼的時候,應該使用-fPIC參數。

參考文章:

【1】《-fpic 與-fPIC的區別》 作者:zhang_dawei666 (https://blog.csdn.net/xiangguiwang/article/details/81939237)

擴展:

動態和靜態庫的區別:https://www.cnblogs.com/mhscn/p/4264357.html

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