一攻人、預(yù)處理的由來:?
在C++的歷史發(fā)展中,有很多的語言特征(特別是語言的晦澀之處)來自于C語言悬槽,預(yù)處理就是其中的一個怀吻。C++從C語言那里把C語言預(yù)處理器繼承過來(C語言預(yù)處理器,被Bjarne博士簡稱為Cpp初婆,不知道是不是C Program Preprocessor的簡稱)蓬坡。
二、常見的預(yù)處理功能:?
預(yù)處理器的主要作用就是:??? 把通過預(yù)處理的內(nèi)建功能對一個資源進行等價替換磅叛,最常見的預(yù)處理有: 文件包含屑咳,條件編譯、布局控制和宏替換4種弊琴。?
文件包含:??? #include 是一種最為常見的預(yù)處理兆龙,主要是做為文件的引用組合源程序正文。?
條件編譯:??? #if,#ifndef,#ifdef,#endif,#undef等也是比較常見的預(yù)處理敲董,主要是進行編譯時進行有選擇的挑選紫皇,注釋掉一些指定的代碼慰安,以達到版本控制、防止對文件重復(fù)包含的功能聪铺。?
布局控制:??? #progma化焕,這也是我們應(yīng)用預(yù)處理的一個重要方面,主要功能是為編譯程序提供非常規(guī)的控制流信息铃剔。?
宏替換:??? #define锣杂,這是最常見的用法,它可以定義符號常量番宁、函數(shù)功能元莫、重新命名、字符串的拼接等各種功能蝶押。?
三踱蠢、預(yù)處理指令:?
預(yù)處理指令的格式如下:?
# define tokens
#符號應(yīng)該是這一行的第一個非空字符,一般我們把它放在起始位置棋电。如果指令一行放不下茎截,可以通過反斜杠“/”進行控制,例如:?
???? #define Error?/
???????????????? if(error) exit(1)???
等價于?
???? #define Error if(error) exit(1)
不過我們?yōu)榱嗣阑鹨姼峡话愣疾辉趺催@么用企锌,更常見的方式如下:?
# ifdef __BORLANDC__?
if_true<(is_convertible::value)>::?
template then::type Make;?
# else?
enum { is_named = is_named_parameter::value };?
typedef typename if_true<(is_named)>::template?
then::type Make;?
# endif?
*******************************************************************?
下面我們看一下常見的預(yù)處理指令:?
#define?????????宏定義?
#undef????????? 取消宏?
#include?????? ?文本包含?
#ifdef??????????? 如果宏被定義就進行編譯?
#ifndef???????? ?如果宏未被定義就進行編譯?
#endif?????????? 結(jié)束編譯塊的控制?
#if?????????????? ?表達式非零就對代碼進行編譯?
#else??????????? 作為其他預(yù)處理的剩余選項進行編譯?
#elif????????????? 這是一種#else和#if的組合選項?
#line?????????????改變當前的行數(shù)和文件名稱?
#error??????????? 輸出一個錯誤信息?
#pragma????????為編譯程序提供非常規(guī)的控制流信息?
*******************************************************************?
下面我們對這些預(yù)處理進行一一的說明,考慮到宏的重要性和繁瑣性于未,我們把它放到最后講撕攒。
四、文件包含指令:?
這種預(yù)處理使用方式是最為常見的烘浦,平時我們編寫程序都會用到抖坪,最常見的用法是:?
#include ?????????????file://標準庫頭文件?
#include ??????????file://舊式的標準庫頭文件?
#include "IO.h"?????????????????????file://用戶自定義的頭文件?
#include "../file.h"???????????????? file://UNIX下的父目錄下的頭文件?
#include "/usr/local/file.h"????? file://UNIX下的完整路徑?
#include "..//file.h"??????????????? file://Dos下的父目錄下的頭文件?
#include "http://usr//local//file.h"?? file://Dos下的完整路徑
這里面有2個地方要注意:?
?1、我們用還是??
我們主張使用闷叉,而不是,為什么呢擦俐?我想你可能還記得我曾經(jīng)給出過幾點理由,這里我大致的說一下:?
首先握侧,.h格式的頭文件早在98年9月份就被標準委員會拋棄了蚯瞧,我們應(yīng)該緊跟標準,以適合時代的發(fā)展品擎。?
其次埋合,iostream.h只支持窄字符集,iostream則支持窄/寬字符集孽查。?
還有饥悴,標準對iostream作了很多的改動,接口和實現(xiàn)都有了變化盲再。?
最后西设,iostream組件全部放入namespace std中,防止了名字污染答朋。?
?2贷揽、<io.h>和"io.h"的區(qū)別??
其實他們唯一的區(qū)別就是搜索路徑不同:?
對于#include?? 梦碗,編譯器從標準庫路徑開始搜索?
對于#include?? "io.h" 禽绪,編譯器從用戶的工作路徑開始搜索
五、編譯控制指令:?
這些指令的主要目的是進行編譯時進行有選擇的挑選洪规,注釋掉一些指定的代碼印屁,以達到版本控制、防止對文件重復(fù)包含的功能斩例。?
使用格式雄人,如下:?
1、?
#ifdef?? identifier
your code?
#endif?
如果identifier為一個定義了的符號念赶,your code就會被編譯础钠,否則剔除?
2、?
#ifndef identifier?
your code?
#endif?
如果identifier為一個未定義的符號叉谜,your code就會被編譯旗吁,否則剔除?
3、?
#if?? expression?
your code?
#endif?
如果expression非零停局,your code就會被編譯很钓,否則剔除?
4、?
#ifdef identifier?
your code1?
#else?
your code2?
#endif?
如果identifier為一個定義了的符號董栽,your code1就會被編譯履怯,否則your code2就會被編譯?
5、?
#if??? expressin1?
your code1?
#elif expression2?
your code2?
#else?
your code3?
#enif
如果epression1非零裆泳,就編譯your code1叹洲,否則,如果expression2非零工禾,就編譯your code2运提,否則,就編譯your code3
其他預(yù)編譯指令?
除了上面我們說的集中常用的編譯指令闻葵,還有3種不太常見的編譯指令:#line民泵、#error、#pragma槽畔,我們接下來就簡單的談一下栈妆。?
#line的語法如下:?
#line number filename?
例如:#line 30?? a.h????? 其中,文件名a.h可以省略不寫。?
這條指令可以改變當前的行號和文件名鳞尔,例如上面的這條預(yù)處理指令就可以改變當前的行號為30嬉橙,文件名是a.h。初看起來似乎沒有什么用寥假,不過市框,他還是有點用的,那就是用在編譯器的編寫中糕韧,我們知道編譯器對C++源碼編譯過程中會產(chǎn)生一些中間文件枫振,通過這條指令,可以保證文件名是固定的萤彩,不會被這些中間文件代替粪滤,有利于進行分析。?
#error語法如下:?
#error?? info?
例如:
#ifndef UNIX?
#error This software requires the UNIX OS.?
#endif?
這條指令主要是給出錯誤信息雀扶,上面的這個例子就是杖小,如果沒有在UNIX環(huán)境下,就會輸出This software requires the UNIX OS.然后誘發(fā)編譯器終止怕吴。所以總的來說窍侧,這條指令的目的就是在程序崩潰之前能夠給出一定的信息。?
至于#pragma,我們在解析#pragma指令一文中有過介紹转绷,我們在這里再補充幾句伟件,#pragma是非統(tǒng)一的,他要依靠各個編譯器生產(chǎn)者议经,例如斧账,在SUN C++編譯器中:?
// 把name和val的起始地址調(diào)整為8個字節(jié)的倍數(shù)?
#progma align 8 (name, val)?
char??? name[9];?
double val;?
file://在程序執(zhí)行開始,調(diào)用函數(shù)MyFunction?
#progma init (MyFunction)
預(yù)定義標識符?
為了處理一些有用的信息煞肾,預(yù)處理定義了一些預(yù)處理標識符咧织,雖然各種編譯器的預(yù)處理標識符不盡相同,但是他們都會處理下面的4種:?
__FILE__?? 正在編譯的文件的名字?
__LINE__?? 正在編譯的文件的行號?
__DATE__?? 編譯時刻的日期字符串籍救,例如: "25 Dec 2000"?
__TIME__?? 編譯時刻的時間字符串习绢,例如: "12:30:55"?
例如:cout<<"The file is :"<<__FILE__"<<"! The lines is:"<<__LINE__<
預(yù)處理何去何從?
在淺析C++里面的宏一文中,我們提到了如何取代#include預(yù)處理指令蝙昙,我們在這里就不再一一討論了闪萄。?
C++并沒有為#include提供替代形式,但是namespace提供了一種作用域機制奇颠,它能以某種方式支持組合败去,利用它可以改善#include的行為方式,但是我們還是無法取代#include烈拒。?
#progma應(yīng)該算是一個可有可無的預(yù)處理指令圆裕,按照C++之父Bjarne的話說广鳍,就是:“#progma被過分的經(jīng)常的用于將語言語義的變形隱藏到編譯系統(tǒng)里,或者被用于提供帶有特殊語義和笨拙語法的語言擴充吓妆∩奘保”?
對于#ifdef,我們?nèi)匀皇譄o策耿战,就算是我們利用if語句和常量表達式蛋叼,仍然不足以替代它焊傅,因為一個if語句的正文必須在語法上正確剂陡,滿足類檢查,即使他處在一個絕不會被執(zhí)行的分支里面狐胎。?
最后鸭栖,我們以Bjarne博士的話作為結(jié)尾:“最后,在許多年之后握巢,將Cpp放逐刀程序開發(fā)環(huán)境里晕鹊,與其他附加性語言工具放到一起,那里才是她應(yīng)該呆的地方暴浦〗埃”
“我是一名從事了10年開發(fā)的老程序員,最近我花了一些時間整理關(guān)于C語言歌焦、C++飞几,自己有做的材料的整合,一個完整的學(xué)習(xí)C語言独撇、C++的路線屑墨,學(xué)習(xí)材料和工具。全球最大的C/C++纷铣、編程愛好者的聚集地就在我這里<C語言C++編程學(xué)習(xí)>卵史!歡迎初學(xué)和進階中的小伙伴。希望你也能憑自己的努力搜立,成為下一個優(yōu)秀的程序員以躯。工作需要、感興趣啄踊、為了入行忧设、轉(zhuǎn)行需要學(xué)習(xí)C/C++的伙伴可以跟我一起學(xué)習(xí)!”
關(guān)注我和我的專欄社痛,帶你遨游代碼世界见转!C語言/C++進階之路 - 專題 - 簡書