1. 程式人生 > >巨集定義和函式呼叫的區別

巨集定義和函式呼叫的區別

含引數的巨集與函式的區別

?         巨集替換不佔執行時間,只佔編譯時間;而函式呼叫則佔執行時間(分配單元、保留現場、值傳遞、返回),所以每次執行都要載入所以執行起來比較慢一些。。

?         定義巨集的時候不要在巨集及其引數之間鍵入空格,因為巨集替換的時候會把你不經意打的空格當作巨集的一部分進去。

?         在巨集定義中把每個引數都用括號括起來,同時把整個結果也用括號(對於單個表示式的巨集,可以使用小括號(),對於巨集定義的複合語句可以使用{},Linux核心中有一個比較好的巨集定義,do {…}while(0))括起來,以防止當巨集用於一個更大的表示式時可能出現的問題。

?         使用巨集次數多時,巨集展開後源程式長,因為每展開一次都使程式增長,但是執行起來比較快一點(這也不是絕對的,當有很多巨集展開,目標檔案很大,執行的時候執行時系統換頁頻繁,效率就會低下)。而函式呼叫不使源程式變長。

?         函式呼叫時,先求出實參表示式的值,然後帶入形參。而使用帶參的巨集只是進行簡單的字元替換。

?         函式呼叫是在程式執行時處理的,分配臨時的記憶體單元;而巨集展開則是在編譯時進行的,在展開時並不分配記憶體單元,不進行值的傳遞處理,也沒有“返回值”的概念。

?         對函式中的實參和形參都要定義型別,二者的型別要求一致,如不一致,應進行型別轉換;而巨集不存在型別問題,巨集名無型別,它的引數也無型別,只是一個符號代表,展開時帶入指定的字元即可。巨集定義時,字串可以是任何型別的資料。

?         呼叫函式只可得到一個返回值,且有返回型別,而巨集沒有返回值和返回型別,但是用巨集可以設法得到幾個結果。

?         函式體內有Bug,可以在函式體內打斷點除錯。如果巨集體內有Bug,那麼在執行的時候是不能對巨集除錯的,即不能深入到巨集內部。

?         C++中巨集不能訪問物件的私有成員,但是成員函式就可以。

?         巨集的定義很容易產生二義性,如:定義#define S(a) (a)*(a),程式碼S(a++),巨集展開變成(a++)*(a++)這個大家都知道,在不同編譯環境下會有不同結果。

行內函數和巨集的區別(行內函數的優點)

行內函數和巨集的區別在於,巨集是由前處理器對巨集進行替代,而行內函數是通過編譯器控制來實現的。

而且行內函數是真正的函式,只是在需要用到的時候,行內函數像巨集一樣的展開,所以取消了函式的引數壓棧,減少了呼叫的開銷。你可以象呼叫函式一樣來呼叫行內函數,而不必擔心會產生於處理巨集的一些問題。

我們可以用Inline來定義行內函數,不過,任何在類的說明部分定義的函式都會被自動的認為是行內函數。(當然行內函數的識別對編譯器來說是一個複雜的工作,後繼可能有專門的論述)

當然,行內函數也有一定的侷限性。就是函式中的執行程式碼不能太多了,如果,行內函數的函式體過大,一般的編譯器會放棄內聯方式,而採用普通的方式呼叫函式。這樣,行內函數就和普通函式執行效率一樣了。

行內函數通過避免被呼叫的開銷來提高執行效率,尤其是它能夠通過呼叫(“過程化整合”)被編譯器優化。

如何選擇使用巨集還是函式:以下情況可以選擇巨集,其他情況最好選用函式

2        一般來說,用巨集來代表簡短的表示式比較合適。

2        在考慮效率的時候,可以考慮使用巨集,或者行內函數。

2        在標頭檔案保護(防止重複包含編譯),條件編譯中的#ifdef,#if defined以及assert的實現