linux 編譯指定庫、標頭檔案的路徑問題
1. 為什麼會出現undefined reference to 'xxxxx'錯誤?
首先這是連結錯誤,不是編譯錯誤,也就是說如果只有這個錯誤,說明你的程式原始碼本身沒有問題,是你用編譯器編譯時引數用得不對,你沒有指定連結程式要用到得庫,比如你的程式裡用到了一些數學函式,那麼你就要在編譯引數裡指定程式要連結數學庫,方法是在編譯命令列里加入-lm。
2.-l引數和-L引數
-l引數就是用來指定程式要連結的庫,-l引數緊接著就是庫名,那麼庫名跟真正的庫文件名有什麼關係呢?就拿數學庫來說,他的庫名是m,他的庫檔名是libm.so,很容易看出,把庫檔名的頭lib和尾.so去掉就是庫名了。
-L引數跟著的是庫檔案所在的目錄名。再比如我們把
3. -include和-I引數
-include用來包含標頭檔案,但一般情況下包含標頭檔案都在原始碼裡用#include xxxxxx實現,-include引數很少用。-I引數是用來指定標頭檔案目錄,/usr/include目錄一般是不用指定的,gcc知道去那裡找,但是如果標頭檔案不在/usr/include裡我們就要用-I引數指定了,比如標頭檔案放在/myinclude目錄裡,那編譯命令列就要加上-I/myinclude引數了,如果不加你會得到一個"xxxx.h: No such file or directory"
4.幾個相關的環境變數
PKG_CONFIG_PATH:用來指定pkg-config用到的pc檔案的路徑,預設是/usr/lib/pkgconfig,pc檔案是文字檔案,副檔名是.pc,裡面定義開發包的安裝路徑,Libs引數和Cflags引數等等。
CC:用來指定c編譯器。
CXX:用來指定cxx編譯器。
LIBS:跟上面的--libs作用差不多。
CFLAGS:跟上面的--cflags作用差不多。
CC,CXX,LIBS,CFLAGS手動編譯時一般用不上,在做configure時有時用到,一般情況下不用管。
環境變數設定方法:
==============================================================================
[相關介紹]
應用程式(Applications)
應用程式通常都有固定的資料夾,系統通用程式放在/usr/bin,日後系統管理員在本地計算機安裝的程式通常放在/usr/local/bin或者/opt資料夾下。除了系統程式外,大部分個人用到的程式都放在/usr /local下,所以保持/usr的整潔十分重要。當升級或者重灌系統的時候,只要把/usr/local的程式備份一下就可以了。
一些其他的程式有自己特定的資料夾,比如X Window系統,通常安裝在/usr/X11中,或者/usr/X11R6。GNU的編譯器GCC,通常放置在/usr/bin或者/usr/local/bin中,不同的Linux版本可能位置稍有不同。
標頭檔案(Head Files)
在C語言和其他語言中,標頭檔案聲明瞭系統函式和庫函式,並且定義了一些常量。對於C語言,標頭檔案基本上散落於/usr/include和它的子資料夾下。其他的程式語言的庫函式分佈在編譯器定義的地方,比如在一些Linux版本中,X Window系統庫函式分佈在/usr/include/X11,GNU C++的庫函式分佈在/usr/include/g++。這些系統庫函式的位置對於編譯器來說都是“標準位置”,即編譯器能夠自動搜尋這些位置。
如果想引用位於標準位置之外的標頭檔案,我們需要在呼叫編譯器的時候加上-I標誌,來顯式的說明標頭檔案所在資料夾。比如,
$ gcc -I/usr/openwin/include hello.c會告訴編譯器除了標準位置外,還要去/usr/openwin/include看看有沒有所需的標頭檔案。詳細情況見編譯器的使用手冊(man gcc)。
庫函式(Library Files)
庫函式就是函式的倉庫,它們都經過編譯,重用性不錯。通常,庫函式相互合作,來完成特定的任務。比如操控螢幕的庫函式(cursers和ncursers庫函式),資料庫讀取庫函式(dbm庫函式)等。
系統呼叫的標準庫函式一般位於/lib以及/usr/lib。C編譯器(精確點說,聯結器)需要知道庫函式的位置。預設情況下,它只搜尋標準C庫函式。
庫函式檔案通常開頭字母是lib。後面的部分標示庫函式的用途(比如C庫函式用c標識, 數學庫函式用m標示),小數點後的字尾表明庫函式的型別:
- .a 指靜態連結庫
- .so 指動態連結庫
去/usr/lib看一下,你會發現,庫函式都有動態和靜態兩個版本。
檢視動態庫函式工具: ldd *.so
與標頭檔案一樣,庫函式通常放在標準位置,但我們也可以通過-L識別符號,來新增新的搜尋資料夾,-l指定特定的庫函式檔案。比如
$ gcc -o x11fred -L/usr/openwin/lib x11fred.c -lX11
上述命令就會在編譯期間,連結位於/usr/openwin/lib資料夾下的libX11函式庫,編譯生成x11fred。
靜態連結庫(Static Libraries)
最簡單的函式庫就是一些函式的簡單集合。呼叫庫函式中的函式時,需要在呼叫函式中include定義庫函式的標頭檔案。我們用-l選項新增標準函式庫之外的函式庫。
靜態函式庫,也稱為archives ,通常以後綴.a結尾。
我們也可以建立維護自己的靜態連結庫函式。下面就介紹一下:
我們建立的庫函式包括兩個函式,然後在後面的例項中呼叫其中之一。兩個庫函式名字分別是fred和bill,僅僅是輸出字串。
- 首先,我們分別編寫兩個原始檔(fred.c和bill.c),原始檔如下:
- /* fred.c */
- #include <stdio.h>
- void fred(int arg)
- {
- printf(“fred: you passed %d\n”, arg);
- }
- /* bill.c */
- #include <stdio.h>
- void bill(char *arg)
- {
- printf(“bill: you passed %s\n”, arg);
- }
2. 接下來,我們將這兩個原始檔編譯為兩個獨立的目標檔案。這裡要用到GCC的-c選項。命令如下所示:
$ gcc -c fred.c bill.c
$ ls *.o
bill.o fred.o
3. 然後,寫一個呼叫bill的測試函式,在此之前,最好為庫函式建立一個頭檔案。標頭檔案中有對庫函式的宣告。如果其他函式要呼叫庫函式,必須在其程式碼中包含標頭檔案。也可以在fred.c 和bill.c中包含該標頭檔案,有利於編譯器發現錯誤。標頭檔案lib.h的內容如下所示:
- /*
- This is lib.h. It declares the functions fred and bill for users
- */
- void bill(char *);
- void fred(int);
4. 測試函式program.c比較簡單,程式碼如下:
- #include “lib.h”
- int main()
- {
- bill(“Hello World”);
- exit(0);
- }
5. 現在我們可以編譯測試一下程式了:
$ gcc -c program.c
$ gcc -o program program.o bill.o
$ ./program
bill: we passed Hello World
6. 接下來,我們要建立一個函式庫。利用ar函式建立歸檔檔案(archive),然後將目標檔案加入其中。$ ar crv libfoo.a bill.o fred.o
a - bill.o
a - fred.o
7. 現在可以使用函式庫中的函數了。我們用-l指定函式庫的名字。因為該函式庫沒有在標準資料夾中,我們還需要用-L將當前資料夾"."新增到搜尋路徑中。編譯命令如下所示:
$ gcc -o program program.o -L. -lfoo
共享連結庫(Shared Libraries)
靜態連結庫的一個缺點是,如果我們同時運行了許多程式,並且它們使用了同一個庫函式,這樣,在記憶體中會大量拷貝同一庫函式。這樣,就會浪費很多珍貴的記憶體和儲存空間。使用了共享連結庫的Linux就可以避免這個問題。
共享函式庫和靜態函式在同一個地方,只是字尾有所不同。比如,在一個典型的Linux系統,標準的共享數序函式庫是/usr/lib/libm.so。
當一個程式使用共享函式庫時,在連線階段並不把函式程式碼連線進來,而只是連結函式的一個引用。當最終的函式匯入記憶體開始真正執行時,函式引用被解析,共享函式庫的程式碼才真正匯入到記憶體中。這樣,共享連結庫的函式就可以被許多程式同時共享,並且只需儲存一次就可以了。共享函式庫的另一個優點是,它可以獨立更新,與呼叫它的函式毫不影響。