網(wǎng)站首頁 編程語言 正文
一、宏和函數(shù)的對比
1.宏的優(yōu)點
- 宏通常被應用于執(zhí)行簡單的運算。
- 比如在兩個數(shù)中找出較大的一個。
#define MAX(a, b) ((a)>(b)?(a):(b))
那為什么不用函數(shù)來完成這個任務?
原因有兩點:
用于調用函數(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;
}
轉到反匯編,查看匯編代碼。
?? ?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;
}
調用函數(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) ?// 調用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) ?
// 下面這些才是計算的代碼,上面這些代碼可以說還是在為調用函數(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ù)調用。
- 計算。
- 函數(shù)返回。
根據(jù)上面的匯編代碼可以看出,函數(shù)調用和函數(shù)返回所用的匯編指令遠多于計算所用的匯編指令,這就導致函數(shù)調用和返回所用的時間遠多于計算所用的時間。而宏本質是替換,不用進行函數(shù)調用和返回,所以這就是宏在實現(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.宏的缺點
- 每次使用宏的時候,一份宏定義的代碼將插入到程序中。除非宏比較短,否則可能大幅度增加程序的長度。
- 宏是沒法調試的。
- 宏由于類型無關,也就不夠嚴謹。
- 宏可能會帶來運算符優(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ù)時,都調用那個地方的同一份代碼 |
執(zhí) 行 速 度 | 更快 | 存在函數(shù)的調用和返回的額外開銷,所以相對慢一些 |
操 作 符 優(yōu) 先 級 | 宏參數(shù)的求值是在所有周圍表達式的上下文環(huán)境里,除非加上括號,否則鄰近操作符的優(yōu)先級可能會產(chǎn)生不可預料的后果,所以建議宏在書寫的時候多些括號。 | 函數(shù)參數(shù)只在函數(shù)調用的時候求值一次,它的結果值傳遞給函數(shù)。表達式的求值結果更容易預 測。 |
帶 有 副 作 用 的 參 數(shù) | 參數(shù)可能被替換到宏體中的多個位置,所以帶有副作用的參數(shù)求值可能會產(chǎn)生不可預料的結果。 | 函數(shù)參數(shù)只在傳參的時候求值一 次,結果更容易控制。 |
參 數(shù) 類 型 | 宏的參數(shù)與類型無關,只要對參數(shù)的操作是合法的, 它就可以使用于任何參數(shù)類型。 | 函數(shù)的參數(shù)是與類型有關的,如果參數(shù)的類型不同,就需要不同的函數(shù),即使他們執(zhí)行的任務是 不同的。 |
調 試 | 宏是不方便調試的 | 函數(shù)是可以逐語句調試的 |
遞 歸 | 宏是不能遞歸的 | 函數(shù)是可以遞歸的 |
5.有沒有宏和函數(shù)的結合體
答案是當然有。
在C99和C++里面都有一個東西叫做內聯(lián)函數(shù)(inline)
內聯(lián)函數(shù)既有函數(shù)的優(yōu)點又有宏的優(yōu)點:
- 宏的優(yōu)點:內聯(lián)函數(shù)沒有函數(shù)的調用和返回。
- 函數(shù)的優(yōu)點: 內聯(lián)函數(shù)本身是個函數(shù),它沒有參數(shù)優(yōu)先級、副作用等宏的缺點。
提示:由于本篇文章主要是講宏和函數(shù)的區(qū)別,內聯(lián)函數(shù)就不多講,這里只做了解,后面我會單獨寫一篇文章來講解內聯(lián)函數(shù)的。
二、宏和函數(shù)的命名約定
一般來講函數(shù)的宏的使用語法很相似。所以語言本身沒法幫我們區(qū)分二者。
那么我們平時就應該有一個良好的書寫習慣:
把宏名全部大寫
函數(shù)名不要全部大寫
這里可以參考《高質量C/C++編程指南》這本書,有興趣的小伙伴可以去看看。
原文鏈接:https://blog.csdn.net/qq_64042727/article/details/127327644
相關推薦
- 2022-04-08 關于Kotlin委托你必須重視的幾個點_Android
- 2021-12-11 Docker容器編譯LNMP的實現(xiàn)示例_docker
- 2022-03-26 C語言中指針常量和常量指針的區(qū)別_C 語言
- 2023-06-21 Flutter學習之Navigator的高級用法分享_Android
- 2022-05-12 uni-app混合原生安卓開發(fā)
- 2022-12-04 React條件渲染實例講解使用_React
- 2023-01-05 基于Go語言實現(xiàn)冒泡排序算法_Golang
- 2022-03-26 C語言實現(xiàn)簡單的猜數(shù)字游戲_C 語言
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細win安裝深度學習環(huán)境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結構-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支