1. 程式人生 > >linux 編譯指定庫、標頭檔案的路徑問題

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看一下,你會發現,庫函式都有動態和靜態兩個版本。

檢視動態庫函式工具: 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,僅僅是輸出字串。

  1. 首先,我們分別編寫兩個原始檔(fred.c和bill.c),原始檔如下:
  1. /* fred.c */
  2. #include <stdio.h>
  3. void fred(int arg) 
  4. printf(“fred: you passed %d\n”, arg); 
  5. /* bill.c */
  6. #include <stdio.h>
  7. void bill(char *arg) 
  8. printf(“bill: you passed %s\n”, arg); 
/* 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.     bill(“Hello World”); 
  4.     exit(0); 
#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。

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