內聯函式和巨集的區別

2021-08-03 04:36:56 字數 2575 閱讀 2312

首先要說明的是內聯函式只在c++中有這個概念,在c語言中沒有。

1.巨集的缺陷

為什麼要使用巨集呢?因為函式的呼叫必須要將程式執行的順序轉移到函式所存放在記憶體中的某個位址,將函式的程式內容執行完後,再返回到轉去執行該函式前的地方。這種轉移操作要求在轉去執行前儲存現場並且記憶執行的位址,轉回後要恢復現場,並按原來儲存的位址繼續執行。因此,函式呼叫要有一定的時間和空間方面的開銷,也必將影響其效率。而巨集只是在預處理的地方把**展開,不需要額外的空間和時間開銷,所以呼叫乙個巨集比呼叫函式更有效率。我想,這個大家都應該很清楚的了。

我們常經常定義一些巨集,如

#define cc(x) ((x) > 0 ? (x) : 0)

就定義了乙個巨集。

但是巨集也有很多的不盡如人意的地方。巨集是用預處理器對巨集進行替代,預處理器處在的關鍵問題使我們可能認為預處理器的行為和編譯器的行為一樣。當然,有意使巨集在外觀上和行為上與函式呼叫一樣,因此容易被混淆。但是當微妙的差異出現時,問題就會出現了。

舉個簡單的例子: #define f(x) (x+1)

現在假如有乙個像下面的f呼叫了f(1),預處理器展開它,出現下面不希望的情況: (x) (x+1) (1) 。出現這個問題是因為在巨集定義中f和括號之間存在空格縫隙。實際上呼叫巨集時可以有空格空隙。像下面的呼叫:f(1)依然可以正確地展開為(1+1)。

上面的例子雖然微不足道,但是問題非常明確。

巨集的定義容易產生二義性。

在巨集呼叫中使用表示式作為引數時,問題就出現了。第乙個問題是表示式在巨集內展開,所以它們的優先順序不同於我們所期望的優先順序。for example:

#define dd(x)  (x*x)

我們用乙個數字去呼叫它,dd(10),這樣看上去沒有錯誤,結果返回100,是正確的,但是我們用dd(10+10)去呼叫的時候,我們期望的是400,但是結果 10+10*10+10 = 120,這顯然不是我們想要的結果。

避免這種錯誤的方法是,就是給巨集的引數加上括號: #define dd(x)  ((x)*(x))

這樣可以確保不會出錯,但是即便這樣,這個巨集仍然有出錯的可能性。這就是第二個問題,這個問題更加微妙。不像普通函式,每次在巨集中使用乙個函式,都對這個引數求值。只要巨集僅用普通變數呼叫,這個求值就開始了。但假如引數求值有***,那麼結果可能出乎預料,並肯定不能模仿函式行為。

例如使用dd(a++)呼叫它,他們本意是希望得到(a+1)*(a+1)的結果,而實際上呢?此時巨集的展開是:(a++)*(a++),如果a的值是4,我們得到的結果是5*6=30。而我們期望的結果是5*5=25。事實上,在一些c的庫函式中也有這些問題。例如:toupper(*pchar++)就會對pchar執行兩次++操作,因為toupper實際上也是乙個巨集。

此外巨集不能訪問物件的私有成員。

很清楚,這不是我們想從看起來像函式呼叫的巨集中所希望的。在這種情況下,明顯有效的解決方案是設計真正的函式。

2內聯函式與巨集的區別

我們可以看到巨集有一些難以避免的問題,怎麼解決呢?答案是內聯函式可以解決這些問題,我們使用內聯函式來取代巨集的定義。

內聯函式和巨集的區別在與,(1)巨集使用預處理器對巨集進行替代,而內聯函式是通過編譯器控制來實現的。而且內聯函式時真正的函式,只是在需要用到的地方,內聯函式像巨集一樣的展開,所以取消了函式的引數壓棧,減少了呼叫的開銷。可以像呼叫函式一樣來呼叫內聯函式,而不必擔心會產生處理巨集的一些問題。

首先,內聯函式可以完全避免巨集易出現的二義性。

for example:inline int dd(int x)  

int y = dd(a++);

編譯器會產生如下**:

int y;

a++;

a*a;

另外,(2)內聯函式可以訪問物件的私有成員函式,內聯函式在c++裡面應用最廣的,應該是用來定義訪問函式。我們定義的類中一般會把資料成員定義成私有的或者保護的,這樣,外界就不能直接讀取我們類成員的資料了。對於私有或者保護成員的讀寫就必需使用成員介面函式來進行。如果我們把這些讀寫成員函式定義為內聯函式的話,將會獲得比較好的效率。

當然,內聯函式也有一定的侷限性,就是當函式的執行**太多的時候,如果內聯函式的函式體過大,一般的編譯器會忽略內聯方式,而採用普通的方式呼叫。這樣內聯函式就和普通函式執行的效率一樣了。

使用內聯函式應注意的事項

內聯函式具有一般函式的特性,它與一般函式所不同之處只在於函式呼叫的處理。一般函式進行呼叫時,要將程式執行權轉到被呼叫函式中,然後再返回到呼叫它的函式中;而內聯函式在呼叫時,是將呼叫表示式用內聯函式體來替換。在使用內聯函式時,應注意如下幾點: 1.在內聯函式內不允許用迴圈語句和開關語句。 如果內聯函式有這些語句,則編譯將該函式視同普通函式那樣產生函式呼叫**,遞迴函式(自己呼叫自己的函式)是不能被用來做內聯函式的。內聯函式只適合於只有1~5行的小函式。對乙個含有許多語句的大函式,函式呼叫和返回的開銷相對來說微不足道,所以也沒有必要用內聯函式實現。 2.內聯函式的定義必須出現在內聯函式第一次被呼叫之前。 3.本欄目講到的類結構中所有在類說明內部定義的函式是內聯函式。

內聯函式和巨集的區別

1 替換的時機 內聯函式是在編譯期間進行替換,就是將該函式在函式的呼叫點直接展開 巨集函式是在預處理階段替換 2 替換的成功率 內聯函式只是對編譯器的乙個建議,如果編譯器覺得該函式不適合成為內聯函式時,便會忽略前面的 inline 關鍵字,比如我們將遞迴函式寫成內聯函式,編譯器會忽略前面的 inli...

巨集和內聯函式的區別

巨集和內聯函式的區別 總結 內聯函式是對巨集的優化 1 巨集 巨集常量例 deifne pi 3.14 優點 一改全改,調高 的復用性 提高效能 缺點 定義巨集出錯時,不容易定位錯誤,因為預處理階段進行了替換 展開 在c 中對巨集常量進行了優化 用const 修飾變數 同時const具有巨集常量的屬...

內聯函式和巨集的區別

先說巨集和函式的區別 1.巨集做的是簡單的字串替換 注意是字串的替換,不是其他型別引數的替換 而函式的引數的傳遞,引數是有資料型別的,可以是各種各樣的型別.2.巨集的引數替換是不經計算而直接處理的,而函式呼叫是將實參的值傳遞給形參,既然說是值,自然是計算得來的.3.巨集在編譯之前進行,即先用巨集體替...