程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> C/C++的預編譯和宏定義

C/C++的預編譯和宏定義

編輯:關於C語言

預編譯是整個編譯過程的第一步,是g++ -E選項輸出的結果。

這個步驟處理的是源文件/頭文件中的宏,宏指令常用的有以下幾類:

文件包含:#include
宏定義:#define、#undef
條件編譯:#ifdef、#ifndef、#if、#elif、#else、#endif
1. 文件包含 #include
預處理會把要包含的文件的內容全部包含進來,比如下面這個文件prepro.cpp:

[cpp]
#include "prehead.h" 
int main(){ 
  add(1, 2); 

引入了頭文件prehead.h:

[cpp]
#ifndef _PREHEAD_H 
#define _PREHEAD_H 
/* declaration of a function that add two integer */ 
int add(int, int); 
#endif 
使用命令g++ -E prepro.cpp預編譯,輸出結果如下:

[plain]
# 1 "prepro.cpp" 
# 1 "<built-in>" 
# 1 "<command-line>" 
# 1 "prepro.cpp" 
# 1 "prehead.h" 1 
 
 
 
 
 
 
int add(int, int); 
# 2 "prepro.cpp" 2 
 
int main(){ 
  add(1, 2); 

可以看到結果中兩個文件的宏定義都被刪除了,文件prehead.h中注釋也被刪除了,而剩下的內容被引入了prepro.h中。至於#ifndef的意思最後介紹。

2. 宏定義:#define、#undef
#define宏定義的一種用法是聲明一個宏,用來實現條件編譯,這種用法後面和#ifdef等宏命令一起介紹。

#define宏定義的另一種用法是用來定義常量,定義准inline的函數。這種用法不好,應該避免使用。

使用#define定義的變量,比如:#define PI 3.14,預編譯以後程序中所有的PI都會被3.14替代。再比如上次談g++那篇博客的這個例子:

[cpp]
#define ONE 1 
#define TWO 2 
 
int add_one_two(){ 
  return ONE + TWO; 

預編譯後結果如下:

[plain]
# 1 "add.cpp" 
# 1 "<built-in>" 
# 1 "<command-line>" 
# 1 "add.cpp" 
 
int add_one_two(){ 
  return 1 + 2; 

可以看到定義的ONE被替換成了1,定義的TWO被替換成了2。

使用常量是好的,但是使用#define這種方法的不好是調試時候變量名都被替換了,失去了常量的效果。最好用下面這種方式定義變量:

[plain]
const TYPE VAR_NAME=value 
使用#define定義准inline函數,比如下面這個的例子(引自《effective C++》):

[cpp]
#define max(a, b) ( (a) > (b) ? (a) : (b) ) 
一般也沒什麼問題,但是:

[cpp]
int a = 5, b = 0; 
max(++a, b);            /* a自增了兩次 */ 
max(++a, b + 10);       /* b自增了一次*/ 
所以這樣定義是會出問題的。使用#define定義函數的初衷是為了提高程序運行速度,但是這種方式提高速度有限,根本上應該從改進數據結構,算法方面入手。這種#define,果斷寫成inline就OK了,那麼多括號寫著累,看著也累,沒必要。

3. 條件編譯:#ifdef、#ifndef、#if、#elif、#else、#endif
條件編譯算是宏最精髓的應用吧。先來說說這幾個指令的意思,如果你看過我前面的博客,而且堅持在USACO上做題,那麼想必你已經熟悉了C++最基本的語法,那這幾個指令看一眼也能基本知道大概意思。下面用注釋方式解釋:

[plain]
#ifdef              //if define,如果定義了。。。 
#ifndef             //if not define,如果沒有定義。。。 
#if             // 如果。。。 
#elif               // 或者如果。。。 
#else               // 或者。。。 
#endif              // 結束if 
第一種條件編譯
用來防止一個頭文件引入兩次。比如開始那個例子裡面的頭文件prehead.h:

[cpp]
#ifndef _PREHEAD_H      /* 如果沒有定義_PREHEAD_H */ 
#define _PREHEAD_H      /* 定義_PREHEAD_H */ 
/* declaration of a function that add two integer */ 
int add(int, int); 
#endif                          /* 結束上面對應的#ifndef */ 
然後更改下我們的prepro.cpp文件,增加一行#include "prehead.h"

[cpp]
#include "prehead.h" 
#include "prehead.h" 
int main(){ 
  add(1, 2); 

這裡我手工展開,變成下面這個樣子,然後在注釋裡面分析一下:
[cpp]
#ifndef _PREHEAD_H      /* 如果沒有定義_PREHEAD_H */  /* 的確沒有定義 */ 
#define _PREHEAD_H      /* 定義_PREHEAD_H */  /* 那麼就定義之 */ 
/* declaration of a function that add two integer */ 
int add(int, int); 
#endif                          /* 結束上面對應的#ifndef */ 
 
#ifndef _PREHEAD_H      /* 如果沒有定義_PREHEAD_H */  /* 上面已經定義了,條件為false */ 
#define _PREHEAD_H      /* 定義_PREHEAD_H */  /* 所以從這裡開始到endif都會被預處理器刪除 */ 
/* declaration of a function that add two integer */ 
int add(int, int); 
#endif                          /* 結束上面對應的#ifndef */  /* 後面的編譯獨立於第二個#ifndef _PREHEAD_H */ 
 
int main(){ 
  add(1, 2); 

使用命令g++ -E prepro.cpp,輸出和第一個例子裡面一樣,如下:

[plain]
# 1 "prepro.cpp" 
# 1 "<built-in>" 
# 1 "<command-line>" 
# 1 "prepro.cpp" 
# 1 "prehead.h" 1 
 
 
 
int add(int, int); 
# 2 "prepro.cpp" 2 
 
int main(){ 
  add(1, 2); 

或許你會說,這個SB,一個頭文件include兩次,哈哈哈哈。但是,這只是個例子,實際項目中文件引入比較復雜,比如上面的prepro.cpp引入了第三個文件,然後第三個文件引入prehead.h,那麼沒有條件編譯的話prepro.cpp裡面就會有兩個prehead.h。

使用#ifndef... #define... #endif可以保證一個文件只引入一次,這裡的...可以是任何內容,只要你能保證定義的這個東西其他地方沒定義,但是慣例是定義文件名的大寫然後加幾個下劃線,像上面那個例子一樣。

第二種條件編譯
用來實現跨平台編譯。比如下面這樣:

[cpp]
#ifdef _linux_ 
// linux平台相關代碼。。。 
#endif 
#ifdef _windows_ 
// windows平台相關代碼。。。 
#endif 
#ifdef _macos_ 
// mac os平台相關代碼 
#endif 

然後在某個include的配置文件config.h裡面,如果有如下定義:#define _linux_,那麼編譯器就只編譯linux平台相關的代碼;如果有如下定義:#define _windows_,那麼只編譯windows平台相關的代碼。

最後要說明的是,配置文件config.h不是手動寫的,而是腳本自動生成的。至於如何編寫腳本或者使用工具生成腳本,算是另一個話題了,以後再介紹吧。

 

宏指令還有其他一些,但是用的很少,我就不寫了,想了解的朋友可以參考這個網址:點擊進入。這個網站C++的文檔,手冊,幫助都一流,學習C++可以多看看。


參考文獻:

Effective C++, third edition. Scott Meyers. 2005

摘自 程序猿

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved