C++預編譯

C++提供的編譯預處理功能主要有以下三種:
  (一) 宏定義
  (二) 文件包含
  (三) 條件編譯

預處理過程掃描源代碼,對其進行初步的轉換,產生新的源代碼提供給編譯器。可見預處理過程先於編譯器對源代碼進行處理。
C語言中,並沒有任何內在的機制來完成如下一些功能:在編譯時包含其他源文件、定義宏、根據條件決定編譯時是否包含某些代碼。要完成這些工作,就需要使用預處理程序。儘管在目前絕大多數編譯器都包含了預處理程序,但通常認爲它們是獨立於編譯器的。預處理過程讀入源代碼,檢查包含預處理指令的語句和宏定義,並對源代碼進行響應的轉換。預處理過程還會刪除程序中的註釋和多餘的空白字符。
預處理指令是以#號開頭的代碼行。#號必須是該行除了任何空白字符外的第一個字符。#後是指令關鍵字,在關鍵字和#號之間允許存在任意個數的空白字符。整行語句構成了一條預處理指令,該指令將在編譯器進行編譯之前對源代碼做某些轉換。下面是部分預處理指令:
        指令             用途
         #           空指令,無任何效果
         #include    包含一個源代碼文件
         #define     定義宏
         #undef      取消已定義的宏
         #if         如果給定條件爲真,則編譯下面代碼
         #ifdef      如果宏已經定義,則編譯下面代碼
         #ifndef     如果宏沒有定義,則編譯下面代碼
         #elif       如果前面的#if給定條件不爲真,當前條件爲真,則編譯下面代碼
         #endif      結束一個#if……#else條件編譯塊
         #error      停止編譯並顯示錯誤信息




     C++ 宏定義將一個標識符定義爲一個字符串,源程序中的該標識符均以指定的字符串來代替。因此預處理命令後通常不加分號。這並不是說所有的預處理命令後都不能有分號出現。由於宏定義只是用宏名對一個字符串進行簡單的替換,因此如果在宏定義命令後加了分號,將會連同分號一起進行置換。

     在C++中,我們一般用const定義符號常量。很顯然,用const定義常量比用define定義常量更好。

在使用宏定義時應注意的是:
  (a) 在書寫#define 命令時,注意<宏名>和<字符串>之間用空格分開,而不是用等號連接。
  (b) 使用#define定義的標識符不是變量,它只用作宏替換,因此不佔有內存。
  (c) 習慣上用大寫字母表示<宏名>,這只是一種習慣的約定,其目的是爲了與變量名區分,因爲變量名
  通常用小寫字母。
  如果某一個標識符被定義爲宏名後,在取消該宏定義之前,不允許重新對它進行宏定義。取消宏定義使用如下命令:
  #undef<標識符>
  其中,undef是關鍵字。該命令的功能是取消對<標識符>已有的宏定義。被取消了宏定義的標識符,可以對它重新進行定義。
  宏定義可以嵌套,已被定義的標識符可以用來定義新的標識符。例如:
  #define PI 3.14159265
  #define R 10
  #define AREA (PI*R*R)

宏定義了一個代表特定內容的標識符。預處理過程會把源代碼中出現的宏標識符替換成宏定義時的值。宏最常見的用法是定義代表某個值的全局符號。宏的第二種用法是定義帶參數的宏,這樣的宏可以象函數一樣被調用,但它是在調用語句處展開宏,並用調用時的實際參數來代替定義中的形式參數。
    1.#define指令
        #define預處理指令是用來定義宏的。該指令最簡單的格式是:首先神明一個標識符,然後給出這個標識符代表的代碼。在後面的源代碼中,就用這些代碼來替代該標識符。這種宏把程序中要用到的一些全局值提取出來,賦給一些記憶標識符。
            #define MAX_NUM 10
            int array[MAX_NUM];
            for(i=0;i<MAX_NUM;i++)  /*……*/
        在這個例子中,對於閱讀該程序的人來說,符號MAX_NUM就有特定的含義,它代表的值給出了數組所能容納的最大元素數目。程序中可以多次使用這個值。作爲一種約定,習慣上總是全部用大寫字母來定義宏,這樣易於把程序紅的宏標識符和一般變量標識符區別開來。如果想要改變數組的大小,只需要更改宏定義並重新編譯程序即可。
        宏表示的值可以是一個常量表達式,其中允許包括前面已經定義的宏標識符。例如:
            #define ONE 1
            #define TWO 2
            #define THREE (ONE+TWO)
        注意上面的宏定義使用了括號。儘管它們並不是必須的。但出於謹慎考慮,還是應該加上括號的。例如:
            six=THREE*TWO;
        預處理過程把上面的一行代碼轉換成:
            six=(ONE+TWO)*TWO;
        如果沒有那個括號,就轉換成six=ONE+TWO*TWO;了。
        宏還可以代表一個字符串常量,例如:
            #define VERSION "Version 1.0 Copyright(c) 2003"
    2.帶參數的#define指令
        帶參數的宏和函數調用看起來有些相似。看一個例子:
            #define Cube(x) (x)*(x)*(x)
        可以時任何數字表達式甚至函數調用來代替參數x這裏再次提醒大家注意括號的使用。宏展開後完全包含在一對括號中,而且參數也包含在括號中,這樣就保證了宏和參數的完整性。看一個用法:
            int num=8+2;
            volume=Cube(num);
        展開後爲(8+2)*(8+2)*(8+2);
        如果沒有那些括號就變爲8+2*8+2*8+2了。
        下面的用法是不安全的:
            volume=Cube(num++);
        如果Cube是一個函數,上面的寫法是可以理解的。但是,因爲Cube是一個宏,所以會產生副作用。這裏的擦書不是簡單的表達式,它們將產生意想不到的結果。它們展開後是這樣的:
            volume=(num++)*(num++)*(num++);
        很顯然,結果是10*11*12,而不是10*10*10;
        那麼怎樣安全的使用Cube宏呢?必須把可能產生副作用的操作移到宏調用的外面進行:
            int num=8+2;
            volume=Cube(num);
            num++;
    3.#運算符
        出現在宏定義中的#運算符把跟在其後的參數轉換成一個字符串。有時把這種用法的#稱爲字符串化運算符。例如:
            #define PASTE(n) "adhfkj"#n
            main()
            {
               printf("%s\n",PASTE(15));
            }
        宏定義中的#運算符告訴預處理程序,把源代碼中任何傳遞給該宏的參數轉換成一個字符串。所以輸出應該是adhfkj15
    4.##運算符
        ##運算符用於把參數連接到一起。預處理程序把出現在##兩側的參數合併成一個符號。看下面的例子:
            #define NUM(a,b,c) a##b##c
            #define STR(a,b,c) a##b##c
            main()
            {
                printf("%d\n",NUM(1,2,3));
                printf("%s\n",STR("aa","bb","cc"));
            }
        最後程序的輸出爲:
                 123
                 aabbcc
        千萬別擔心,除非需要或者宏的用法恰好和手頭的工作相關,否則很少有程序員會知道##運算符。絕大多數程序員從來沒用過它。




文件包含
    #include預處理指令的作用是在指令處展開被包含的文件。包含可以是多重的,也就是說一個被包含的文件中還可以包含其他文件。標準C編譯器至少支持八重嵌套包含。
    預處理過程不檢查在轉換單元中是否已經包含了某個文件並阻止對它的多次包含。這樣就可以在多次包含同一個頭文件時,通過給定編譯時的條件來達到不同的效果。例如:
        #define AAA
        #include "t.c"
        #undef AAA
        #include "t.c"
    爲了避免那些只能包含一次的頭文件被多次包含,可以在頭文件中用編譯時條件來進行控制。例如:
        /*my.h*/
        #ifndef MY_H
        #define MY_H
          ……
        #endif
    在程序中包含頭文件有兩種格式:
        #include <my.h>
        #include "my.h"
    第一種方法是用尖括號把頭文件括起來。這種格式告訴預處理程序在編譯器自帶的或外部庫的頭文件中搜索被包含的頭文件。第二種方法是用雙引號把頭文件括起來。這種格式告訴預處理程序在當前被編譯的應用程序的源代碼文件中搜索被包含的頭文件,如果找不到,再搜索編譯器自帶的頭文件。
    採用兩種不同包含格式的理由在於,編譯器是安裝在公共子目錄下的,而被編譯的應用程序是在它們自己的私有子目錄下的。一個應用程序既包含編譯器提供的公共頭文件,也包含自定義的私有頭文件。採用兩種不同的包含格式使得編譯器能夠在很多頭文件中區別出一組公共的頭文件




條件編譯指令
    條件編譯指令將決定那些代碼被編譯,而哪些是不被編譯的。可以根據表達式的值或者某個特定的宏是否被定義來確定編譯條件。
    1.#if指令
        #if指令檢測跟在製造另關鍵字後的常量表達式。如果表達式爲真,則編譯後面的代碼,知道出現#else#elif#endif爲止;否則就不編譯。
    2.#endif指令
        #endif用於終止#if預處理指令。
            #define DEBUG 0
            main()
            {
                #if DEBUG
                    printf("Debugging\n");
                #endif
                    printf("Running\n");
            }
        由於程序定義DEBUG宏代表0,所以#if條件爲假,不編譯後面的代碼直到#endif,所以程序直接輸出Running
        如果去掉#define語句,效果是一樣的。
    3.#ifdef#ifndef
        #define DEBUG
        main()
        {
            #ifdef DEBUG
                printf("yes\n");
            #endif
            #ifndef DEBUG
                printf("no\n");
            #endif
        }
        #if defined等價於#ifdef; #if !defined等價於#ifndef
    4.#else指令
        #else指令用於某個#if指令之後,當前面的#if指令的條件不爲真時,就編譯#else後面的代碼。#endif指令將中指上面的條件塊。
        #define DEBUG
        main()
        {
            #ifdef DEBUG
                printf("Debugging\n");
            #else
                printf("Not debugging\n");
            #endif
                printf("Running\n");
       }
    5.#elif指令
        #elif預處理指令綜合了#else#if指令的作用。
        #define TWO
        main()
        {
            #ifdef ONE
                printf("1\n");
            #elif defined TWO
                printf("2\n");
            #else
                printf("3\n");
            #endif
        }
        程序很好理解,最後輸出結果是2
    6.其他一些標準指令
        #error指令將使編譯器顯示一條錯誤信息,然後停止編譯。
        #line指令可以改變編譯器用來指出警告和錯誤信息的文件號和行號。
        #pragma指令沒有正式的定義。編譯器可以自定義其用途。典型的用法是禁止或允許某些煩人的警告信息。

entium processor build" )

一:#pragma warning指令
該指令允許有選擇性的修改編譯器的警告消息的行爲
指令格式如下:
#pragma warning( warning-specifier : warning-number-list [; warning-specifier : warning-number-list...]
#pragma warning( push[ ,n ] )
#pragma warning( pop )
主要用到的警告表示有如下幾個:
once:只顯示一次(警告/錯誤等)消息
default:重置編譯器的警告行爲到默認狀態
1,2,3,4:四個警告級別
disable:禁止指定的警告信息
error:將指定的警告信息作爲錯誤報告
如果大家對上面的解釋不是很理解,可以參考一下下面的例子及說明
#pragma warning( disable : 4507 34; once : 4385; error : 164 )
等價於:
#pragma warning(disable:4507 34) // 不顯示450734號警告信息
#pragma warning(once:4385)        // 4385號警告信息僅報告一次
#pragma warning(error:164)        // 164號警告信息作爲一個錯誤。
同時這個pragma warning 也支持如下格式:
#pragma warning( push [ ,n ] )
#pragma warning( pop )
這裏n代表一個警告等級(1---4)
#pragma warning( push )保存所有警告信息的現有的警告狀態。
#pragma warning( push, n)保存所有警告信息的現有的警告狀態,並且把全局警告
等級設定爲n  
#pragma warning( pop )向棧中彈出最後一個警告信息,在入棧和出棧之間所作的
一切改動取消。例如:
#pragma warning( push )
#pragma warning( disable : 4705 )
#pragma warning( disable : 4706 )
#pragma warning( disable : 4707 )
#pragma warning( pop )
在這段代碼的最後,重新保存所有的警告信息(包括470547064707)
在使用標準C++進行編程的時候經常會得到很多的警告信息,而這些警告信息都是不必要的提示,
所以我們可以使用#pragma warning(disable:4786)來禁止該類型的警告
vc中使用ADO的時候也會得到不必要的警告信息,這個時候我們可以通過
#pragma warning(disable:4146)來消除該類型的警告信息
二:#pragma pack()
注:如果設置的值比結構體中字節最長的類型還要大,則這個變量(注意僅針對這一個變量)只按照它的字節長度對齊,即不會出現內存浪費的情況。請參見(4)
(1)
#pragma pack(1)        //每個變量按照1字節對齊
struct A
{
char x;    //aligned on byte boundary 0
int y;     //aligned on byte boundary 1
}a;
sizeof(a)==5
(2)
#pragma pack(2)        //每個變量按照2字節對齊
struct A
{
char x;    //aligned on byte boundary 0
int y;     //aligned on byte boundary 2
}a;
sizeof(a)==6
(3)
#pragma pack(4)        //每個變量按照4字節對齊
struct A
{
char x;    //aligned on byte boundary 0
int y;     //aligned on byte boundary 4
}a;
sizeof(a)==8
(4)
#pragma pack()        //默認,相當於#pragma pack(8) 每個變量按照8字節對齊
struct A
{
char x;    //aligned on byte boundary 0
int y;     //aligned on byte boundary 4
}a;
sizeof(a)==8
但是這裏y的大小是4字節,所以不會按照8字節對齊,否則將造成1int空間的浪費
.#pragma comment
The following pragma causes the linker to search for the EMAPI.LIB library while linking. The linker searches first in the current working directory and then in the path specified in the LIB environment variable:
#pragma comment( lib, "emapi" )
.#pragma deprecated
When the compiler encounters a deprecated symbol, it issues C4995:
void func1(void) {}
void func2(void) {}
int main() {
   func1();
   func2();
   #pragma deprecated(func1, func2)
   func1();   // C4995
   func2();   // C4995
}
.#pragma message
The following code fragment uses the message pragma to display a message during compilation:
#if _M_IX86 == 500
#pragma message( "P

#endif

發佈了48 篇原創文章 · 獲贊 20 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章