C/C++細數(shù)宏與函數(shù)有那些區(qū)別
一、宏和函數(shù)的對比
1.宏的優(yōu)點
- 宏通常被應用于執(zhí)行簡單的運算。
- 比如在兩個數(shù)中找出較大的一個。
#define MAX(a, b) ((a)>(b)?(a):(b))
那為什么不用函數(shù)來完成這個任務?
原因有兩點:
用于調(diào)用函數(shù)和從函數(shù)返回的代碼可能比實際執(zhí)行這個小型計算工作所需要的時間更多。
所以宏比函數(shù)在程序的規(guī)模和速度方面更勝一籌。
舉例:
用宏實現(xiàn)兩個數(shù)中找出較大值。
#define MAX(x, y) ((x) > (y) ? (x) : (y)) int main() { int a = 10; int b = 20; int c = MAX(a, b); // 宏 return 0; }
轉(zhuǎn)到反匯編,查看匯編代碼。
int c = MAX(a, b); // 宏
00791783 mov eax,dword ptr [a]
00791786 cmp eax,dword ptr [b]
00791789 jle __$EncStackInitStart+3Ah (0791796h)
0079178B mov ecx,dword ptr [a]
0079178E mov dword ptr [ebp-0E8h],ecx
00791794 jmp __$EncStackInitStart+43h (079179Fh)
00791796 mov edx,dword ptr [b]
00791799 mov dword ptr [ebp-0E8h],edx
0079179F mov eax,dword ptr [ebp-0E8h]
007917A5 mov dword ptr [c],eax
用函數(shù)實現(xiàn)兩個數(shù)中找出較大值。
int Max(int x, int y) { return ((x) > (y) ? (x) : (y)); } int main() { int a = 10; int b = 20; int c = Max(a, b); // 函數(shù) return 0; }
調(diào)用函數(shù)的匯編代碼。
int c = Max(a, b); // 函數(shù)
002C1793 mov eax,dword ptr [b]
002C1796 push eax
002C1797 mov ecx,dword ptr [a]
002C179A push ecx
002C179B call _Max (02C139Dh) // 調(diào)用Max函數(shù)
002C17A0 add esp,8
002C17A3 mov dword ptr [c],eax
計算的匯編代碼。
int Max(int x, int y)
{
002C1DF0 push ebp
002C1DF1 mov ebp,esp
002C1DF3 sub esp,0C4h
002C1DF9 push ebx
002C1DFA push esi
002C1DFB push edi
002C1DFC lea edi,[ebp-4]
002C1DFF mov ecx,1
002C1E04 mov eax,0CCCCCCCCh
002C1E09 rep stos dword ptr es:[edi]
002C1E0B mov ecx,offset _66EADA86_詳解預處理\test@c (02CC000h)
002C1E10 call @__CheckForDebuggerJustMyCode@4 (02C1307h)
// 下面這些才是計算的代碼,上面這些代碼可以說還是在為調(diào)用函數(shù)做準備
// 并且可以看出下面的匯編代碼和宏那里的匯編代碼是一樣的
return ((x) > (y) ? (x) : (y));
002C1E15 mov eax,dword ptr [x]
002C1E18 cmp eax,dword ptr [y]
002C1E1B jle __$EncStackInitStart+2Ch (02C1E28h)
002C1E1D mov ecx,dword ptr [x]
002C1E20 mov dword ptr [ebp-0C4h],ecx
002C1E26 jmp __$EncStackInitStart+35h (02C1E31h)
002C1E28 mov edx,dword ptr [y]
002C1E2B mov dword ptr [ebp-0C4h],edx
002C1E31 mov eax,dword ptr [ebp-0C4h]
}
函數(shù)返回的匯編代碼。
002C1E37 pop edi
002C1E38 pop esi
002C1E39 pop ebx
002C1E3A add esp,0C4h
002C1E40 cmp ebp,esp
002C1E42 call __RTC_CheckEsp (02C1230h)
002C1E47 mov esp,ebp
002C1E49 pop ebp
002C1E4A ret
總結:
如果用函數(shù)的話,會經(jīng)過以下幾個步驟:
- 函數(shù)調(diào)用。
- 計算。
- 函數(shù)返回。
根據(jù)上面的匯編代碼可以看出,函數(shù)調(diào)用和函數(shù)返回所用的匯編指令遠多于計算所用的匯編指令,這就導致函數(shù)調(diào)用和返回所用的時間遠多于計算所用的時間。而宏本質(zhì)是替換,不用進行函數(shù)調(diào)用和返回,所以這就是宏在實現(xiàn)小型計算工作時比函數(shù)快的原因。
- 更為重要的是函數(shù)的參數(shù)必須聲明為特定的類型。
所以函數(shù)只能在類型合適的表達式上使用。
而宏是類型無關的,宏可以適用于整形、長整型、浮點型等,可以用于 “>” 來比較的類型。
舉例:
下面為宏和函數(shù)實現(xiàn)的兩個數(shù)中找出較大值。
// 宏 #define MAX(x, y) ((x) > (y) ? (x) : (y)) // 函數(shù) int Max(int x, int y) { return ((x) > (y) ? (x) : (y)); }
可以看出,該函數(shù)只能對兩個int類型的數(shù)進行比較,而宏卻可以對兩個任意類型的數(shù)進行比較,這就使宏用起來更加的靈活。
2.宏的缺點
- 每次使用宏的時候,一份宏定義的代碼將插入到程序中。除非宏比較短,否則可能大幅度增加程序的長度。
- 宏是沒法調(diào)試的。
- 宏由于類型無關,也就不夠嚴謹。
- 宏可能會帶來運算符優(yōu)先級的問題,導致程容易出現(xiàn)錯。
3.宏的獨特性
宏有時候可以做函數(shù)做不到的事情。
比如:宏的參數(shù)可以出現(xiàn)類型,但是函數(shù)做不到。
#define MALLOC(num, type) (type*)malloc(num * sizeof(type)) int main() { // 使用 int* p1 = MALLOC(10, int); // 替換后為 int* p1 = (int*)malloc(10 * sizeof(int)); char* p2 = MALLOC(5, int); // 替換后為 int* p2 = (char*)malloc(5 * sizeof(char)); return 0; }
4.總結并整理宏和函數(shù)的區(qū)別
屬 性 | #define定義宏 | 函數(shù) |
代 碼 長 度 | 每次使用時,宏代碼都會被 插入到程序中。除了非常小的宏之外,程序的長度會大幅度增長 | 函數(shù)代碼只出現(xiàn)于一個地方;每次使用這個函數(shù)時,都調(diào)用那個地方的同一份代碼 |
執(zhí) 行 速 度 | 更快 | 存在函數(shù)的調(diào)用和返回的額外開銷,所以相對慢一些 |
操 作 符 優(yōu) 先 級 | 宏參數(shù)的求值是在所有周圍表達式的上下文環(huán)境里,除非加上括號,否則鄰近操作符的優(yōu)先級可能會產(chǎn)生不可預料的后果,所以建議宏在書寫的時候多些括號。 | 函數(shù)參數(shù)只在函數(shù)調(diào)用的時候求值一次,它的結果值傳遞給函數(shù)。表達式的求值結果更容易預 測。 |
帶 有 副 作 用 的 參 數(shù) | 參數(shù)可能被替換到宏體中的多個位置,所以帶有副作用的參數(shù)求值可能會產(chǎn)生不可預料的結果。 | 函數(shù)參數(shù)只在傳參的時候求值一 次,結果更容易控制。 |
參 數(shù) 類 型 | 宏的參數(shù)與類型無關,只要對參數(shù)的操作是合法的, 它就可以使用于任何參數(shù)類型。 | 函數(shù)的參數(shù)是與類型有關的,如果參數(shù)的類型不同,就需要不同的函數(shù),即使他們執(zhí)行的任務是 不同的。 |
調(diào) 試 | 宏是不方便調(diào)試的 | 函數(shù)是可以逐語句調(diào)試的 |
遞 歸 | 宏是不能遞歸的 | 函數(shù)是可以遞歸的 |
5.有沒有宏和函數(shù)的結合體
答案是當然有。
在C99和C++里面都有一個東西叫做內(nèi)聯(lián)函數(shù)(inline)
內(nèi)聯(lián)函數(shù)既有函數(shù)的優(yōu)點又有宏的優(yōu)點:
- 宏的優(yōu)點:內(nèi)聯(lián)函數(shù)沒有函數(shù)的調(diào)用和返回。
- 函數(shù)的優(yōu)點: 內(nèi)聯(lián)函數(shù)本身是個函數(shù),它沒有參數(shù)優(yōu)先級、副作用等宏的缺點。
提示:由于本篇文章主要是講宏和函數(shù)的區(qū)別,內(nèi)聯(lián)函數(shù)就不多講,這里只做了解,后面我會單獨寫一篇文章來講解內(nèi)聯(lián)函數(shù)的。
二、宏和函數(shù)的命名約定
一般來講函數(shù)的宏的使用語法很相似。所以語言本身沒法幫我們區(qū)分二者。
那么我們平時就應該有一個良好的書寫習慣:
把宏名全部大寫
函數(shù)名不要全部大寫
這里可以參考《高質(zhì)量C/C++編程指南》這本書,有興趣的小伙伴可以去看看。
到此這篇關于C/C++細數(shù)宏與函數(shù)有那些區(qū)別的文章就介紹到這了,更多相關C/C++宏與函數(shù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
C語言實現(xiàn)數(shù)據(jù)結構串(堆分配存儲表示法)實例詳解
這篇文章主要介紹了C語言實現(xiàn)數(shù)據(jù)結構串(堆分配存儲表示法)實例詳解的相關資料,需要的朋友可以參考下2017-07-07vc中SendMessage自定義消息函數(shù)用法實例
這篇文章主要介紹了vc中SendMessage自定義消息函數(shù)用法,以實例實行詳細講述了SendMessage的定義、原理與用法,具有一定的實用價值,需要的朋友可以參考下2014-10-10