C語言 inline行內函數與帶參巨集
C語言 inline行內函數與帶參巨集
一、簡述
簡單的介紹inline行內函數、帶參巨集的作用。
二、函式的執行與呼叫
函式執行:會將之前的棧的頂,棧基址壓棧,並在棧中開闢空間存放傳入的引數資料。返回時將返回資料放到通用暫存器,並釋放棧空間。
函式呼叫:在C語言中,程式式順序執行的,函式呼叫時,轉移到函式所存放的記憶體中的某個地址,將函式執行完之後,再返回到呼叫之前的位置,繼續執行。這種轉移再回來操作,要求在轉移之前保護現場(當時的執行狀態,如暫存器的值,程式執行到帶那裡,堆疊的使用情況等),在轉回之後要先恢復現場,然後繼續執行。
函式呼叫會增加時間和空間的開銷。頻繁的呼叫函式會影響程式的執行效率。
但是函式功能最好單一(一個函式一個功能),那麼有時候就需要將某一些程式碼功能封裝為函式模組,以方便多次呼叫,加上有意義的函式名簡潔易懂,方便維護。
三、帶參巨集
為了將某一部分程式碼功能封裝,但是又不想經過函式呼叫,我們可以使用帶參巨集來實現。
如:求最大值、最小值的函式
int Max(int x, int y) // 求兩個整數的最大值 { return (x>y?x:y); } int Min(int x, int y)// 求兩個整數的最小值 { return (x<y?x:y); }
如果在程式設計中多處需要求兩個數的最值,將其封裝成一個功能函式,讓程式碼更加的簡介,清晰,容易維護。
但是這個函式的程式碼量很少,多處呼叫,封裝成為函式就會花費時間和空間在函式呼叫上,會降低程式的執行效率。
在這種情況下,帶參巨集就能很好的解決:既能實現一定的封裝,又不會降低程式的執行效率。
將其編寫為帶參巨集:
#define MAX(x,y) (x>y?x:y)
#define MIN(x,y) (x<y?x:y)
我們可以像函式一樣呼叫,但它不是函式,而是巨集,在預編譯階段就展開,其實就是簡單的替換。
如:
int a = 5;
int b = 8;
int c = Max(a, b);// 函式方式呼叫
int c = MAX(a, b);// 帶參巨集方式,在預編譯階段,這一句就會展開成為 int c = (a>b?a:b);
//其實就是簡單的巨集替換 將 MAX(a, b) 替換成為 (a>b?a:b)
注1:C語言編從C程式碼譯成可執行檔案依次經過4個階段:預編譯、編譯、彙編、連結
預編譯:處理預編譯指令,如#include(檔案包含),#define(巨集替換),#if(條件編譯)等
編譯:將C程式碼編譯生成彙編程式碼
彙編:將彙編程式碼生成目標檔案(二進位制檔案)
連結:連結其他目標檔案、動態庫、靜態庫--》生成可執行檔案。
注2:巨集定義一般會將引數加小括號以確保優先順序(也就是無惡報按照自己的意願順序執行)
如:
#define MAX(x,y) (x>y?x:y) 會寫為 #define MAX(x,y) ( (x)>(y)?(x):(y) )
舉例:不加小括號會出現的問題
// 以下定義一個求積的帶參巨集
#define MLT(x,y) x*y //或define MLT(x,y) (x*y)
//巨集一般使用大寫 multiplication:乘法
假設有
int a = 5;
int b = 8;
int c = MLT(a,b);// 等價 int c = a*b;可以正確得出結果
如果是
int c = MLT(a+2,b);// 等價int c = a+2*b 結果為21,與預期的(a+2)*b=(5+2)*8=56不同。
帶參巨集的優缺點:
優點:1) 在一定封裝性的同時提高執行效率。
2)可以簡單的實現"過載"。(對同一功能不同引數型別的相容)
缺點:1) 因為是純文字替換,不會進行引數型別檢查,並且容易出現優先順序問題。
2) 不方便除錯。
3) 如果呼叫的此時很多,那麼執行程式碼會增多。
四、行內函數 (inline)
在普通函式前面加上關鍵字inline,這個函式就稱為行內函數。如:
inline int Max(int x, int y) // 求兩個整數的最大值
{
return (x>y?x:y);
}
行內函數在編譯時期展開,就是將 要呼叫的行內函數的程式碼 在呼叫的地方複製一份。這樣不用經過函式呼叫,提高執行效率。
行內函數的優缺點:
優點:1) 不用經過函式呼叫,提高執行效率。
2) 相對帶參巨集,在除錯程式時有更多有用的除錯資訊。
3) 進行引數型別檢查。
缺點:1) 最終由編譯器決定是否內聯,而且不能有遞迴,否則產生無窮展開。
2) 行內函數的使用可能大大增加編譯時間。
3) 如果多處呼叫,會增加程式碼量。
五、總結
無論是帶參巨集,函式行內函數,都有著"減少函式呼叫,提高執行效率"的目的。並且都有著"簡潔的封裝"的意思。
所以想要將某一動作作為帶參巨集或者是行內函數,這個動作最好要"簡單"一點,最好不要有多層巢狀的迴圈,不要有遞迴呼叫。
巨集:簡單的純文字替換(在預編譯階段展開)。
行內函數:將函式程式碼複製到要呼叫的地方(在編譯階段展開)。