1. 程式人生 > >Linux C/C++語言標頭檔案、庫檔案的查詢路徑

Linux C/C++語言標頭檔案、庫檔案的查詢路徑

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

對檔案包含命令還要說明以下幾點:
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的使用細節,請參考它的使用手冊。總結一下系統預設的查詢路徑:

本文介紹在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 ,然後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

相關推薦

Linux C/C++語言檔案檔案查詢路徑

在程式設計中,檔案包含是很有用的。一個大的程式可以分為多個模組,由多個程式設計師分別程式設計。有        些公    用的符號常量或巨集定義等可單獨組成一個檔案,在其它檔案的開頭用包含命令包含該檔案即可使        用。這樣,可避免在每個檔案開頭都去書寫那些公

Linux中的原始檔目標檔案檔案

在說明Linux的.a、.so和.o檔案關係之前,先來看看windows下obj,lib,dll,exe的關係   windows下obj,lib,dll,exe的關係     lib是和dll對應的。lib是靜態連結庫的庫檔案,dll是動

linuxC語言檔案詳解

linux中C程式標頭檔案的種類 一類:#include<stdio.h> stdio.h檔案就在/usr/include目錄下 二類:#include<arpa/inet.h> arpa/是/usr/include目錄下的子目錄,inet.h其實是/usr/include

C語言檔案stdlib.h裡面有什麼函式

主要含有的內容方向: A.字串轉換 B. 隨機數 C.記憶體管理 D.與環境的介面 E.查詢與排序 F.整數運算 G.多位元組字元 … 友情連結: (1)關於 stdio.h 和 stdlib.h 包含的函式 https://blogG…csdn.net/weixin_42513339/

LinuxC/C++編譯新增檔案路徑方式

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

c語言檔案以及make注意事項

c語言標頭檔案以及make注意事項 標頭檔案說明:自己定義的標頭檔案和專案檔案放在一起,注意使用""而不是使用<>,系統的標頭檔案才使用<> 當main函式要呼叫其他函式的時候在編譯的時候需要將其他的c檔案也一起編譯 gcc main.c test.c   make

C語言檔案的定義

每個C++/C程式通常由標頭檔案(header files)和定義檔案(definition files)組成。標頭檔案作為一種包含功能函式、資料介面宣告的載體檔案,主要用於儲存程式的宣告(declaration),而定義檔案用於儲存程式的實現 (implementation)。 .c就是你寫的程式

ICTCLAS2016 linux C++版本 使用教程 Eclipse下開發C/C++程式之檔案檔案引用

歡迎使用NLPIR/ICTCLAS2014分詞系統 NLPIR分詞系統前身為2000年釋出的ICTCLAS詞法分析系統,從2009年開始,為了和以前工作進行大的區隔,並推廣NLPIR自然語言處理與資訊檢索共享平臺,調整命名為NLPIR分詞系統。張華平博士先後傾力打造十餘

C++ 基礎(五)使用vs2015封裝c++生成.dll檔案.lib檔案.h檔案後,給另一個工程使用:使用前,需配置檔案(.h)靜態(.lib)和 動態(.dll )專案屬性

一、簡介 我是一個認真的人,要麼不寫,要麼我就把步驟、截圖、程式碼,一 一 附上,方便大家參考學習。 1、為什麼要生成DLL和lib?      就是為了給VS工程呼叫(比如C#呼叫、C++工程)。 2、C#為什麼要呼叫DLL和lib? C++寫的程

C語言檔案作用及寫法

標頭檔案幾個好處: 1,標頭檔案可以定義所用的函式列表,方便查閱你可以呼叫的函式; 2,標頭檔案可以定義很多巨集定義,就是一些全域性靜態變數的定義,在這樣的情況下,只要修改標頭檔案的內容,程式 就可以做相應的修改,不用親自跑到繁瑣的程式碼內去搜索。 3,標頭檔案只是宣告,不佔記憶體空間,要知道

C語言檔案正確寫法

目錄 一般寫法 自動生成 輸入 執行結果 參考連結 一般寫法 例如這樣有一根file.h標頭檔案,一般寫法如下 //file.h //條件編譯 #ifndef _FILE_H_ //如果沒有

Linux C程式設計——常用檔案

1 資料型別 1.1 stdint.h uint8_t uint16_t uint32_t 1.2 stdbool.h     C語言中是沒有bool型別的(C++中有),若要使用此型別,需要包含標

MAC環境下 VS Code中C語言檔案匯入錯誤

今天重新拾起了C語言,下載了vs code和CLion, 然後發現vs code寫好程式碼之後標頭檔案下方出現波浪線表示錯誤,百思不得其解。 然後看網上解答,找到了答案,好像是因為需要路徑設定 #include errors detected. Please update your

C語言檔案詳解

1. GCC編譯時問題 在由多個C語言檔案組成的程式中,對於標頭檔案的管理非常重要。標頭檔案提供給使用者一個介面,使用者從中可以知道可以呼叫哪些函式,實現哪些功能,而不必去管這些功能的具體實現。因此,寫好標頭檔案是非常關鍵的。在標頭檔案中,最常見的問題是"標頭檔案的重複包含"。其意思是同一標頭

C語言檔案裡我們如何應用#ifndef

舉個例子 我在led.h 標頭檔案裡這麼寫 #ifndef __LED_H #define __LED_H ....... ...... #endif 1 這樣寫是如何完成防止重複定義功能的 2 __LED_H 這個巨集定義我可以隨便命名嗎,還是這個#define後面的名

c語言檔案的作用以及寫法

標頭檔案幾個好處: 1,標頭檔案可以定義所用的函式列表,方便查閱你可以呼叫的函式; 2,標頭檔案可以定義很多巨集定義,就是一些全域性靜態變數的定義,在這樣的情況下,只要修改標頭檔案的內容,程式 就可以做相應的修改,不用親自跑到繁瑣的程式碼內去搜索。 3,標頭檔案只是

C語言檔案定義全域性變數問題

正確的作法是在c原始檔中定義一個全域性變數,在標頭檔案中加入全域性變數的宣告,在外部檔案呼叫的時候,包含其標頭檔案,加入全域性變數的宣告(不加也是可以的,最好加上)。 //1.c int a;

C語言檔案之float.h

float.h中的符號常量 float.h與limits.h一樣是定義邊界值的,float.h定義的是浮點數的邊界值 double DBL_DIG            double小數點後面精確的位數 DBL_EPSILON        小的正數,double的0跨

c語言檔案time.h

#include <stdio.h> #include <time.h> void main() { time_t sec; //typedef long time_t struct tm * curTime; sec = ti

C語言檔案包含和編寫的幾個基本規則

總想著把所有的標頭檔案都塞到一個頭檔案裡邊,然後,所有.c檔案只包含這一個混雜的標頭檔案就行了,也不用注意太多,, 但是這樣好像不行,摘抄一篇文章備份一下,覺得寫的不錯, 尊重原創,原文連結:http://blog.csdn.net/ison81/article/det