當前位置:才華齋>計算機>C語言>

C語言的預處理程式碼

C語言 閱讀(2.57W)

導語:預處理是程式在被編譯器編譯以前,由前處理器預先進行的處理。預處理程式碼含有“#”作為符號標誌,以區分其他原始碼。下面是C語言的預處理程式碼,一起來學習下吧:

C語言的預處理程式碼

 1.#include 標頭檔案

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

關於標頭檔案的好處、來源、用法可以參考一文

補充:

①.#include 和 #include “XXX.h” 的區別:

表示在編譯器定義的引用目錄查詢.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個檔案,分別是,b.h,c.h,d.h。的頭部是:#include "b.h "#include "c.h "b.h和c.h的頭部都是:#include "d.h "而d.h裡面有class D的定義。這樣一來,編譯器編譯的時候,先根據#include "b.h "去編譯b.h這個問題,再根據b.h裡面的#include "d.h ",去編譯d.h的這個檔案,這樣就把d.h裡面的class D編譯了;然後再根據的第二句#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

#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

實際本程式碼中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其實就是一個“巨集替換”

這個例子是巨集定義的經典應用之一,完成了函式不能完成的功能,但是巨集定義也不能濫用,通常,如果相同的程式碼需要出現在程式的幾個地方,更好的

方法是把它實現為一個函式。