天天看點

GCC 編譯使用動态連結庫和靜态連結庫--及先後順序----及環境變量設定總結

閱讀目錄

  • gcc -lXXX 如何選擇靜态庫還是動态庫?
  • gcc 混合連接配接動态庫和靜态庫
  • gcc優先連結動态庫,找不到,才連結靜态庫
  • 共享庫檔案在/usr/lib/目錄下, 但是程式運作的時候還是找不到是為什麼?
  • 2 個回答
  • Linux設定和檢視環境變量的方法

來自http://blog.csdn.net/benpaobagzb/article/details/51364005

GCC 編譯使用動态連結庫和靜态連結庫

1 庫的分類

根據連結時期的不同,庫又有靜态庫和動态庫之分。

靜态庫是在連結階段被連結的(好像是廢話,但事實就是這樣),是以生成的可執行檔案就不受庫的影響了,即使庫被删除了,程式依然可以成功運作。

有别于靜态庫,動态庫的連結是在程式執行的時候被連結的。是以,即使程式編譯完,庫仍須保留在系統上,以供程式運作時調用。(TODO:連結動态庫時連結階段到底做了什麼)

2 靜态庫和動态庫的比較

連結靜态庫其實從某種意義上來說也是一種粘貼複制,隻不過它操作的對象是目标代碼而不是源碼而已。因為靜态庫被連結後庫就直接嵌入可執行檔案中了,這樣就帶來了兩個問題。

首先就是系統空間被浪費了。這是顯而易見的,想象一下,如果多個程式連結了同一個庫,則每一個生成的可執行檔案就都會有一個庫的副本,必然會浪費系統空間。

再者,人非聖賢,即使是精心調試的庫,也難免會有錯。一旦發現了庫中有bug,挽救起來就比較麻煩了。必須一一把連結該庫的程式找出來,然後重新編譯。

而動态庫的出現正彌補了靜态庫的以上弊端。因為動态庫是在程式運作時被連結的,是以磁盤上隻須保留一份副本,是以節約了磁盤空間。如果發現了bug或要更新也很簡單,隻要用新的庫把原來的替換掉就行了。

那麼,是不是靜态庫就一無是處了呢?

答曰:非也非也。不是有句話麼:存在即是合理。靜态庫既然沒有湮沒在滔滔的曆史長河中,就必然有它的用武之地。想象一下這樣的情況:如果你用libpcap庫編了一個程式,要給被人運作,而他的系統上沒有裝pcap庫,該怎麼解決呢?最簡單的辦法就是編譯該程式時把所有要連結的庫都連結它們的靜态庫,這樣,就可以在别人的系統上直接運作該程式了。

所謂有得必有失,正因為動态庫在程式運作時被連結,故程式的運作速度和連結靜态庫的版本相比必然會打折扣。然而瑕不掩瑜,動态庫的不足相對于它帶來的好處在現今硬體下簡直是微不足道的,是以連結程式在連結時一般是優先連結動态庫的,除非用-static參數指定連結靜态庫。

動态連結庫

1. 建立動态連結庫 

  1. #include<stdio.h>  
  2. void hello()  
  3. {  
  4.         printf("hello world/n");  
  5. }

用指令gcc -shared hello.c -o libhello.so編譯為動态庫。可以看到,目前目錄下多了一個檔案libhello.so。

 2. 再編輯一個測試檔案test.c,内容如下

  1. #include<stdio.h>  
  2. int main()  
  3. {  
  4.         printf("call hello()");  
  5.         hello();  
  6. }  

編譯 gcc test.c -lhello

-l 選項告訴編譯器要使用hello這個庫。奇怪的地方是動态庫的名字是libhello.so,這裡卻使用hello.

但這樣還不行,編譯會出錯。

In function `main':

test.c:(.text+0x1d): undefined reference to `hello'

collect2: ld returned 1 exit status

這是因為hello這個庫在我們自己的路徑中,編譯器找不到。

需要使用-L選項,告訴hello庫的位置

gcc test.c -lhello -L. -o test

-L .告訴編譯器在目前目錄中查找庫檔案

3. 編譯成功後執行./test, 仍然出錯

說找不到庫

有兩種方法:

一、可以把目前路徑加入 /etc/ld.so.conf中然後運作ldconfig,或者以目前路徑為參數運作ldconfig(要有root權限才行)。

二、把目前路徑加入環境變量LD_LIBRARY_PATH中

 當然,如果你覺得不會引起混亂的話,可以直接把該庫拷入/lib,/usr/lib/等位置(無可避免,這樣做也要有權限),這樣連結器和加載器就都可以準确的找到該庫了。

我們采用第二種方法:

 export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH

 這樣,再執行就成功了。

下面再講講靜态連結庫

仍使用剛才的hello.c和test.c。

1. gcc -c hello.c 注意這裡沒有使用-shared選項

2. 把目标檔案歸檔    ar -r libhello.a hello.o

    程式 ar 配合參數 -r 建立一個新庫 libhello.a 并将指令行中列出的對象檔案插入。采用這種方法,如果庫不存在的話,參數 -r 将建立一個新的庫,而如果庫存在的話,将用新的子產品替換原來的子產品。

3. 在程式中連結靜态庫

         gcc test.c -lhello -L. -static -o hello.static 

或者   gcc test.c libhello.a -L. -o hello.static

生成的hello.static就不再依賴libhello.a了

file和ldd指令

file程式是用來判斷檔案類型的,在file指令下,所有檔案都會原形畢露的。

順便說一個技巧。有時在 windows下用浏覽器下載下傳tar.gz或tar.bz2檔案,字尾名會變成奇怪的tar.tar,到Linux有些新手就不知怎麼解壓了。但 Linux下的檔案類型并不受檔案字尾名的影響,是以我們可以先用指令file xxx.tar.tar看一下檔案類型,然後用tar加适當的參數解壓。

另外,還可以借助程式ldd實用程式來判斷。

ldd是用來列印目标程式(由指令行參數指定)所連結的所有動态庫的資訊的,如果目标程式沒有連結動态庫,則列印“not a dynamic executable”,ldd的用法請參考manpage。

gcc中關于靜态庫和動态庫使用    1,如何生成靜态庫 靜态庫隻是一堆object對象的集合,使用ar指令可以将.o檔案打包成.a靜态庫。 假設gcc已經生成了a.o, b.o, c.o,使用下面的指令即可生成libmylib.a #ar rcs libmylib.a a.o b.o c.o   2,如何生成動态庫 動态庫的生成由gcc直接生成。 假設a.c, b.c兩個檔案,通過下面的指令可生成libmylib.so #gcc a.c b.c -o libmylib.so --shared   3,如何使用庫 gcc中關于庫的參數有: -L  指定搜尋庫的目錄       如指定目前目錄 gcc -L . -l    指定要連結的庫的名稱       加入庫的名稱是libmylib.a,則gcc -l mylib,即去頭去尾。 --static  組織在連結時使用動态庫 --shared 生成動态庫 --static-libgcc  連結靜态libgcc庫 --shared-libgcc 連結動态libgcc庫   可見對動态庫和靜态庫的使用方法是一樣的,同一個庫如果同時存在動态庫和靜态庫,優先連結動态庫,除非使用--static強制使用靜态庫。

回到頂部

gcc -lXXX 如何選擇靜态庫還是動态庫?

庫标準路徑下存在libABC.a和libABC.so使用gcc -lABC如何選擇連接配接靜态連接配接庫或者動态連接配接庫?通過--hare --static選項?   答:如果在同一路徑下面,并且兩種庫同名,這樣會選擇動态庫。

回到頂部

gcc 混合連接配接動态庫和靜态庫

 問:gcc 同時連接配接 靜态庫和動态庫現在有 libmy.a & libmy.so兩個庫,其中的函數供main.cc調用要在可執行檔案中同時連接配接這兩個庫gcc -g -lstdc++ -g -L. -lmy -l ./libmy.a -o test.exe main.cc // 報找不到libmy.a,可是在目前目錄下已經有這個檔案了gcc -g -lstdc++ -g -L. -l libmy.so -l ./libmy.a -o test.exe main.cc // 報找不到libmy.so,在目前目錄下也有這個檔案用了 -static 選線,則報動态庫中的函數沒定義請問大家有什麼招不?感激   答:記得靜态庫混合動态庫要加特殊指令的,你可以試試這樣:gcc -g -lstdc++ -g -WI,-Bdynamic -L. -lmy -WI,-Bstatic -L. -lmy -o test.exe main.cc   回到頂部

gcc優先連結動态庫,找不到,才連結靜态庫

gcc連結時 -l 參數可否連接配接動态庫?

g++編譯時, 用-labc 選項後, 編譯器會自動按照命名規則去搜尋 libabc.a 庫檔案, 但如果我想使用動态連結庫連結應如何指定參數?

優先連結共享庫。共享庫找不到,才連結靜态庫。

可是如果我在 -l 選項後添加共享庫, 則連結時報告庫檔案不存在, 共享庫和靜态庫的命名規則是否一樣?

例如我有一個名為 libabc.so, 我應采用什麼格式的選項才可以在gcc指令中找到它?

使用 -l 選項指定靜态庫和動态庫的格式都是一樣的,如果庫檔案名為libabc.so,那麼就用 -labc 即可。

連結時會去搜尋這個庫檔案,如果不是系統庫,那麼你需要告訴連結器它的路徑。有兩種方法:一種是在參數中用 -L 選項指定庫檔案搜尋路徑,可以并列多個。例如:gcc -L /home/ddd -L/home/ddd/lib。 另一種方法是在環境變量中設定LD_LIBRARY_PATH 包含有你的動态庫檔案所在的路徑。這個環境變量用在Sorlarise和Linux,如果你是在HP-UX下,是SHLIB_PATH。

  回到頂部

共享庫檔案在/usr/lib/目錄下, 但是程式運作的時候還是找不到是為什麼?

已經嘗試過

ldconfig

LD_LIBRARY_PATH="/usr/lib" && export LD_LIBRARY_PATH

但是都沒用

現在通過google找到了一種解決方法,就是

sudo apt-get install libcr-dev

,執行之後程式就正常運作了. 但是查找共享庫

libcr.so.0

的結果還是

不知道這中間發生了什麼? 請解釋一下

回到頂部

2 個回答

執行

locate

前先

sudo updatedb

一下。。

============

你試試把

LIBRARY_PATH

也設為那個目錄,然後重新

make

一下。

export LIBRARY_PATH=/usr/lib:$LIBRARY_PATH

           

回到頂部

Linux設定和檢視環境變量的方法

linux 檢視環境變量與設定環境變量在使用過程中很常見,本文整理了一些常用的與環境變量相關的指令,感興趣的朋友可以參考下希望對你有所幫助  

1. 顯示環境變量HOME 
$ echo $HOME 
/home/redbooks 

2. 設定一個新的環境變量hello 
$ export HELLO="Hello!" 
$ echo $HELLO 
Hello! 

3. 使用env指令顯示所有的環境變量 
$ env 
HOSTNAME=redbooks.safe.org 
PVM_RSH=/usr/bin/rsh 
Shell=/bin/bash 
TERM=xterm 
HISTSIZE=1000 
... 

4. 使用set指令顯示所有本地定義的Shell變量 
$ set 
BASH=/bin/bash 
BASH_VERSINFO=([0]="2"[1]="05b"[2]="0"[3]="1"[4]="release"[5]="i386-redhat-linux-gnu") 
BASH_VERSION='2.05b.0(1)-release' 
COLORS=/etc/DIR_COLORS.xterm 
COLUMNS=80 
DIRSTACK=() 
DISPLAY=:0.0 
... 

5. 使用unset指令來清除環境變量 
set可以設定某個環境變量的值。清除環境變量的值用unset指令。如果未指定值,則該變量值将被設為NULL。示例如下: 
$ export TEST="Test..." #增加一個環境變量TEST 
$ env|grep TEST #此指令有輸入,證明環境變量TEST已經存在了 
TEST=Test... 
$ unset $TEST #删除環境變量TEST 
$ env|grep TEST #此指令沒有輸出,證明環境變量TEST已經存在了 

6. 使用readonly指令設定隻讀變量 
如果使用了readonly指令的話,變量就不可以被修改或清除了。示例如下: 
$ export TEST="Test..." #增加一個環境變量TEST 
$ readonly TEST #将環境變量TEST設為隻讀 
$ unset TEST #會發現此變量不能被删除 
-bash: unset: TEST: cannot unset: readonly variable 
$ TEST="New" #會發現此也變量不能被修改 
-bash: TEST: readonly variable 
環境變量的設定位于/etc/profile檔案 
如果需要增加新的環境變量可以添加下屬行 
export path=$path:/path1:/path2:/pahtN 
----------------------------------------------------------------------------------------------------------------------- 
1.Linux的變量種類 
按變量的生存周期來劃分,Linux變量可分為兩類: 
1.1 永久的:需要修改配置檔案,變量永久生效。 
1.2 臨時的:使用export指令聲明即可,變量在關閉shell時失效。 

2.設定變量的三種方法 
2.1 在/etc/profile檔案中添加變量【對所有使用者生效(永久的)】 
用VI在檔案/etc/profile檔案中增加變量,該變量将會對Linux下所有使用者有效,并且是“永久的”。 
例如:編輯/etc/profile檔案,添加CLASSPATH變量 
# vi /etc/profile 
export CLASSPATH=./JAVA_HOME/lib;$JAVA_HOME/jre/lib 
注:修改檔案後要想馬上生效還要運作# source /etc/profile不然隻能在下次重進此使用者時生效。 
2.2 在使用者目錄下的.bash_profile檔案中增加變量【對單一使用者生效(永久的)】 
用VI在使用者目錄下的.bash_profile檔案中增加變量,改變量僅會對目前使用者有效,并且是“永久的”。 
例如:編輯guok使用者目錄(/home/guok)下的.bash_profile 
$ vi /home/guok/.bash.profile 
添加如下内容: 
export CLASSPATH=./JAVA_HOME/lib;$JAVA_HOME/jre/lib 
注:修改檔案後要想馬上生效還要運作$ source /home/guok/.bash_profile不然隻能在下次重進此使用者時生效。 
2.3 直接運作export指令定義變量【隻對目前shell(BASH)有效(臨時的)】 
在shell的指令行下直接使用[export 變量名=變量值] 定義變量,該變量隻在目前的shell(BASH)或其子shell(BASH)下是有效的,shell關閉了,變量也就失效了,再打開新shell時就沒有這個變量,需要使用的話還需要重新定義。 

3.環境變量的檢視 
3.1 使用echo指令檢視單個環境變量。例如: 
echo $PATH 
3.2 使用env檢視所有環境變量。例如: 
env 
3.3 使用set檢視所有本地定義的環境變量。 
unset可以删除指定的環境變量。 

4.常用的環境變量 
PATH 決定了shell将到哪些目錄中尋找指令或程式 
HOME 目前使用者主目錄 
HISTSIZE 曆史記錄數 
LOGNAME 目前使用者的登入名 
HOSTNAME 指主機的名稱 
SHELL 目前使用者Shell類型 
LANGUGE  語言相關的環境變量,多語言可以修改此環境變量 
MAIL 目前使用者的郵件存放目錄 
PS1 基本提示符,對于root使用者是#,對于普通使用者是$