1. 爲什麼會出現undefined reference to 'xxxxx'錯誤?
首先這是鏈接錯誤,不是編譯錯誤,也就是說如果只有這個錯誤,說明你的程序源碼本身沒有問題,是你用編譯器編譯時參數用得不對,你沒有指定鏈接程序要用到得庫,比如你的程序裏用到了一些數學函數,那麼你就要在編譯參數裏指定程序要鏈接數學庫,方法是在編譯命令行里加入-lm。
2.-l參數和-L參數
-l參數就是用來指定程序要鏈接的庫,-l參數緊接着就是庫名,那麼庫名跟真正的庫文件名有什麼關係呢?就拿數學庫來說,他的庫名是m,他的庫文件名是libm.so,很容易看出,把庫文件名的頭lib和尾.so去掉就是庫名了。
-L參數跟着的是庫文件所在的目錄名。再比如我們把libtest.so放在/aaa/bbb/ccc目錄下,那鏈接參數就是-L/aaa/bbb/ccc -ltest另外,大部分libxxxx.so只是一個鏈接
3. -include和-I參數
-include用來包含頭文件,但一般情況下包含頭文件都在源碼裏用#include xxxxxx實現,-include參數很少用。-I參數是用來指定頭文件目錄,/usr/include目錄一般是不用指定的,gcc知道去那裏找,但是如果頭文件不在/usr/include裏我們就要用-I參數指定了,比如頭文件放在/myinclude目錄裏,那編譯命令行就要加上-I/myinclude參數了,如果不加你會得到一個"xxxx.h: No such file or directory"的錯誤。-I參數可以用相對路徑,比如頭文件在當前目錄,可以用-I.來指定
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時有時用到,一般情況下不用管。
環境變量設定方法:export ENV_NAME=xxxxxxxxxxxxxxxxx
==============================================================================
[相關介紹]
應用程序(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看一下,你會發現,庫函數都有動態和靜態兩個版本。
與頭文件一樣,庫函數通常放在標準位置,但我們也可以通過-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。
當一個程序使用共享函數庫時,在連接階段並不把函數代碼連接進來,而只是鏈接函數的一個引用。當最終的函數導入內存開始真正執行時,函數引用被解析,共享函數庫的代碼才真正導入到內存中。這樣,共享鏈接庫的函數就可以被許多程序同時共享,並且只需存儲一次就可以了。共享函數庫的另一個優點是,它可以獨立更新,與調用它的函數毫不影響。