原創文章,轉載請註明出處,謝謝!
作者:清林,部落格名:飛空靜渡
#include
如果從純粹的text檔案來說,#include的作用就是搜尋它後面指示的檔案,並把這個檔案的內容加到當前的檔案中。一般我們編程時,都是包含一些與.h為尾碼的標頭檔,但是它可以包含任何尾碼的任何形式的text檔案的,而不僅僅是.h為尾碼的標頭檔。
#include有兩種形式,例如如下:
#include <syshead.h>
#include "userhead.h"
用角括弧表示的是包含系統的標頭檔,用雙引號包含的是使用者自己的標頭檔。
下面是使用#include時的一些規則:
1)使用<>包含的標頭檔一般會先搜尋-I選項後的路徑(即用gcc編譯時間的-I選項),之後就是標準的系統標頭檔路徑。
2)而用""號包含的標頭檔會首先搜尋當前的工作目錄,之後的搜尋路徑才是和<>號包含的標頭檔所搜尋的路徑一樣的路徑。
3)在unix系統中,一般標準的標頭檔路徑為:
/usr/local/include
/usr/lib/gcc-lib/target/version/include
/usr/target/include
/usr/include
4)一般有兩條獨立的標頭檔搜尋路徑鏈。一條是-I後面指示的路徑,另一條是系統標頭檔路徑和以-prefix, -withprefix,和-idirafter後操作的目錄。
5)如果gcc編譯的是c++的程式,那麼在搜尋上面所說的目錄前,前置處理器會首先搜尋/usr/include/g++v3目錄,v3是你的gcc中c++的版本。
6)在標頭檔中運行增加路徑名,例如:#include <sys/time.h>,那麼就會在搜尋的系統目錄的sys目錄下尋找time.h檔案。
7)一般會用斜線來作為目錄的分割符,甚至有些系統使用不同的字元作為分割符(例如反斜線)。
8)#include後面所包含的檔案名稱就是檔案名稱,例如abc*d.h這個檔案,必須就要有abc*d.h這個檔案,而不是abckkkd.h這些檔案,*不能解釋成任
何的字元的意思,而是實實在在的一個字元。
9)可以使用一個指定的名字作為#include指令後面的標頭檔,例如:
#define BOGHEADER "bog_3.h"
#include BOGHEADER
10)在#include 指令的後面,除了所包含的標頭檔和注釋外,不能包含其它的任何東西了。
11)#line指令不能改變當前的工作目錄。
12)-I-選項可以改變-I指定的搜尋目錄。
#include_next
#include_next是GNU的一個擴充,並不是標準C中的指令。
#include_next看起來有些複雜,但在這裡我將詳詳細細的說明一下,希望可以把它講的清楚,讓讀者瞭解 :)
首先,我將會說明一下這條指令的功能,然後說明一下為什麼要引人這條指令,希望能說個明白。
#include_next和#include指令一樣,也是包含一個標頭檔,它們的不同地方是包含的路徑不一樣。
#include_next的意思就是“包含指定的這個檔案所在的路徑的後面路徑的那個檔案”,聽起來是不是很坳口,我自己也覺得是這樣,但下面舉個例子說明就清楚了。
例如有個搜尋路徑鏈,在#include中,它們的搜尋順序依次是A,B,C,D和E。在B目錄中有個標頭檔叫a.h,在D目錄中也有個標頭檔叫a.h,如果在我們的原始碼中這樣寫#include <a.h>,那麼我們就會包含的是B目錄中的a.h標頭檔,如果我們這樣寫#include_next <a.h>那麼我們就會包含的是D目錄中的a.h標頭檔。#include_next <a.h>的意思按我們上面的引號包含中的解釋來說就是“在B目錄中的a.h標頭檔後面的目錄路徑(即C,D和E)中搜尋a.h標頭檔並包含進來)。#include_next <a.h>的操作會是這樣的,它將在A,B,C,D和E目錄中依次搜尋a.h標頭檔,那麼首先它會在B目錄中搜尋到a.h標頭檔,那它就會以B目錄作為分割點,搜尋B目錄後面的目錄(C,D和E),然後在這後面的目錄中搜尋a.h標頭檔,並把在這之後搜尋到的a.h標頭檔包含進來。這樣說的話大家應該清楚了吧。
還有一點是#include_next是不區分<>和""的包含形式的。
現在來說說為什麼要引人這條指令!
假如,你要建立一個新的標頭檔,而這個新的標頭檔和現在已有的標頭檔有相同的名字,而且你想用你的這個新的標頭檔,那麼你要做的就是把這個新的標頭檔放在#include指令的搜尋路徑的前面,即是在舊的標頭檔的前面新的標頭檔首先被搜尋到,這樣你就可以使用你這個新的標頭檔。但是你在另一個原始碼檔案中想使用舊的標頭檔了,那怎麼辦!有個辦法就是使用絕對路徑來搜尋,那麼就不存在這樣的問題了。問題出在,如果我們把標頭檔的位置移動了,移到了其它的目錄裡了,那我們就得在相應的源碼檔案中修改這個包含的絕對路徑,如果一個源碼檔案還好,但如果是大型工程的話,修改的地方多了就容易出問題。
又進一步說,如果你這個新的標頭檔引用了舊的標頭檔,而這個新的標頭檔如果沒有使用只編譯一次的預先處理語句包含(即#ifndef,#endif等),那麼就會陷入一個無限的遞迴包含中,這個新的標頭檔就會無限的包含自己,就會出現一個致命的錯誤。如果我們使用#include_next就會避免這樣的問題。
在標準的C中,這沒有一個辦法來解決上面的問題的,因此GNU就引人了這個指令#include_next。
下面再舉一個#include_next的例子。
假設你用-I選項指定了一個編譯包含的路徑 '-I /usr/local/include',這個路徑下面有個signal.h的標頭檔,在系統的'/usr/include'下也有個signal.h標頭檔,我們知道-I選項的路徑首先搜尋。如果我們這樣
#include <signal.h>
包含,就會包含進/usr/local/include下的signal.h標頭檔;如果是
#include_next <signal.h>,就會包含
'/usr/include'下的signal.h標頭檔。
GNU建議一般沒有其它可取代的辦法的情況下才使用#include_next的。
又一個例子,如在系統標頭檔stdio.h中,裡面有個函數(應該說是一個宏)getc,它從標準輸入中讀取一個字元。你想重新定義一個getc,並放到自己建立的stdio.h檔案中,那麼你可以這樣使用你自訂的getc。
#include_next "stdio.h"
#undef getc
#define getc(fp) ((int)'x')
更多的說明請參考GNU的官方文檔和GCC文檔。