linux 編譯指定庫、頭文件的路徑問題

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/pkgconfigpc文件是文本文件,擴展名是.pc,裏面定義開發包的安裝路徑,Libs參數和Cflags參數等等。

CC:用來指定c編譯器。

CXX:用來指定cxx編譯器。

LIBS:跟上面的--libs作用差不多。

CFLAGS:跟上面的--cflags作用差不多。

CCCXXLIBSCFLAGS手動編譯時一般用不上,在做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,僅僅是輸出字符串。

  1. 首先,我們分別編寫兩個源文件(fred.c和bill.c),源文件如下:
  1. /* fred.c */  
  2. #include <stdio.h>  
  3. void fred(int arg)  
  4. {  
  5. printf(“fred: you passed %d\n”, arg);  
  6. }  
  7. /* bill.c */  
  8. #include <stdio.h>  
  9. void bill(char *arg)  
  10. {  
  11. printf(“bill: you passed %s\n”, arg);  
  12. }  
/* 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的內容如下所示:

  1. /* 
  2. This is lib.h. It declares the functions fred and bill for users 
  3. */  
  4. void bill(char *);  
  5. void fred(int);  
/* This is lib.h. It declares the functions fred and bill for users */ void bill(char *); void fred(int);

    4. 測試函數program.c比較簡單,代碼如下:

  1. #include “lib.h”  
  2. int main()  
  3. {  
  4.     bill(“Hello World”);  
  5.     exit(0);  
  6. }  
#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。

當一個程序使用共享函數庫時,在連接階段並不把函數代碼連接進來,而只是鏈接函數的一個引用。當最終的函數導入內存開始真正執行時,函數引用被解析,共享函數庫的代碼才真正導入到內存中。這樣,共享鏈接庫的函數就可以被許多程序同時共享,並且只需存儲一次就可以了。共享函數庫的另一個優點是,它可以獨立更新,與調用它的函數毫不影響。

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