天天看點

14、預處理宏

預處理宏

預處理器(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 &lt;iostream.h&gt;

   #deifne BookChapterVerse(b,c,v) b ## c ## v

     unsigned bcv=BookChapterVerse(5,12,43);

     cout &lt;&lt; bcv;

     return 0;

#include   在指令的位置包含一個源代碼檔案

頭檔案通常以.h結尾,其 内容可使用#include預處理器指令包含到程式中。 頭檔案中一般包含: 函數原型與全局變量

  形式常有下面兩種

  #include &lt;iostream&gt;

  #include "myheader.h"

  前者&lt;&gt;用來引用标準庫頭檔案,後者""常用來引用自定義的頭檔案

    前者&lt;&gt;編譯器隻搜尋包含标準庫頭檔案的預設目錄,後者首先搜尋正在編譯的源檔案所在的目錄,找不到時再搜尋包含标準庫頭檔案的預設目錄.

  如果把頭檔案放在其他目錄下,為了查找到它,必須在雙引号中指定從源檔案到頭檔案的完整路徑

  #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 &lt;&lt; setw(digits) &lt;&lt; (Var) &lt;&lt; endl

宏後面沒有;号

Print(Var)中的Print和(之間不能有空格,否則(就會被解釋為置換字元串的一部分調用

  Print(ival, 15);

  預處理器就會把它換成

  cout &lt;&lt; setw(15) &lt;&lt; (ival) &lt;&lt; endl;

  所有的情況下都可以使用内聯函數來代替宏,這樣可以增強類型的檢查

  template&lt;class T&gt;

  inline void Print (const T&amp; var, const int&amp; digits)

  {

      count&lt;&lt;setw(digits)&lt;&lt;var&lt;&lt;endl;

  }

  調用

邏輯預處理器指令#if  #else  #elif   #endif   #undef   #ifndef

 #if defined CALCAVERAGE 或 #ifdef CALCAVERAGE

   int count=sizeof(data)/sizeof(data[0]);

   for(int i=0; i&lt;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()宏

在标準庫頭檔案&lt;cassert&gt;中聲明

用于在程式中 測試一個邏輯表達式,如果邏輯表達式為false, 則assert()會終止程式,并顯示診斷消息

用于在條件不滿足就會出現重大錯誤,是以應確定後面的語句不應再繼續執行,是以它的應用非常靈活。

注意:assert不是錯誤處理 機制,邏輯表達式的結果不應産生負面效果,也不應超出程式員的控制(如找開一個檔案是否成功), 程式應提供适當的代碼來處理這種情況

 assert(expression);

  assert(expression) &amp;&amp; assert(expression2);

  可以使用#define NDEBUG來關閉斷言機制

參考

[1] http://www.cnblogs.com/sdywcd_coffee/archive/2010/01/06/1640653.html

繼續閱讀