C/C++預先處理過程與語句總結zz

來源:互聯網
上載者:User

自:  http://www.cnscn.org

 

1)預先處理


  根據已放置在檔案中的預先處理指令來修改源檔案的內容


  前置處理器會分析/執行所有的前置處理器指令,然後刪除他們

,得到一個僅包含C++語句的轉換單元
  預先處理指令以#號開頭

  常用的預先處理指令:
  #include       包含標頭檔

  #if               條件
  #else          否則
  #elif            否則如果
  #endif         結束條件

  #ifdef  或 #if defined        如果定義了一個符號, 就執行操作
  #ifndef 或 #if !defined      如果沒有定義一個符號, 就指執行操作

  #define        定義一個符號
  #undef         刪除一個符號

  #line            重新定義當前行號和檔案名稱

  #error            輸出編譯錯誤訊息

, 停止編譯

  #pragma        提供機器

專用的特性,同時保證與C++的完全相容

2)#include  在
程式

中包含標頭檔


 標頭檔通常以.h結尾,其內容

可使用#include前置處理器指令包含到程式

中
 標頭檔中一般包含: 函數原型與全域變數

  形式常有下面兩種
  #include <iostream>
  #include "myheader.h"

  前者<>用來引用標準庫標頭檔,後者""常用來引用自訂的標頭檔
  前者<>編譯器只搜尋包含標準庫標頭檔的預設目錄

,後者首先搜尋正在編譯的源檔案所在的目錄

,找不到時再搜尋包含標準庫標頭檔的預設目錄

.
  如果把標頭檔放在其他目錄

下,為了尋找到它,必須在雙引號中指定從源檔案到標頭檔的完整路徑

3)#define  定義符號、宏

1>符號


  #define PI 3.1415925  定義符號PI為3.1415925
  #define PI      取消PI的值

  這裡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)符號名不能限制在一個命名空間

中

2>#undef 刪除#define定義的符號


  #define PI 3.14159265
  ... //之間所有的PI都可以被替換為3.14159265

  #undef PI
  之後不再有PI這個標識符

3>定義宏


  #define Print(Var) count<<(Var)<<endl
  用宏名中的參數帶入語句中的參數
  宏後面沒有“;”號
  Print(Var)中的Print和(之間不能有空格,否則(就會被解釋為置換字串的一部分

  #define Print(Var, digits)  count << setw(digits) << (Var) << endl
  調用
  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;
  }

  調用
  Print(ival, 15);

  使用宏時應注意的易引起的錯誤:
  #define max(x,y) x>y?x:y;+

  調用 result = max(myval, 99);  則換成 result = myval>99?myval:99;  這個沒有問題是正確的
 
調用 result = max(myval++, 99);  則換成 result = myval++>99?myval++:99;
這樣如果myval>99那麼myval就會遞增兩次,這種情況下()是沒什麼用的如result=max((x),y)則 result =
(myval++)>99?(myval++):99;

  再如
  #define product(m,n) m*n

  調用
  result = product(5+1,6);則替換為result = 5+1*6; 所以產生了錯誤的結果,此時應使用()把參數括起
  #define product(m,n) (m)*(n)
  則result = product(5+1,6);則替換為result = (5+1)*(6); 所以產生了錯誤的結果,此時應使用()把參數括起

結論: 一般用內嵌函式來代替前置處理器宏

技巧

:


    1)給替換變數加引號


    #define MYSTR "I love you"

    cout << MYSTR ;       //I love you而不是"I love you"
    如果
    cout << "MYSTR" ;    //則會輸出"MYSTR"而不是"I love you"

    可以這樣做
    cout << #MYSTR ;    //則會輸出 "I love you"即cout << "/"I love you/"";

    2)在宏運算式中串連幾個參數






    如
      #define join(a,b) ab 這樣不會理解為參數a的值與參數b的值的串連,即如join(10,999)不會理解為10999而是把ab理解為字串,即輸出ab
    這時可以
    #define  join(a,b)  a##b
      則join(10,999)就會輸出10999

3)邏輯前置處理器指令


 #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{...
   namespace compare{
     double max(const double* data, int size);
     double min(const double* data, int size);
   }
  #endif

  比較
  #define VERSION /
   3
  因為有分行符號/ 所以上句等價於 #define VERSION 3
  由此可以看出#define COMPARE_H與namespace compare是獨立沒有關係的兩個行

  也可以這樣用
  #if defined block1 && defined block2
  ...
  #endif

  #if CPU==PENTIUM4
    ...
  #endif

  #if LANGUAGE == ENGLISH
  #define Greeting "Good Morning."
  #elif LANGUAGE == GERMAN
  #define Greeting "Guten Tag."
  #elif LANGUAGE == FRENCH
  #define Greeting "Bonjour."
  #else
  #define Greeting "Hi."
  #endif
  std::cout<<Greeting << std::endl;

  #if VERSION == 3
  ...
  #elif VERSION == 4
  ...
  #else
  ...
  #endif

5)標準的前置處理器宏


  __LINE__     當前源檔案中的程式碼號,十進位整數
  __FILE__   源檔案的名稱,字串字面量
  __DATE__  源檔案的處理日期,字串字面量,格式mmm dd yyyy其中mmm是月份如Jan、Feb等 dd是01-31 yyyy是四位的年份
  __TIME__    源檔案的編譯時間

,也是字串字面量格式是hh:mm:ss
  __STDC__   這取決於實現方式,如果編譯器選項設定為編譯標準的C代碼,通常就定義它,否則就不定義它
  __cplusplus  在編譯C++程式

時,它就定義為199711L

  使用#line可以修改__FILE__返回的字串
  如
  #line 1000    把當前行號設定為1000
  #line 1000 "the program file"      修改__FILE__返回的字串行號改為了1000,檔案名稱改為了"the program file"
  #line __LINE__ "the program file"  修改__FILE__返回的字串行號沒變,檔案名稱改為了"the program file"

  cout << "program last complied at "<<__TIME__
       << " on " << __DATE__
       << endl;

6)#error


  在預先處理階段,如果出現了錯誤,則#error指令可以產生一個診斷訊息

,並顯示為一個編譯錯誤,同時中止編譯
  #ifndef __cplusplus
  #error "Error -  Should be C++"
  #endif

7)#pragma


 專門用於實現預先定義好的選項,其結果在編譯器說明文檔中進行了詳細的解釋。編譯器未識別出來的#pragma指令都會被忽略

8)assert()宏


  在標準庫標頭檔<cassert>中聲明
  用於在程式

中測試

一個邏輯運算式,如果邏輯運算式為false, 則assert()會終止程式

,並顯示診斷訊息


  用於在條件不滿足就會出現重大錯誤,所以應確保後面的語句不應再繼續執行,所以它的應用非常靈活
  注意: assert不是錯誤處理機制

,邏輯運算式的結果不應產生負面效果,也不應超出程式

員的控制(如找開一個檔案是否成功),程式

應提供適當的代碼來處理這種情況
 assert(expression);
  assert(expression) && assert(expression2);
  可以使用#define NDEBUG來關閉斷言機制

  #include <iostream>
  #include <cassert>
  using std::cout;
  using std::endl;

  int main()
  {
     int x=0;
     int y=0;

     cout<<endl;

     for(x=0; x<20; x++)
     {
        cout<<"x= "<<x <<" y= "<<y<<endl;
        assert(x<y); //當x>=y與x==5時,就報錯,並終止程式

的執行
     }
     return 0;
  }

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.