頭文件沒啥好說的,無非就是" "和< >的區別,這估計只要是學過C/C++的人都明白。現在的編譯器對頭文件的包含順序沒有要求,但老的C實現則不一樣。當然,我們現在無需關心頭文件順序了。
我們為啥要包含頭文件呢?頭文件裡面有定義嘛,使用任何函數之前都必須定義該函數。所以我們並不強求包含頭文件,只要你自己在庫函數使用之前定義該函數,有些編譯器還會自動的添加標准庫定義。但是吶,我們還是推薦將頭文件添加上去,頭文件裡面有時候還會包含類型定義、常量定義、宏定義。
在linux環境下,gcc會在以下路徑中查找頭文件:/usr/local/include;libdir/gcc/target/version/include;/usr/target/include;/usr/include。C++程序還會查找libdir/../include/c++/version。target是一個標准名指的是你的GCC編譯的目錄。version則是版本號了。
Gcc可以使用-Idir參數指定頭文件目錄。-nostdinc選項則會阻止GCC搜尋某個頭文件目錄。-nostdinc在編譯系統內核的時候會很有用,因為系統內核不使用標准C庫,內核使用的是源碼裡面自帶的庫文件。注意:-nostdinc對-Idir無效。
我們可以在GCC -I選項參數的任意位置放置-I-標志,在-I-標志之前的目錄我們只查找由引號標記的頭文件,-I-標記之後的目錄則查找所有頭文件。你要是想指定查找由引號標記的頭文件目錄,我們不推薦使用-I-標記,而是使用用-iquote選項。
如果很不幸,你有一個目錄叫"-"。這種情況下你得使用-I./- 和-I-區別開來。
為了防止頭文件被包含多次,你需要使用宏的條件語句:
/*File foo. */ #ifndef FILE_FOO_SEEN #define FILE_FOO_SEEN /* *the entire file */ #endif /*!FILE_FOO_SEEN*/
這是一種很常見的結構被稱為wrapper #ifndef
針對C++的優化:C++會記住wrapper #ifndef的布爾狀態,如果第二次重復包含同一個頭文件,C++會直接忽略該文件(直接不掃描)。wrapper #ifndef之外的注釋並不會影響這種優化。
一般在系統頭文件中像FILE_FOO_SEEN這種宏名以“__”開頭,所以在個人頭文件中建議使用“_”開頭,以防沖突。
C++中我們有兩種方式防止頭文件被重復包含,但我們都不推薦使用。
一、#import
#import實際上來自於Objective-C的標准做法,是#include的一個變種。#import包含頭文件,但最多只包含一次。我們不推薦的理由是:#import放權給了用戶,用戶必須知道一個頭文件必須只包含一次。但我們的宗旨是把這一任務交給頭文件來完成。
二、#pragma once
#pragma once 指令使用在頭文件裡面,符合我們的宗旨。但是並不是所有的預處理器都能識別該指令。如果考慮可移植性,該指令顯然是要移除的。
在預處理過程中,預處理器會告訴編譯器標記符號的位置,實際上就是哪個文件哪一行。編譯出錯的時候編譯器往往會有提示,告訴你錯誤可能在那個文件哪一行。這是通過語法解析器在解析代碼的時候自動插入的$line 宏
在glibc中有的函數可能只是一個宏定義,也可能是一個實實在在的函數。這對我們的程序沒什麼影響。使用宏定義函數的理由是:可以產生內聯擴展,這要比函數調用快的多,缺點是不易於debug(原因是編譯器給出的行號不是宏展開的地方,而是宏定義的地方)。基於宏定義函數的缺點,你可能想避免使用宏定義函數。你有兩種方式來避免:
1、給函數名添加括號
2、使用#undef預處理指令
比如abs函數,既有宏定義也有函數實現。
#include <stdlib.h> abs(i); //根據編譯器選擇可能是宏,也可能是函數調用 (abs)(i); //一定是函數調用 #undef abs abs(i); //一定是函數調用