640 likes | 756 Views
第七章. 前置處理器. 本章學習目標:. 使用廣泛的巨集代換指引 -#define 定義含有引數的巨集函式 利用條件式編譯指引來控制編譯流程 介紹 #include 指令,如何在專案中加入自訂標頭檔. 撰寫 C++ 程式,使用前端處理器是無法避免的程序。所以本章節介紹了前端處理器含有 # 符號的前端處理指引,包含巨集代換指引 (#define) 、條件式編譯指引和 #include 指令。. 7-1 前置處理器. 7-2-1 如何定義巨集. 7-2 巨集指令. 7-2-2 函式巨集. 7-2-3 前端處理運算子. 7-3-1 以條件式控制編譯流程.
E N D
第七章 前置處理器
本章學習目標: • 使用廣泛的巨集代換指引-#define • 定義含有引數的巨集函式 • 利用條件式編譯指引來控制編譯流程 • 介紹#include指令,如何在專案中加入自訂標頭檔 撰寫C++程式,使用前端處理器是無法避免的程序。所以本章節介紹了前端處理器含有#符號的前端處理指引,包含巨集代換指引(#define)、條件式編譯指引和#include指令。
7-1 前置處理器 7-2-1 如何定義巨集 7-2 巨集指令 7-2-2 函式巨集 7-2-3 前端處理運算子 7-3-1 以條件式控制編譯流程 7-3-2 判斷巨集是否定義 7-3 條件式巨集 7-3-3 取消巨集的定義 7-3-4 除錯指令-#error 7-4-1 引入標頭檔 7-4標頭檔的使用 7-4-2 自訂標頭檔 章節目錄
7-2-1 如何定義巨集 7-2 巨集指令 7-2-2 函式巨集 7-2-3 前端處理運算子 7-3-1 以條件式控制編譯流程 7-3-2 判斷巨集是否定義 7-3 條件式巨集 7-3-3 取消巨集的定義 7-3-4 除錯指令-#error 7-4-1 引入標頭檔 7-4標頭檔的使用 7-4-2 自訂標頭檔 章節目錄 7-1 前置處理器
7-1 前置處理器 • 在介紹前端處理器之前,讓我們先了解一下前端處理器的歷史。 • C++所使用的「前端處理器」(Preprocessor)大部份是源自於C語言 • 只是C++將許多的前端處理器以較新的程式語法取代 • 其目地就是讓新一代的C++語法更靈活、程式寫作的空間更大。
#define 巨集定義指令 指令名稱 指令說明 #include 包含檔定義指令 除錯定義指令 #error #if、#else、#endif、#elif、#ifdef、#ifndef、#undef 條件式編譯指令 C++前端處理指令 • 那麼C++中使用的前端處理器有那些呢?以下表說明:
前置處理指引 • C++的前置處理器是一個巨集處理器,當程式要編譯時會自動啟動,主要是用來處理C++程式中含有#符號開頭的敘述,稱為「前置處理指引」(Preprocessor directive)敘述。 • 前置處理指引與C++敘述的最大差別在於使用前置處理指引時必須以「#」為開頭。雖然程式中任何地方都可以使用,但是習慣把它放在主程式或其它函式前面。 • 前置處理指引的有效範圍是從指引處開始,直到程式結束為止。
章節目錄 7-1 前置處理器 7-2 巨集指令
7-2 巨集指令 • 介紹前端處理指引「巨集代換指引」(#define)。 • C++撰寫程式時,#define的使用最廣泛 • 它的作用是將程式中經常使用的常數、字串、函式以巨集名稱來取代。
7-2-1 如何定義巨集 章節目錄 7-1 前置處理器 7-2 巨集指令
巨集名稱:和一般識別字的命名規則相同,習慣上以大寫英文字母來命名。巨集名稱:和一般識別字的命名規則相同,習慣上以大寫英文字母來命名。 回傳資料型別 替代內容:要以巨集取代的內容,通常是數字或字串。編譯器碰到此指引內容時,會以前置處理器將程式中的每個巨集名稱都以「替代內容」來替換,再進行編譯。此外,替代內容並不需要利用「;」來表示結束。 函數名稱 7-2-1 如何定義巨集 • #define定義中最常用到的功能 • 就是以定義的識別符號來取代程式敘述中較常使用的字元組 • 以便讓程式設計師在編寫程式時,更了解這個字元組在程式敘述中所代表的意義及便利性 • 這樣的取代過程我們稱之為「巨集代換指引」,其語法如下:
使用define敘述 • 我們以#define定義一個名稱為「PI」巨集,替代內容是「3.14159」 • 當編譯器碰到PI時,就會以「3.14159」來取代,再執行編譯程序
執行程式:round.cpp • 瞭解#define定義巨集的用法,利用PI巨集來取代數值「3.14159」。 • 第6行宣告一個PI巨集。 • 第12行計算圓周長 • 第13行計算圓面積,此處加入pow()函式來代換原有的(半徑*半徑)。 • 第15行輸出結果時,加入setw()函式,讓輸出字元為8個字元,並向右對齊。
7-2-1 如何定義巨集 7-2-2 函式巨集 章節目錄 7-1 前置處理器 7-2 巨集指令
函式巨集 讓使用的巨集名稱像函式一樣地加入引數,編譯器會將程式中所出現的巨集識別符號以巨集的定義內容來取代並且執行它。 7-2-2 函式巨集 • #define這個前置處理指引還有另一種定義格式,就是在巨集名稱內加入引數,語法如下:
執行程式:macargu.cpp • 第3~5行宣告三個巨集,SQU計算數值平方,CUBE計算數值立方,BIG找出最大值。 • 第10~19行以do/while迴圈來處理使用者輸入的兩個數值,將number1以SQU巨集來計算平方,number2以CUBE巨集來取得立方值,以BIG來找出最大值。
7-2-1 如何定義巨集 7-2-2 函式巨集 7-2-3 前端處理運算子 章節目錄 7-1 前置處理器 7-2 巨集指令
7-2-3 前端處理運算子 • 在函式巨集中也有用到「#」及「##」運算子 • 其中「#」我們稱為字串運算子 • 「#」的功用是將#之後的引數以引號字串顯示 • 而「##」的功用則是將兩個引數連起來而已 • 在此我們直接以範例「merging.cpp」來瞭解。
程式解說:merging.cpp • 第4行利用ShowStr(x)巨集來定義「#」運算子,將引數取代成引號字串。 • 第5行定義CONBINE(x, y ,z)巨集,使用「##」運算子將引數相連的巨集。 • 第8行顯示使用巨集ShowStr(x),引數填入HELLO!的結果 • 第10行顯示使用巨集CONBINE(x, y, z) , 引數填入a、b、c的結果。
7-2-1 如何定義巨集 7-2-2 函式巨集 7-2-3 前端處理運算子 章節目錄 7-1 前置處理器 7-2 巨集指令 7-3 條件式巨集
7-3 條件式巨集 • 條件式編譯指引,顧名思義就是在編譯時,透過前置處理器,讓C++編譯器依據定義的條件進行編譯。但是,這與之前所提的判斷式不同 • 條件式編譯指引是在編譯階段便會執行並且判斷的指令 • 而一般程式碼中的條件判斷則是在程式執行時依據定義的條件進行選擇 • 相信大家對條件式編譯指引已有了概略性瞭解,我們將在後面的小節做更詳細的說明。
7-2-1 如何定義巨集 7-2-2 函式巨集 7-2-3 前端處理運算子 7-3-1 以條件式控制編譯流程 章節目錄 7-1 前置處理器 7-2 巨集指令 7-3 條件式巨集
7-3-1 以條件式控制編譯流程 • 相信大家對於if…else與endif敘述的用法及它們的關係並不陌生。這也是條件式編譯指引用來控制編譯流程的「條件式編譯」(conditional compilation),C++提供了 • #if、#elif • #else、#endif • 語法如下:
使用定義好的條件式編譯指引 • 利用定義好的條件式編譯指引,C++編譯器會依據定義的條件運算式有條件的編譯所包含的程式碼 • 假如定義的條件運算式為真,編譯器就會編譯#if之後及#else之間所包含的程式敘述1 • 如果條件運算式為否,則會去編譯#else及#endif之間的程式敘述2 • 當然與條件判斷相同的#else條件式編譯指引也可以省略,在#if及#endif中僅有一個程式敘述1。 • 透過以下面兩個簡單的範例來加深概念
程式解說:condif.cpp • 使用#if…#else…#endif條件式編譯指引來判斷成績等級。 • 第4行定義了一個常數值SCORE。 • 第7~11行為條件式編譯指引,如果SCORE大於90,定義GRADE為「表現優良」,如果不是則將GRADE顯示為「尚可」。 • 第13~16行為主程式,利用cout物件輸出SCORE和GRADE兩個常數值內容。 • 如果我們將第4行定義的SCORE更改其常數值為「95」,就會發現執行的結果就會不一樣。
使用elif • 在條件式編譯指引中還有個重要的指引就是#elif相當於條件判斷式中的else if敘述,用來編譯多條件情形 • 語法如下: • 對於條件式編譯指引#if、#else、#elif及#endif的使用有了初步瞭解後。還得提醒大家一件事,因為#if、#else、#elif及#endif的指引在編譯時就會執行判斷,所以使用時只能以常數做為條件加以判斷,一般的變數就不能在這裡使用了。
7-2-1 如何定義巨集 7-2-2 函式巨集 7-2-3 前端處理運算子 7-3-1 以條件式控制編譯流程 7-3-2 判斷巨集是否定義 章節目錄 7-1 前置處理器 7-2 巨集指令 7-3 條件式巨集
7-3-2 判斷巨集是否定義. • 條件式編譯指引中另外兩個指令為#ifdef及#ifndef。 • #ifdef指引用來直接判斷程式碼中的巨集是否已被定義了 • #ifndef則是判斷巨集是否未被定義 • 這兩個指令語法如下:
判斷巨集是否定義.. • 一般程式設計師在編寫較大的程式時,會因為程式碼的較為龐大或者是包含檔的引入,而無法確定某些巨集指令是否以被定義了,這時就可藉由這兩個條件式編譯指引做確認動作。 • 我們以部份程式碼來說明 • 這些敘述主要是說明了,如果SCORE巨集在這之前已被#define定義,則#ifdef及#endif中的程式敘述就會被編譯器編譯進去
判斷巨集是否定義… • 讓我們再看其他部份的程式碼,敘述如下: • 這些程式敘述正好與前面的敘述相反 • 它說明了倘若SCORE巨集在這之前尚未定義,則#ifndef及#endif中的程式敘述就會被編譯器編譯進去了 • 上述的部份程式碼內容,是為了瞭解#ifdef及#ifndef的用法。 • 當然,使用#ifdef及#ifndef這兩個指引時還可以和#else及#elif的指引合併使用,讓我們在程式的編寫上有更大的揮灑空間!
程式解說:conddef.cpp • 使用#ifdef…#endif條件式編譯指引來判斷AGE巨集是否已被定義。 • 第5行定義了一個AGE巨集,第7行會顯示AGE已被定義,第8行則顯示AGE未被定義。 • 第11~16行為條件式編譯指引,如果AGE確實已被定義,則會輸出第12行所定義的DISPY替換內容。如果AGE並未定義,才會輸出第15行的DISPN的替換內容。
7-2-1 如何定義巨集 7-2-2 函式巨集 7-2-3 前端處理運算子 7-3-1 以條件式控制編譯流程 7-3-2 判斷巨集是否定義 7-3-3 取消巨集的定義 章節目錄 7-1 前置處理器 7-2 巨集指令 7-3 條件式巨集
7-3-3 取消巨集的定義 • 在介紹巨集定義及判斷巨集是否定義的指引之後,其實還有一個指引跟巨集是息息相關的#undef指令,它的功能是在取消程式之前所定義的巨集,它的語法如下: • 例如,在程式中已定義了巨集AGE,當編譯器遇到指引#undef AGE後,便會認定巨集AGE已被取消,爾後的程式碼中若再出現AGE巨集,編譯器就會產生錯誤訊息來告知此巨集尚未定義。 • 延續上一個範例「conddef.cpp」來說明#undef指引。
執行程式:condef2.cpp • 先以以#undef來取消AGE巨集的定義,再利用#ifndef…#endif條件式編譯指引來判斷AGE巨集是否尚未定義。 • 第6行定義了一個AGE巨集,第8行會顯示AGE已被定義,第9行則顯示AGE未被定義。 • 第12~18行為條件式編譯指引,如果AGE確實已被定義,則會輸出第14行所定義的DISPY替換內容。 • 第20行以#undef來取消AGE巨集的定義。 • 第21~27行為條件式編譯指引,如果AGE尚未定義,則會輸出第23行所定義的DISPN替換內容。
7-2-1 如何定義巨集 7-2-2 函式巨集 7-2-3 前端處理運算子 7-3-1 以條件式控制編譯流程 7-3-2 判斷巨集是否定義 7-3-3 取消巨集的定義 7-3-4 除錯指令-#error 章節目錄 7-1 前置處理器 7-2 巨集指令 7-3 條件式巨集
7-3-4 除錯指令--#error • #error指令,可以算是前端處理器指引中比較特殊的指令,它的語法如下: • 在程式碼中宣告,主要功能是在程式編譯階段除錯用的指令 • 意思是指C++的編譯器在程式碼中看到#error的指引存在時就會立即停止編譯程式,並且在除錯視窗中顯示#error定義後的錯誤訊息 • 一般程式設計師會連同我們之前所說明的條件式編譯指令一起使用。
7-2-1 如何定義巨集 7-2-2 函式巨集 7-2-3 前端處理運算子 7-3-1 以條件式控制編譯流程 7-3-2 判斷巨集是否定義 7-3-3 取消巨集的定義 7-3-4 除錯指令-#error 章節目錄 7-1 前置處理器 7-2 巨集指令 7-3 條件式巨集 7-4標頭檔的使用
7-4 標頭檔的使用 • 程式中常用的巨集,是否能在其他的程式上使用呢?當然可以!除此之外,還包含函式庫等,都能利用#include檔案含入指令,讓編譯器在處理前,依據指定的標頭檔來引入。 • 一般來說,無論是巨集或是函式庫,大都是自已或別的程式設計師把已經寫好的程式碼加以編譯,確定程式已無任何錯誤。 • 將這些巨集或函式庫單獨儲存成附檔名為「*.h」標頭檔 • 再利用#include指令,將這些標頭檔包含進來加以使用
7-2-1 如何定義巨集 7-2-2 函式巨集 7-2-3 前端處理運算子 7-3-1 以條件式控制編譯流程 7-3-2 判斷巨集是否定義 7-3-3 取消巨集的定義 7-3-4 除錯指令-#error 7-4-1 引入標頭檔 章節目錄 7-1 前置處理器 7-2 巨集指令 7-3 條件式巨集 7-4標頭檔的使用
透過前端處理器 #include指令 引入標頭檔 有二種方式: #include<標頭檔名> #include “標頭檔名” 7-4-1 引入標頭檔
標頭檔的使用 • 這兩種指令都是指示編譯器將I/O函式庫的標頭檔包含進來提供給程式設計師使用 • 兩種指令的差別在於編譯器尋找標頭檔的所在目錄。 • #include指令後面是以「<>」角括號來包含標頭檔,是告訴編譯器要到系統指定的目錄去找尋標頭檔時。 • 而以「“”」雙引號包含的標頭檔則是會從程式預設目錄找尋,如果在預設目錄找不到,才會去系統指定目錄找尋。 • 程式設計師會利用這樣的特性來區分函式庫。 • #include指令後面的雙引號保留著與目前程式有關的標頭檔,這些標頭檔都是由程式設計師自己所編寫,故存於程式預設目錄。 • 而用角括號包含的是系統標頭檔,存放位置當然是在系統目錄了。