天天看點

c/c++編譯預處理

(1)#include<頭檔案名稱>

(2)#include"頭檔案名稱"

第一種形式一般用來包含開發環境提供的庫頭檔案,它訓示編譯預處理器

在開發環境中設定的路徑中找所需的頭檔案。

第二種形式一般用來包含自己寫的頭檔案,它訓示編譯預處理器在目前工

作目錄中搜尋頭檔案,如果找不到再到開發環境所設定的路徑中找

.#include是一個僞指令

内部包含衛哨和外部包含衛哨

為了避免同一個編譯單元包含同 ? 個頭檔案的内容超過一次(這會導緻類

型重 複定義錯),我們需要在頭檔案裡面使用内部包含衛哨。内部包含衛哨實

際上是使用 預處理器的一種标志宏。有了内部包含衛哨,我們就可以放心地在

同一個編譯單元 及其包含的頭檔案中多次包含同一個頭檔案而不會造成重複包

含。例如:

// stddef.h

#ifndef_STDDEF_H_INCLUDED_

#define _STDDEF_H_INCLUDED_

......

//頭檔案的内容

#endif

//! STDDEF H INCLUDED

// xxx.cpp 

#include “stddef.h”

#include “stddef.h” // No problem!

    !當包含一個頭檔案的時候,如果能始終如一地使用外部包含衛哨,可以顯著地提

高編譯速度,因為當一個頭檔案被一個源檔案反複包含多次時(常常是因為遞歸的

#include指令展開後所緻),可以避免多次查找和打開頭檔案的操作。例如:

#if !definde (_INCLUDED_STDDEF_H_)

#include <stddef.h>

#define _ INCLUDED_STDDEF_H_

#endif //! INCLUDED_STDDEF H

建議外部包含衛哨和内部包含衛哨使用同一個标志宏,這樣就可以少定義一個 | 宏。例如:

#if ! defined (_STDDEF_H_INCLUDED_)

#include 〈stddef.h〉

#endif //!_STDDEF_H_INCLUDED_

因為檔案stddefh中的内部包含衛哨就是—STDDEF_HJNCLUDED—,是以這裡

!不需要再次定義。

可以僅在頭檔案中包含其他頭檔案時使用外部包含衛哨,源檔案中可以不使用,

也基本不會影響編譯速度。

宏不可以調試,因為宏不會進入符号表(符号表是編譯器建立的,在編譯 時宏已經消失了),即使宏替換後出現了文法錯誤,編譯器也會将錯誤定位到源程式 中而不是定位到具體的某個宏定義中。

##與#

#include<iostream>
using namespace std;
#define STRING(x) #x #x #x
#define TEXT(x) "class" #x "info"
int main()
{
  int abc = 100;
  cout << STRING(abc) << endl;
  cout << TEXT(abc) << endl;
  system("pause");
}      
c/c++編譯預處理

合并操作符##将出現在其左右的字元序列合并成一個新的辨別符(注意,不 |是字元串)。例如:

#define CLASS_NAME(name) class##name 
#define MHRGE(x, y) x##y##x      

則宏引用:

CLASS_NAME(SysTimer)

MERGE(me, To)

将分别擴充為如下兩個辨別符:

classSysTimer 

meTome

使用合并操作符##時,産生的辨別符必須預先有定義,否則編譯器會報“辨別符未定義”的編譯錯誤。

#pragma

編譯僞指令#pragma用于執行語言實作所定義的動作(見示例9 - 5), 具體參考你 I所使用的編譯器的幫助文檔。

示例9 - 5

#pragma pack(push, 8) /*對象成員對齊位元組數*/
#pragma pack(pop)
#pragma waming(disable:4069) /* 不要産生第 C4069 号編譯警告 */
#pragma comment(lib, "kemel32.1ib”)
#pragma comment(lib, "user32.1ib")
#pragma comment(lib, "gdi32.1ib")      

建議

(1)雖然宏定義很靈活,并且通過彼此結合可以産生許多變形用法,但 是C++/C程式員不要定義很複雜的宏,宏定義應該簡單而清晰;

(2)宏名采用大寫字元組成的單詞或其縮寫序列,并在各單詞之間使用分隔;

(3)如果需要公布某個宏,那麼該宏定義應當放置在頭檔案中,否則放 置在實作檔案(.cpp)的頂部;

(4)不要使用宏來定義新類型名,應該使用typedef,否則容易造成錯誤。

(5)給宏添加注釋時請使用塊注釋(/*

*/),而不要使用行注釋。因為有些編譯器可能會把宏後面的行注釋了解為宏體的一部分;

(6)盡量使用const取代宏來定義符号常量;

(7)對于較長的使用頻率較髙的重複代碼片段,建議使用函數或模闆而不要使用帶參數的宏定義;而對于較短的重複代碼片段,可以使用帶參 數的宏定義,這不僅是出于類型安全的考慮,而且也是優化與折衷的展現;

(8)盡量避免在局部範圍内(如函數内、類型定義内等)定義宏,除非它隻在該局部範圍内使用,否則會損害程式的清晰性。

條件編譯

使用條件編譯可以控制預處理器選擇不同的代碼段作為編譯器的輸入,進而使 !得源程式在不同的編譯條件下産生不同的目标代碼。條件編譯僞指令就如同程式控 !制結構中的選擇結構,不同的是前者隻在編譯預處理階段即在正式編譯之前發揮作 丨用(不生成運作時代碼),而後者則是在運作時起作用的。條件編譯為程式的移植和 !調試帶來了極大的友善,可以用它來暫時或永久性地阻止一段代碼的編譯。我們将 在下面看到它的價值。條件編譯僞指令主要包括#if、#ifdef. #ifhdef、#elif、#else、

丨#endif、defined。每一個條件編譯塊都必須以#if開始,##endif結束,#if必須與它 丨下面的某一#endif配對;defined必須結合甜丨或#6證使用,而不能單獨使用。條件 I編譯塊可以出現在程式代碼的任何地方。

在程式設計過程中,當需要暫時放棄編譯一段代碼的時候,我們習慣于使用塊注釋 來把它屏蔽掉。但是如果這段代碼本身就含有塊注釋時,那麼雙重注釋很麻煩。此時我們可以使用條件編譯僞指令#if來屏蔽這段代碼,如下代碼所示。如果要使這段代碼生效,隻需把0改為任何一個非0的值(例如1)即可。但是千萬要記住:不要企圖用#if來代替塊注釋,兩者根本不是同一種用途。

#if 0
  ... /*...*/ //希望禁止編譯的代碼段
  ... /*...*/ //希望禁止編譯的代碼段
#endif      

由于條件編譯由編譯預處理器來處理,顯然預編譯僞指令無法計算有變量參與其中的表達式或sizeof表達式,是以隻能用常量表達式。如果常量表達式的值非0, 則條件為真:否則條件為假(見示例9-2)。

示例9-2

#define FLAG_DOS 2 
#defme FLAG_UNIX 1 
#define FLAG_WIN 0 
#define OS 1
#if OS == FLAG_DOS
  cout<<"DOS platform" <<endl;
#elif OS == FLAG_UNIX
  cout<< "UNIX platform" << endl; 
#elif OS = FLAG 一WIN
  cout<< "Windows platform" << endl; 
#else
  cout<< "Unknown platform" << endl; 
#endif      

9.3.2 #ifdef 和 #ifndef

預編譯僞指令#ifdefXYZ等價于#ifdefmed(XYZ),此處XYZ稱為調試宏。如果 前面曾經用#defme定義過宏XYZ,那麼#ifdefXYZ表示條件為真,否則條件為假。 例如:

#define XYZ

。。。

#ifdef XYZ 

DoSomethingO;

#endif

如果你不想讓DoSomething();語句被編譯,那麼删除#define XYZ,或者在其後 用#undef XYZ取消該宏即可。 預編譯僞指令#ifhdef XYZ等價于#if !defmed(XYZ)。示例9-3示範了内部包含衛

哨的用法。

示例9-3

#ifndef GRAPHICS_H //防止graphics.h被重複引用
#define GRAPHICS_H 
  #include "myheader.h"//引用非标準庫的頭檔案 
  #include <math.h>//引用标準庫的頭檔案
...//一些函數的定義和聲明
#endif