C语言常用宏定义(#define)使用方法
· ?正 ?· ?文 ?· ?來 ?· ?啦 ?·
前言
------在上篇文章里面,我們分析了預處理的一個完整過程,這能夠讓我們理解一個寫好的程序,在生成一個可執行文件,到底發生了什么,對我們在大型工程項目里面有助于對程序的理解;今天我們繼續接著上篇文章的基礎上,來分享有關c語言里面關于宏定義的用法!
宏定義基本語法
每個#define行(即邏輯行)由三部分組成:第一部分是指令 #define 自身,“#”表示這是一條預處理命令,“define”為宏命令。第二部分為宏(macro),一般為縮略語,其名稱(宏名)一般大寫,而且不能有空格,遵循C變量命令規則。第三部分“替換文本”可以是任意常數、表達式、字符串等。在預處理工作過程中,代碼中所有出現的“宏名”,都會被“替換文本”替換。這個替換的過程被稱為“宏代換”或“宏展開”(macro expansion)。“宏代換”是由預處理程序自動完成的。在C語言中,“宏”分為兩種:無參數 和 有參數(這里有參數先不舉例子,下面具體分析的話,讀者可以詳細看到示例來理解這個)。下面是宏定義的基本形式:
?????#define???宏名?????宏體注意:宏體后面不要加分號“;”,這個在寫代碼的時候要小心點哦宏定義的優點和缺點
------優點:
1、方便程序的修改:
? ? ? 使用簡單宏定義可用宏代替一個在程序中經常使用的常量,這樣在將該常量改變時,不用對整個程序進行修改,只修改宏定義的字符串即可,而且當常量比較長時, 我們可以用較短的有意義的標識符來寫程序,這樣更方便一些(特別當跨平臺的時候,要修改程序一些參數的時候,用宏定義的話,只需要修改宏定義的宏名就可以代表修改了整個程序里面用到這個宏名,就不用一個個去改了,極大的提升了工作效率!)。
2、提高程序的運行效率:
? ? ?
? ? ?? 這里我們就拿帶參宏和函數來對比了:
? ? ? (1)宏定義是在預處理期間處理的,而函數是在編譯期間處理的。這個區別帶來的實質差異是:宏定義最終是在調用宏的地方把宏體原地展開,而函數是在調用函數處跳轉到函數中去執行,執行完后再跳轉回來。
注:宏定義和函數的最大差別就是:宏定義是原地展開,因此沒有調用開銷;而函數是跳轉執行再返回,因此函數有比較大的調用開銷。所以宏定義和函數相比,優勢就是沒有調用開銷,沒有傳參開銷,所以當函數體很短(尤其是只有一句話時)可以用宏定義來替代,這樣效率高。
? ? ?(2)帶參宏和帶參函數的一個重要差別就是:宏定義不會檢查參數的類型,返回值也不會附帶類型;而函數有明確的參數類型和返回值類型。當我們調用函數時編譯器會幫我們做參數的靜態類型檢查,如果編譯器發現我們實際傳參和參數聲明不同時會報警告或錯誤。
注:用函數的時候程序員不太用操心類型不匹配因為編譯器會檢查,如果不匹配編譯器會警告(但是實際測試并沒有警告,理論上是有的);用宏的時候程序員必須很注意實際傳參和宏所希望的參數類型一致,否則可能編譯不報錯但是運行有誤(一般所希望的是整型數據類型,不然結果一般會出錯,下面的例子就是);而且最好在宏體里面每個參數都帶小括號,因為有時候會涉及到運算符號的優先級問題,這樣一來寫程序的話就不會引起bug了。
#include?<stdio.h>#define?MAX(a,?b)?(((a)>(b))???(a)?:?(b))int?max(int?a,?int?b) {if?(a?>?b)return?a;elsereturn?b; }int?main(void) {float?a,?b,?c;a?=?1.5;b?=?4.7;c?=?MAX(a,?b);??????????????????????????//?展開后:c =?(((a)>(b)) ? (a)?:?(b));printf("c?=?%d.\n",?c);c?=?max(a,?b);??????????????????????????//?無法展開,只能調用printf("c?=?%d.\n",?c);return?0; }我們來看一下它預處理過后成了什么樣了:
#?2?"b.c"?2 #?6?"b.c" int?max(int?a,?int?b) {if?(a?>?b)return?a;elsereturn?b; }int?main(void) {float?a,?b,?c;a?=?1.5;b?=?4.7;c?=?(((a)>(b))???(a)?:?(b));printf("c?=?%d.\n",?c);c?=?max(a,?b);printf("c?=?%d.\n",?c);return?0; }演示結果(你會看到):
總結:宏和函數各有千秋,各有優劣。總的來說,如果代碼比較多用函數適合而且不影響效率;但是對于那些只有一兩句話的函數開銷就太大了,適合用帶參宏。但是用帶參宏又有缺點:不檢查參數類型。
------缺點:
由于是直接嵌入的,所以代碼可能相對多一點。
嵌套定義過多可能會影響程序的可讀性,而且很容易出錯,不容易調試。
對帶參的宏而言,由于是直接替換,并不會檢查參數是否合法,存在安全隱患。
宏定義的用法
1、嵌套宏的使用:
?#include?<stdio.h>#define?M????10??#define?N?????Mint?main(void){printf("the?M?is?%d\n",M);printf("the?N?is?%d\n",N);return?0;}預處理之后:
演示結果(簡單來講嵌套宏說白了還是直接替換的作用):
2、#運算符:
? ? ??出現在宏定義中的#運算符把跟在其后的參數轉換成一個字符串。有時把這種用法的#稱為字符串化運算符。例如:
#include?<stdio.h>#define??M(n)???"hhh"#n????????int?main(void) {printf("the?M(6)?is?%s\n",M(6));return?0;}演示結果:
3、##運算符:
? ? ??##運算符用于把參數連接到一起。預處理程序把出現在##兩側的參數合并成一個符號。看下面的例子:
#include?<stdio.h>#define??M(a,b,c)??????????a##b##cint?main(void) {printf("the?M(2,3,4)?is?%d\n",M(2,3,4));return?0;}演示結果:
the?M(2,3,4)?is?2344、宏定義中使用了do{}? while(0) (這種形式在代碼還是經常能看的到的,下面我還是用例子來慢慢引導大家來看懂這用這個的含義):
?#include?<stdio.h>#define??M(n)??\printf("the?n?is?%d\n",n);\printf("the?M(n)?is?%d\n",n);int?main(void){int?n=8;int?a=1;if(a)M(n);return?0;}預處理后(你可以看到這樣一來,第二條語句就沒有在我們if語句的范圍內了,而且讀者應該注意到,帶參宏有點像函數調用,調用這個也是一條語句,所以語句后面加了“;”,這里在實際編譯過程中是多加了,會導致編譯報錯;但是不在這條語句后面加的話,就不像一條語句了,不過它是可以編譯通過的,下面改進后的程序就是這種情況):
5、可變宏的使用:
? ??? C99中規定宏可以像函數一樣帶有可變參數,實現思想就是宏定義中參數列表的最后一個參數為省略號(也就是三個英文輸入法下的句號)。這樣預定義宏__VA_ARGS__就可以被用在替換部分中,以表明省略號代表什么:
#include<stdio.h> #define?Variable_Macro(...)???printf(__VA_ARGS__) int?main(void) {Variable_Macro("This?is?a?variable?macro?test...\n");Variable_Macro("My?age?is?%d",22);return?0; }預處理后:
???#?3?"b.c"int?main(void){printf("This?is?a?variable?macro?test...\n");printf("My?age?is?%d",22);return?0;}演示結果:
?
?This?is?a?variable?macro?test...My?age?is?22注意:帶參宏后面不能再有參數,而我們的帶參函數前面必須要有參數(這里我就不舉例子關于帶參函數了):
#include<stdio.h> #define?Variable_Macro(...,a)???printf(__VA_ARGS__) int?main(void) {Variable_Macro("This?is?a?variable?macro?test...\n");Variable_Macro("My?age?is?%d",22);return?0; }演示結果:
? ?
?root@ubuntu-virtual-machine:/home/ubuntu#?gcc?b.cb.c:2:27:?error:?missing?')'?in?macro?parameter?list#define?Variable_Macro(...,a)???printf(__VA_ARGS__)^你點的每個贊,我都當成喜歡
總結
以上是生活随笔為你收集整理的C语言常用宏定义(#define)使用方法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【工具】更新云文档办公利器汇总,腾讯文档
- 下一篇: 美丽的童话故事都是从城堡开始...