6-3 宏定義
發表時間:2020-10-19
發布人:葵宇科技
浏覽次數:37
6.3.1 宏定義的規則和(hé)使用解析
(1)宏定義的解析規則就是:在預處理階段由預處理器(qì)來進行替換,這個(gè)替換是原封不動(dòng)的替換。
(2)宏定義替換會遞歸進行,直到替換出來的值本身不再是一個(gè)宏為止。
(3)一個(gè)正确的宏定義式子(zǐ)本身分為3部分:第一部分是#define,第二部分是宏名,剩下(xià)的為第三部分。
(4)宏可(kě)以帶參數,稱為帶參宏。帶參宏的使用和(hé)帶參函數非常像,但是使用上有一些差異。在定義帶參宏時,每一個(gè)參數在宏體中(zhōng)引用時都必須加括号,最後整體再加括号,括号缺一不可(kě)。
#include <stdio.h>
#define X(a,b) ((a)+(b))
int main()
{
int a = 1,b = 2;
int c =3 * X(a,b);
printf("c = %d\n",c);
return 0;
}
6.3.2 宏定義示例1:MAX,求2個(gè)數中(zhōng)較大的一個(gè)
#define MAX(a,b) (((a) > (b)) ? (a) : (b))
關(guān)鍵:
第一點:要想到使用三目運算符來完成。
第二點:注意括号的使用。
6.3.3 宏定義示例2:SEC_PER_YEAR,用宏定義表示一年有多少(shǎo)秒
#define SEC_PER_YEAR (365*24*60*60UL)
關(guān)鍵:
第一點:當一個(gè)數字直接出現在程序中(zhōng)時,它的類型默認是int;
第二點:一年有多少(shǎo)秒,這個(gè)數字剛好超過了int類型存儲的範圍
6.3.4 帶參宏和(hé)帶參函數的區别(宏定義的缺陷)
(1)宏定義是在預處理期間處理的,而函數是在編譯期間處理的。這個(gè)區别帶來的實質差異是:宏定義最終是在調用宏的地方把宏體原地展開,而函數是在調用函數處跳轉到函數中(zhōng)去執行,執行完後再跳轉回來。
注意:宏定義和(hé)函數的最大差别就是:宏定義是原地展開,因此沒有調用開銷;而函數是跳轉執行再返回,因此函數有比較大的調用開銷。所以宏定義和(hé)函數相比,優勢就是沒有調用開銷,沒有傳參開銷,所以當函數體很短(duǎn)(尤其是隻有一句話時)可(kě)以用宏定義來替代,這樣效率比較高。
(2)帶參宏和(hé)帶參函數的一個(gè)重要差别就是:宏定義不會檢查參數的類型,返回值也不會附帶類型;而函數有明确的參數類型和(hé)返回值類型。當我們調用函數時編譯器(qì)會幫我們做參數的靜态類型檢查,如(rú)果編譯器(qì)發現我們實際傳參和(hé)參數聲明不同時會報警告或錯誤。
注意:用函數的時候程序員不太用操心類型不匹配,因為編譯器(qì)會檢查,如(rú)果不匹配編譯器(qì)會叫;用宏的時候程序員必須很注意實際傳參和(hé)宏所希望的參數類型一緻,否則可(kě)能編譯不報錯但是運行有誤。
總結:宏和(hé)函數各有千秋,各有優劣。總的來說,如(rú)果代碼比較多用函數适合而且不影響效率;但是對于那些隻有一兩句話的函數開銷就太大了,适合用帶參宏。但是用帶參宏又有缺點:不檢查參數類型。
6.3.5 内聯函數和(hé)inline關(guān)鍵字
(1)内聯函數通(tōng)過在函數定義前加inline關(guān)鍵字實現。
(2)内聯函數本質上是函數,所以有函數的優點(内聯函數是編譯器(qì)負責處理的,編譯器(qì)可(kě)以幫我們做參數的靜态類型檢查);但是它同時也有帶參宏的優點(不用調用開銷,而是原地展開)。所以幾乎可(kě)以這樣認為:内聯函數就是帶了參數靜态類型檢查的宏。
(3)當我們的函數内函數體很短(duǎn)(譬如(rú)隻有一兩句話)的時候,我們又希望利用編譯器(qì)的參數類型檢查來排錯,我還希望沒有調用開銷時,最适合用内聯函數。
6.3.6 宏定義來實現條件編譯(#define #undef #ifdef)
(1)程序有DEBUG和(hé)RELEASE版本,區别就是編譯時有無定義DEBUG宏。
#include <stdio.h>
//這兩行代碼的效果加在一起就相當于沒有定義DEBUG
//注銷一個(gè)宏。如(rú)果前面有定義這個(gè)宏則取消這個(gè)宏,如(rú)果沒有就無視掉
#define DEBUG
#undef DEBUG
#ifdef DEBUG
#define debug(x) printf(x)
#else
#define debug(x)
#endif
int main(void)
{
debug("this is a debug info\n");
return 0;
}