(轉) C語言標頭檔、庫檔案的尋找路徑

來源:互聯網
上載者:User

標籤:target   get   記錄   val   它的   預設   選項   副檔名   root   

在程式設計中,檔案包含是很有用的。一個大的程式可以分為多個模組,由多個程式員分別編程。有        些公    用的符號常量或宏定義等可單獨組成一個檔案,在其它檔案的開頭用包含命令包含該檔案即可使        用。這樣,可避免在每個檔案開頭都去書寫那些公用量,從而節省時間,並減少出錯。

對檔案包含命令還要說明以下幾點:
1. 包含命令中的檔案名稱可以用雙引號括起來,也可以用角括弧括起來。例如以下寫法都是允許的:
    #include"stdio.h"
    #include
    但是這兩種形式是有區別的:使用角括弧表示在包含檔案目錄中去尋找(包含目錄是由使用者在設定環境時    設定的),而不在源檔案目錄去尋找;
    使用雙引號則表示首先在當前的源檔案目錄中尋找,若未找到才到包含目錄中去尋找。使用者編程時可根據    自己檔案所在的目錄來選擇某一種命令形式。
2. 一個include命令只能指定一個被包含檔案,若有多個檔案要包含,則需用多個include命令。
3. 檔案包含允許嵌套,即在一個被包含的檔案中又可以包含另一個檔案。

 

1.include<標頭檔名>和include"標頭檔名"

如:include和include"stdio.h"

前者(使用<>),來引用stdio.h檔案,是首先檢索標準路徑,看看這些檔案夾下是否有該標頭檔;如果沒有,也不會檢索當前檔案所在路徑,並將報錯。

後者(使用""),來引用stdio.h檔案,是首先檢索檔案的當前路徑;如果沒有,再檢索標準路徑,看看這些檔案夾下是否有該標頭檔。

2.linux下,上述標準路徑有:/usr/include,/usr/local/include。

3.。如,等。其中,前面的字串(如sys,net)表示標準路徑下的檔案夾名,後面的字串(如io.h,ethernet.h),表示在linux標準路徑下的各檔案夾下的標頭檔名,如sys檔案夾下的io.h檔案,即我們可以在/usr/include/sys目錄下發現io.h檔案。

linux博大精深,需要慢慢積累。

4.如果想在指定路徑下檢索標頭檔,可加選項-I。如我的/home/Desktop目錄下有個標頭檔local1.h,在編譯包含local1.h的test.c檔案時,可用:gcc test.c -o test -I /root/Desktop。 

 

 

一、討論環境
*作業系統:Redhat5/Fedora14

*編譯器:gcc 4.5.1

以下言論僅確保在以上環境中適用。別的環境,大家可以通過類比方法,得到啟示。

 


二、C語言標頭檔的尋找路徑
C語言,使用include指令,包含標頭檔,但又細分兩種形式:

1、形式一:#include “file1”
  gcc先在目前的目錄(指包含本條#include指令的源檔案所在的目錄)尋找file1,如果找不到,繼續在由-iquote選項(如果有的話)指定的目錄中尋找file1。

  例如,在檔案/usr/include/sys/stat.h中,包含指令#include “types.h”,那麼gcc先在/usr/include/sys目錄下尋找types.h檔案。嗯,在該目錄下,確實存在一個types.h的檔案。現假設我們把這個檔案移動到另一個目錄:mv /usr/include/sys/types.h /bar/foo/,我們在編譯時間,可以通過-iquote選項,在不改變stat.h的情況下,正常編譯(當然,通常不建議這樣做):

gcc -iquote /bar/foo -I/usr/include/sys *.o

2、形式二:#include 
  gcc按照以下順序尋找file2:

-Idir1 -Idir2 ... 
/usr/local/include 
libdir/gcc///include 
/usr//include 
/usr/include 
第一行中,-Idir1 -Idir2 ... 是使用者通過gcc的-I選項指定的目錄。值得一提的是,放在/usr/local/include/下的標頭檔也會被gcc自動的檢索,這與/usr/local/lib/目錄下的庫處理方式是不一樣的(gcc的連結器在運行時階段不會自動尋找該目錄下的庫檔案,下一節會提到)。

 


三、C語言庫檔案的尋找路徑
C語言庫檔案的尋找路徑,又分為兩個階段:連結階段、運行時階段。

1、連結階段(link time)
  此階段,需要告訴編譯器,在哪裡找到庫檔案?以靜態還是動態方式連結庫檔案?預設情況下使用動態方式連結,這要求存在對應的.so動態庫檔案,如果不存在,則尋找相應的.a靜態庫檔案。若在編譯時間向gcc傳入-static選項,則使用靜態方式連結,這要求所有庫檔案都必須有對應的*.a靜態庫。

  那麼,是否可以令某些庫使用動態連結,另一些庫使用靜態連結?不太確定,請參考ld的使用手冊,我沒有這樣用過。

  切入正題,在連結階段,gcc編譯器如何尋找庫檔案呢(linker本身並沒有預設的尋找路徑,這些尋找路徑是由gcc傳遞給linker的)?大家可以在編譯時間,向gcc加入-v選項來觀察它向linker傳遞的庫檔案尋找路徑(觀察LIBRARY_PATH變數的值),通常尋找路徑如下:

任何由-rpath-link或-rpath選項指定的目錄 
LD_RUN_PATH(如果沒有找到-rpath或-rpath-link選項) 
-Ldir1 -Ldir2 ... 
/usr/lib/gcc/// 
/usr/lib/ 
第一行-rpath-link與-rpath選項的區別在於,-rpath選項指定的目錄被寫入程式碼到可執行檔中,-rpath-link選項指定的目錄只在連結階段生效。由於這兩個選項都是連結器ld的選項,如何從gcc中向ld傳遞這兩個選項?方法如下(更從細節參考gcc的-Wl選項):

gcc -Wl, -rpath, /usr/local/lib

這相當於向ld向傳遞了如下參數:

ld -rpath /usr/local/lib

第二行,如果沒有設定-rpath或-rpath-link選項,則尋找LD_RUN_PATH環境變數指定的目錄,並把它當作-rpath選項來處理。第三行-Ldir1 -Ldir2 ...,是我們通過gcc的-L選項向其指定的庫檔案尋找路徑,尋找順序按照我們傳遞的-L參數從左至右進行搜尋;第四行屬於gcc自己的庫目錄;第五行/usr/lib/是Linux系統預設的系統庫檔案的目錄。第四、第五行,都是gcc自動向linker傳遞的尋找目錄。例如我現在的機器上,使用gcc -v可以看到LIBRARY_PATH變數值為:

LIBRARY_PATH=/usr/lib/gcc/i686-redhat-linux/4.5.1/:/usr/lib/

但是這並不是全部真理!根據我自己的測試,我發現gcc會把/usr/local/lib/目錄也作為連結階段的尋找路徑,這正是問題的根源——我們在連結過程中,使用到了/usr/local/lib/裡面的一些庫檔案,但在運行時階段,卻說找不到該庫檔案。

2、運行時階段(runtime)
  僅當可執行程式採用動態方式連結庫檔案時,才會存在執行階段程式庫檔案的尋找問題。對於這種可執行程式,它本身只是記錄動態庫的名稱。所以在運行該程式時,作業系統的載入程式(ld.so)需要根據庫的名稱,在必要時載入庫檔案到記憶體中。

  在linux中,在運行時階段,動態庫(又叫共用庫)的尋找路徑如下:

-rpath選項指定的目錄(已被寫入程式碼到可執行檔中) 
LD_LIBRARY_PATH 
/lib或/usr/lib 
系統預設的尋找路徑 
我們可以通過readelf查看被寫入程式碼到可執行檔中的rpath:

$ readelf -d <可執行檔名>                #Display the dynamic section (if present)

LD_LIBRARY_PATH則沒有這個問題,但是通常我們不建議使用這個環境變數,因為修改這個變數意味著影響所有依賴於這個環境變數的程式(如果非要使用,請把這個環境變數寫在啟動指令碼中,並且讓它隻影響指令碼中的程式)。

  那麼系統預設的尋找路徑又是怎樣的?在Redhat5/Fedora14中,ld.so通過讀取/etc/ld.so.cache檔案來尋找庫檔案的位置,如果沒有找到則繼續從/etc/ld.so.conf檔案中指定的目錄尋找。這個ld.so.cache檔案相當於一個key-value的資料庫,key就是動態庫的名稱,value就是這些庫的存放路徑。

  那麼/etc/ld.so.cache檔案是怎麼產生的呢?這就要談到ldconfig這個工具程式了。ldconfig是動態連結程式庫的組態工具,使用它可以更新/etc/ld.so.cache檔案,也可以查看這個檔案中的key-value資訊(使用ldconfig -p),ldconfig的使用細節,請參考它的使用手冊。總結一下系統預設的尋找路徑:

/etc/ld.so.cache 
/etc/ld.so.conf檔案中指定的目錄 
四、參考資料
man ld 
man ldconfig 
http://gcc.gnu.org/onlinedocs/cpp/Search-Path.html 
http://www.eyrie.org/~eagle/notes/rpath.html 

 

 

 

本文介紹在linux中標頭檔的搜尋路徑,也就是說你通過include指定的標頭檔,linux下的gcc編譯器它是怎麼找到它的呢。在此之前,先瞭解一個基本概念。

    標頭檔是一種文字檔,使用文字編輯器將代碼編寫好之後,以副檔名.h儲存就行了。標頭檔中一般放一些重複使用的代碼,例如函式宣告、變數聲明、常數定義、宏的定義等等。當使用#include語句將標頭檔引用時,相當於將標頭檔中所有內容,複製到#include處。#include有兩種寫法形式,分別是:

#include <> : 直接到系統指定的某些目錄中去找某些標頭檔。

#include “” : 先到源檔案所在檔案夾去找,然後再到系統指定的某些目錄中去找某些標頭檔。

    

    #include檔案可能會帶來一個問題就是重複應用,如a.h引用的一個函數是某種實現,而b.h引用的這個函數卻是另外一種實現,這樣在編譯的時候將會出現錯誤。所以,為了避免因為重複引用而導致的編譯錯誤,標頭檔常具有:

#ifndef    LABEL

#define    LABEL

    //代碼部分

#endif

的格式。其中LABEL為一個唯一的標號,命名規則跟變數的命名規則一樣。常根據它所在的標頭檔名來命名,例如,如果標頭檔的檔案名稱叫做hardware.h,那麼可以這樣使用:

#ifndef    __HARDWARE_H__

#define    __HARDWARE_H__

  //代碼部分

#endif

這樣寫的意思就是,如果沒有定義__HARDWARE_H__,則定義__HARDWARE_H__,並編譯下面的代碼部分,直到遇到#endif。這樣當重複引用時,由於__HARDWARE_H__已經被定義,則下面的代碼部分就不會被編譯了,這樣就避免了重複定義。

 

    一句話,標頭檔事實上只是把一些常用的命令整合在裡面,你要用到哪方面的命令就載入哪個標頭檔就可以了。

 

    gcc尋找標頭檔的路徑(按照1->2->3的順序)

    1. 在gcc編譯源檔案的時候,通過參數-I指定標頭檔的搜尋路徑,如果指定路徑有多個路徑時,則按照指定路徑的順序搜尋標頭檔。命令形式如:“gcc -I /path/where/theheadfile/in sourcefile.c“,這裡源檔案的路徑可以是絕對路徑,也可以是相對路徑。eg:

設當前路徑為/root/test,include_test.c如果要包含標頭檔“include/include_test.h“,有兩種方法:

1) include_test.c中#include “include/include_test.h”或者#include "/root/test/include/include_test.h",然後gcc include_test.c即可

2) include_test.c中#include <include_test.h>或者#include <include_test.h>,然後gcc –I include include_test.c也可

 

    2. 通過尋找gcc的環境變數C_INCLUDE_PATH/CPLUS_INCLUDE_PATH/OBJC_INCLUDE_PATH來搜尋標頭檔位置。

 

    3. 再找內定目錄搜尋,分別是

/usr/include

/usr/local/include

/usr/lib/gcc-lib/i386-linux/2.95.2/include

最後一行是gcc程式的庫檔案地址,各個使用者的系統上可能不一樣。

    gcc在預設情況下,都會指定到/usr/include檔案夾尋找標頭檔。

    gcc還有一個參數:-nostdinc,它使編譯器不再系統預設的標頭檔目錄裡面找標頭檔,一般和-I聯合使用,明確限定標頭檔的位置。在編譯驅動模組時,由於非凡的需求必須強制GCC不搜尋系統預設路徑,也就是不搜尋/usr/include要用參數-nostdinc,還要自己用-I參數來指定核心標頭檔路徑,這個時候必須在Makefile中指定。

 

    4. 當#include使用相對路徑的時候,gcc最終會根據上面這些路徑,來最終構建出標頭檔的位置。如#include 就是包含檔案/usr/include/sys/types.h

(轉) C語言標頭檔、庫檔案的尋找路徑

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.