一、-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.cpp
與data,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