預處理宏
預處理器(Preprocessor)定義了讀取源代碼、對代碼預先翻譯以及編寫供編譯器讀取的新代碼的過程。預處理先于編譯器對源代碼進行處理。
C/C++ 語言沒有内置工具在編譯時間包含其他源檔案、宏定義,或根據條件包含或排除一些代碼行的編譯時指令。預處理器提供了這些能力。雖然目前大多數編譯器内部內建了預處理器,人們還是認為預處理獨立于編譯器的過程。預處理器讀取源代碼,查找預處理指令語句和宏調用,然後翻譯源代碼,它還去掉程式中的注釋和多餘的空白。
在C++語言中預處理指令有:
指令
功能描述
#
空指令,沒有作用
#include
在指令的位置包含一個源代碼檔案
#define
定義一個宏
#undef
取消宏定義
#if
如果給定條件為真,則編譯代碼
#ifdef
如果宏被定義,則編譯代碼
#ifndef
如果宏未被定義,則編譯代碼
#elif
如果前面的#if...條件不為真而目前條件為真,則編譯代碼
#endif
終止#if....#else條件塊
#error
終止編譯并顯示錯誤資訊
#line
修改編譯器尾部用于消息報告的檔案名和行号
#pragma
功能取決于平台
"#"串化運算符
宏定義内的"#"運算符把位于其後的形參所對應的實參轉化為字元串。
例子:
#include <iostream.h>
#define Error(n) cout << "Error " << #n
int main(void)
{
Error(53);
return 0;
}
程式運作結果:Error 53
<a href="http://www.cnblogs.com/mydomain/archive/2010/09/25/1834917.html">http://www.cnblogs.com/mydomain/archive/2010/09/25/1834917.html</a>
"##"運算符
"##"運算符把多個實參連接配接起來。
#include <iostream.h>
#deifne BookChapterVerse(b,c,v) b ## c ## v
unsigned bcv=BookChapterVerse(5,12,43);
cout << bcv;
return 0;
#include 在指令的位置包含一個源代碼檔案
頭檔案通常以.h結尾,其 内容可使用#include預處理器指令包含到程式中。 頭檔案中一般包含: 函數原型與全局變量
形式常有下面兩種
#include <iostream>
#include "myheader.h"
前者<>用來引用标準庫頭檔案,後者""常用來引用自定義的頭檔案
前者<>編譯器隻搜尋包含标準庫頭檔案的預設目錄,後者首先搜尋正在編譯的源檔案所在的目錄,找不到時再搜尋包含标準庫頭檔案的預設目錄.
如果把頭檔案放在其他目錄下,為了查找到它,必須在雙引号中指定從源檔案到頭檔案的完整路徑
#define 定義符号、宏符号
#define PI 3.1415925 定義符号PI為3.1415925
#define PI 取消PI的值
或有#undef PI
3.14159265 不是一個數值,隻是一個字元串,不會進行檢查
在編譯前,預處理器會周遊代碼,在它認為置換有意義的地方,用字元串PI的定義值(3.14159265)來代替
在注釋或字元串中的PI不進行替換
在C中常以#define來定義符号常量,但在C++中最好使用const 來定義常量
#define PI 3.14159265
const long double PI=3.14159265;
兩者比較下,前者沒有類型的指定容易引起不必須的麻煩,而後者定義清楚,是以在C++中推薦使用const來定義常量
#define的缺點:
1)不支援類型檢查
2)不考慮作用域
3)符号名不能限制在一個命名空間中
用宏名中的參數帶入語句中的參數
#define Print(Var, digits) count << setw(digits) << (Var) << endl
宏後面沒有;号
Print(Var)中的Print和(之間不能有空格,否則(就會被解釋為置換字元串的一部分調用
Print(ival, 15);
預處理器就會把它換成
cout << setw(15) << (ival) << endl;
所有的情況下都可以使用内聯函數來代替宏,這樣可以增強類型的檢查
template<class T>
inline void Print (const T& var, const int& digits)
{
count<<setw(digits)<<var<<endl;
}
調用
邏輯預處理器指令#if #else #elif #endif #undef #ifndef
#if defined CALCAVERAGE 或 #ifdef CALCAVERAGE
int count=sizeof(data)/sizeof(data[0]);
for(int i=0; i<count; i++)
average += data;
average /= count;
#endif
如果已經定義符号CALCAVERAGE則把#if與#endif間的語句放在要編譯的源代碼内
防止重複引入某些頭檔案
#ifndef COMPARE_H
#define COMPARE_H
注意:這裡隻是定義一個沒有值的符号COMPARE_H, 下面的namespace compare不是COMPARE_H的 内容,這裡的定義不像是定義一個常量或宏,僅僅定義一個符号,指出此符号已定義,則就會有下面的内容
namespace compare{
double max(const double* data, int size);
double min(const double* data, int size);
}
比較
#define VERSION \
3
因為有換行符\ 是以上句等價于 #define VERSION 3
由此可以看出#define COMPARE_H與namespace compare是獨立沒有關系的兩個行。
#line
使用#line可以修改__FILE__傳回的字元串
如
#line 1000 把目前行号設定為1000
#line 1000 "the program file" 修改__FILE__傳回的字元串行号改為了1000,檔案名改為了"the program file"
#line __LINE__ "the program file" 修改__FILE__傳回的字元串行号沒變,檔案名改為了"the program file"
在預處理階段,如果出現了錯誤,則#error指令可以生成一個診斷消息,并顯示為一個編譯錯誤,同時中止編譯
#ifndef __cplusplus
#error "Error - Should be C++"
專門用于實作預先定義好的選項,其結果在編譯器說明文檔中進行了詳細的解釋。編譯器未識别出來的#pragma指令都會被忽略。
assert()宏
在标準庫頭檔案<cassert>中聲明
用于在程式中 測試一個邏輯表達式,如果邏輯表達式為false, 則assert()會終止程式,并顯示診斷消息
用于在條件不滿足就會出現重大錯誤,是以應確定後面的語句不應再繼續執行,是以它的應用非常靈活。
注意:assert不是錯誤處理 機制,邏輯表達式的結果不應産生負面效果,也不應超出程式員的控制(如找開一個檔案是否成功), 程式應提供适當的代碼來處理這種情況
assert(expression);
assert(expression) && assert(expression2);
可以使用#define NDEBUG來關閉斷言機制
參考
[1] http://www.cnblogs.com/sdywcd_coffee/archive/2010/01/06/1640653.html