1. 程式人生 > >C++的編譯預處理

C++的編譯預處理

end 裏的 const return key 大寫字母 font include 效果

C++中,在編譯器對源程序進行編譯之前,首先要由預處理對程序文本進行預處理。預處理器提供了一組預編譯處理指令和預處理操作符。預處理指令實際上不是C++語言的一部分,它只是用來擴充C++程序設計的環境。所有的預處理指令在程序中都是以“#”來引導,每一條預處理指令單獨占用一行,不要用分號結束。預處理指令可以根據需要出現在程序的位置。

先來看看一些預處理指令

技術分享圖片

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

首先是宏定義:

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

在C++中,我們一般用const定義符號常量。很顯然,用const定義常量比用define定義常量更好。 在使用宏定義時應註意的是: (1) 在書寫#define 命令時,註意<宏名>和<字符串>之間用空格分開,而不是用等號連接。
(2) 使用#define定義的標識符不是變量,它只用作宏替換,因此不占有內存。
(3) 習慣上用大寫字母表示<宏名>,這只是一種習慣的約定,其目的是為了與變量名區分,因為變量名
通常用小寫字母。
如果某一個標識符被定義為宏名後,在取消該宏定義之前,不允許重新對它進行宏定義。取消宏定義使用如下命令:
#undef<標識符>
其中,undef是關鍵字。該命令的功能是取消對<標識符>已有的宏定義。被取消了宏定義的標識符,可以對它重新進行定義。
宏定義可以嵌套,已被定義的標識符可以用來定義新的標識符。例如:
#define PI 3.14159265
#define R 10
#define AREA (PI*R*R) 1>#define指令
#define預處理指令是用來定義宏的。該指令最簡單的格式是:首先神明一個標識符,然後給出這個標識符代表的代碼。在後面的源代碼中,就用這些代碼來替代該標識符。這種宏把程序中要用到的一些全局值提取出來,賦給一些記憶標識符。 #define MAX_SIZE 10
int array[MAX_SIZE];
for(i=0;i<MAX_SIZE;i++) /*……*/ 在這個例子中,對於閱讀該程序的人來說,符號MAX_NUM就有特定的含義,它代表的值給出了數組所能容納的最大元素數目。程序中可以多次使用這個值。作為一種約定,習慣上總是全部用大寫字母來定義宏,這樣易於把程序紅的宏標識符和一般變量標識符區別開來。如果想要改變數組的大小,只需要更改宏定義並重新編譯程序即可。
宏表示的值可以是一個常量表達式,其中允許包括前面已經定義的宏標識符。例如:
#define ONE 1
#define TWO 2
#define THREE (ONE+TWO)
註意上面的宏定義使用了括號。盡管它們並不是必須的。但出於謹慎考慮,還是應該加上括號的。例如:
six=THREE*TWO; 預處理過程把上面的一行代碼轉換成:
six=(ONE+TWO)*TWO;   結果為6;正確
如果沒有那個括號,就轉換成six=ONE+TWO*TWO;了。   結果為5 ;錯誤
宏還可以代表一個字符串常量,例如:
#define VERSION "Version 1.0 Copyright(c) 2003"   又如:用預處理指令#define定義一個常數,表示一年有多少秒;   #define SECONDS_PER_YEAR (60*60*24*365)UL (表達式將使一個16位機的整形數溢出,因此要用到長整型符號L,告訴編譯器該數是一個無符號長整型數UL)。 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++; 寫一個“”標準“”宏 MIN 這個宏輸入兩個參數並返回較小的那個 #define MIN(A,B) ((A)<=(B)?(A):(B))

#define KEY_DOWN(vk_code) (GetAsyncKeyState(vk_code)&0x80000?1:0) //獲取鍵盤輸入

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
復雜一點的如

#define CC_READONLY_FUN(varType, varName, funName) \
public:\
void set##funName(varType var)\
{\
varName = var;\
}\
varType get##funName()\
{\
return varName;\
}\
protected:\
varType varName;

那麽CC_READONLY_FUN(int, m_Size, Size) 就等同於

public:

void setSize(int var)

{

  m_Size = var;

}

int getSize()

{

  return m_Size;

}

protected:

int m_Size

千萬別擔心,除非需要或者宏的用法恰好和手頭的工作相關,否則很少有程序員會知道##運算符。絕大多數程序員從來沒用過它,但是還是要理解一下的。

然後是文件包含:

#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"
第一種方法是用尖括號把頭文件括起來。這種格式告訴預處理程序在編譯器自帶的或外部庫的頭文件中搜索被包含的頭文件。第二種方法是用雙引號把頭文件括起來。這種格式告訴預處理程序在當前被編譯的應用程序的源代碼文件中搜索被包含的頭文件,如果找不到,再搜索編譯器自帶的頭文件。
采用兩種不同包含格式的理由在於,編譯器是安裝在公共子目錄下的,而被編譯的應用程序是在它們自己的私有子目錄下的。一個應用程序既包含編譯器提供的公共頭文件,也包含自定義的私有頭文件。采用兩種不同的包含格式使得編譯器能夠在很多頭文件中區別出一組公共的頭文件

最後是條件編譯指令:

使用條件編譯指令,可以限定程序中的某些內容要在滿足一定條件的情況下才參與編譯。因此,利用條件編譯可以使同一個源程序在不同的編譯條件下產生不同的目標代碼。例如,可以在調試時增加一些調試語句,以達到跟蹤的目的,並利用條件 編譯指令,限定當程序調試後,重新編譯時,使調試語句不參與編譯。常用的條件編譯語句有下列5種形式:

(1)形式一

技術分享圖片

(2)形式二

技術分享圖片

(3)形式三

技術分享圖片

(4)形式四

技術分享圖片

如果標識符經“#defined”定義過,且未經undef 刪除,則編譯程序段1,否則編譯程序段2.如果沒有程序段2,則#else可以省略:

技術分享圖片

(5)形式五

技術分享圖片

如果“標識符”未被定義過,則編譯程序段1,否則編譯程序段2.如果沒有程序段2,則#else 可以省略:

技術分享圖片

至此,結束。

C++的編譯預處理