天天看點

C語言的預處理代碼

C語言主要有三種預處理功能:1.檔案包含#include 2.宏定義#define 3.條件編譯#if  #ifdef等。

預處理是程式在被編譯器編譯以前,由預處理器預先進行的處理。預處理代碼含有“#”作為符号标志,以區分其他源代碼。

預處理好處主要是友善提高程式的可讀性的維護性,友善開發人員開發代碼,還可以提高編譯的效率。

預處理的過程主要是訓示預處理程式如何修改源代碼。在對程式進行通常的編譯處理之前,編譯程式會自動運作預處理程式,對程式進行編譯前的預處理。

比如說當使用#include檔案包含時,會在編譯之前将include的檔案替換到#include的相應位置;當使用#define,會在編譯之前将宏替換成實際的參數:當使用條件編譯時,會判斷是否編譯器編譯目前的代碼段。

1.#include 頭檔案   

其實就是把 頭檔案的内容 全部替換到 目前#include處。

關于頭檔案的好處、來源、用法可以參考http://blog.csdn.net/pashanhuxp/article/details/39927913一文

補充:

①.#include <XXX.h> 和 #include “XXX.h” 的差別:

<XX> 表示在編譯器定義的引用目錄查找.h頭檔案;

“XX” 表示先在項目目前目錄查找.h頭檔案,若沒有再去編譯器指定的目錄查找;

是以,若引用自定義的.h頭檔案,隻能用#include “XX”。

②.在Eclipse CDT下,#include "XX.h"  指引用usr/include 目錄下的頭檔案,并不包括子檔案夾的頭檔案,若引用子檔案夾下的頭檔案,需要改為#include “XX/XX.h”

2.#define 宏定義:

宏定義也可以稱作“宏替換”,宏會在編譯之前被預處理程式替換掉。通常用大寫表示

宏定義可以增加程式的可讀性和可維護性,比如 #define PI 3.14159

并且宏替換函數,可以提高運作效率,減少函數調用造成的系統開銷。比如 #define sum(a,b,c) (a+b+c) 比定義一個求和函數int sum(a,b,c)要節省記憶體,提高效率。這裡需要注意替換的意思,宏是被直接替換的,比如 #define AA a-b      若不加括号,則在使用過程中,會出現cc*AA 變成 cc*a-b 的錯誤。 

關于空的宏定義的補充:

①空的宏定義修飾函數:#define SUM

代碼中有時會出現 #define SUM   并沒給宏SUM “指派” 。這時可以将sum 了解成空的,無意義的。

比如用來修飾函數: SUM int getSum(a,b)  這時可以将SUM的作用了解為對函數作用進行 簡單的描述 ,使調用者更明白

②空的宏定義常見于頭檔案中,防止頭檔案的内容被重複包含。(平時:最好養成這種寫頭檔案的習慣)

#ifndef _8_4_2_H_
#define _8_4_2_H_

...頭檔案内容...

#endif /* 8_4_2_H_ */
           

這時 _8_4_2_H_ 宏就是一個空宏,用于條件編譯,有時常見于防止頭檔案重複包含的用途中。給你舉個例子,再順便分析一下:假設你的工程裡面有4個檔案,分别是a.cpp,b.h,c.h,d.h。a.cpp的頭部是:#include "b.h "#include "c.h "b.h和c.h的頭部都是:#include "d.h "而d.h裡面有class D的定義。這樣一來,編譯器編譯a.cpp的時候,先根據#include "b.h "去編譯b.h這個問題,再根據b.h裡面的#include "d.h ",去編譯d.h的這個檔案,這樣就把d.h裡面的class D編譯了;然後再根據a.cpp的第二句#include "c.h ",去編譯c.h,最終還是會找到的d.h裡面的class D,但是class D之前已經編譯過了,是以就會報重定義錯誤。

如果在d.h中加上

ifndef  _D_H  

define _D_H    

.......頭檔案内容......比如定義class D        

endif  

就可以防止這種重定義錯誤。

③與條件編譯結合:

#ifdefine WINDOWS
   ....針對windows的相關處理...
#endif
           
</pre><pre name="code" class="cpp">#ifdefine LINUX
    ...針對LINUX的相關操作.....
 #endif

           
則,通過#define WINDOWS 或者 #define LINUX,可以實作多系統下程式設計。
           

④#define後隻有一個函數,等價空函數:#define FUNCTION(args)  

在頭檔案中見到過這種情況,FUNCTION(args) 函數在這裡define為空,則等價空函數實際不會進行任何處理。

通常,在這個頭檔案中,還其他已指派的這個語句 #define FUNCTION(args)  (args*5)

使用舉例:

#ifdefine WIN
  #define FUNCTION(args)  (args*5)
#else
  #define  FUNCTION(args)
#endif
           

如果define了WIN 則FUNCTION(args) 會進行一些具體的操作,否則,FUNTION(args)并不會執行任何處理。

這樣定義,友善了調用者,即我在調用的時候,不需要花費代碼去判斷是不是define了WIN,我都可以在我的代碼裡直接使用FUNCTION(args)。定義了,則會對參數進行處理,而沒有定義的話,不會對參數進行改變。

⑤還有一些編譯器預定義宏,格式是“雙下劃線開頭”。主要辨別一些編譯環境資訊。比較少用到。

3.#條件編譯

使用條件編譯時,會判斷是否編譯器編譯目前的代碼段。提高編譯效率。

#ifdef 條件
  代碼段。。。   
#endif
           

解釋:若宏定義了條件,則執行代碼段,否則不執行。

#if 條件
  代碼段。。。
#endif
           

解釋:若條件為真,則執行代碼段,否則不執行。

使用舉例:

#if 0
  A
#endif</span>
           

實際本代碼中A從不執行,這樣寫是為了友善以後調試更改,若想執行A,則隻改為 #if 1即可。

4.#宏和函數的差別(摘自網絡)

(1)看一個例子,比較兩個數或者表達式大小,首先我們把它寫成宏定義:

#define MAX( a, b) ( (a) > (b) (a) : (b) ) 其次,把它用函數來實作: int max( int a, int b) {  return (a > b a : b)  } 很顯然,我們不會選擇用函數來完成這個任務,原因有兩個: 首先,函數調用會帶來額外的開銷,它需要開辟一片棧空間,記錄傳回位址,将形參壓棧,從函數傳回還要釋放堆棧。這種開銷不僅會降低代碼效率, 而且代碼量也會大大增加,而使用宏定義則在代碼規模和速度方面都比函數更勝一籌; 其次,函數的參數必須被聲明為一種特定的類型,是以它隻能在類型合适的表達式上使用,我們如果要比較兩個浮點型的大小,就不得不再寫一個專門針對浮點型的比較函數。反之,上面的那個宏定義可以用于整形、長整形、單浮點型、雙浮點型以及其他任何可以用“>”操作符比較值大小的類型,也就是說,宏是與類型無關的。 (2)和使用函數相比,使用宏的不利之處在于每次使用宏時,一份宏定義代碼的拷貝都會插入到程式中。除非宏非常短,否則使用宏會大幅度增加程式的長度。 (3)還有一些任務根本無法用函數實作,但是用宏定義卻很好實作。比如參數類型沒法作為參數傳遞給函數,但是可以把參數類型傳遞給帶參的宏。 看下面的例子: #define MALLOC(n, type) / ( (type *) malloc((n)* sizeof(type)))     // “/”為強制換行符,表示下一行其實是在上一行的。 利用這個宏,我們就可以為任何類型配置設定一段我們指定的空間大小,并傳回指向這段空間的指針。我們可以觀察一下這個宏确切的工作過程: int *ptr; ptr = MALLOC ( 5, int ); 将這宏展開以後的結果: ptr = (int *) malloc ( (5) * sizeof(int) );       // 記住#define其實就是一個“宏替換” 這個例子是宏定義的經典應用之一,完成了函數不能完成的功能,但是宏定義也不能濫用,通常,如果相同的代碼需要出現在程式的幾個地方,更好的 方法是把它實作為一個函數。

繼續閱讀