#define是C語言提供的宏命令,其主要目的是:在編程時,為程序員提供一定方便,並能在一定程度上提高程序的執行效率。#define將一個標示符定義為一個字符串,該標示符被稱為宏,被定義的字符串稱為字符替換文本。宏定義有兩種形式:一種是簡單宏定義(即字面值),另一種是帶參數宏定義(即通常說的偽函數)
第一種:#define <宏名> <字符串> #define PI 3.1415926
一般,宏名用大寫字母表示,但這並非規定,也可以小寫,從編碼一致性,可讀性角度考慮,強烈建議宏名統一采用大寫字母表示
使用宏名代替一個字符串,可以減少程序中重復書寫某些字符串的工作量,同時也避免了由於疏忽導致的字符串書寫錯誤問題
宏定義是用宏名代替一個字符串,也就是做簡單的置換,並不做正確性檢查。如寫成:#define PI 3.1415926(把1錯寫成l),在預編譯時,不會做任何語法檢查,在編譯宏展開后的源程序時,才會發現語法錯誤並報告
宏定義不是C語句,不必在行尾加分號,如果加了分號,將連同分號一起被替換
在程序中,如果#define出現在函數外面,宏的有效范圍為宏定義到本源文件結束,通常宏定義寫在文件開頭,在此文件范圍內有效。另外,用#undef可終止宏定義的作用域,這樣可以靈活控制宏定義的作用范圍
在進行宏定義時,可以用已經定義的宏名,可層層置換,但對於用引號引起來的字符串內的字符,即使與宏名相同,也不進行替換
宏定義是專門用於預處理的專用名詞,它與定義變量的含義不同,只做字符替換,不分配內存空間
在C語言中,通過簡單的宏定義恆值常量,是C語言中定義恆值的唯一手段,但在C++中這並不是唯一的方法,C++中的const也可以定義一個恆值常量。
二者區別如下
1.const定義的常量有數據類型,而#define定義的常量無數據類型
2.有些高度程序可以對const進行調試,但無法對#define進行調試
3.當定義局部變量時,const作用域僅限於定義局部變量的函數體。但#define不是,而是從定義點到整個程序的結束點。但可用#undef取消其定義,從而限定其作用域范圍
4.const還可修飾函數形式參數,返回值和類的成員函數等,從而提高函數的健壯性。因為const修飾的內容必須受C/C++的靜態類型安全檢查機制的強制保護,可防止意外修改
帶參數的宏定義形式為:
#define<宏名> (<參數表>) <宏體> 例如:#define MAX(x,y) (x)>(y)?(x):(y)
標示符被定義為宏后,該標識符便是一個宏名,在程序中,出現宏名的地方在程序被編譯前,先將宏名用被定義的字符串替換,稱為宏替換,替換后才進行編譯,宏替換是簡單的字符替換
帶參數宏展開,只是將語句中宏名后面括號內的實參字符串替換#define宏定義中的形式參數
宏定義時,宏名與包含宏參數的括號之間不應加空格,否則編譯器會將空格后的字符都作為替換字符串的一部分
帶參數的宏與函數區別如下
1.函數調用時,先求實參表達式的值,然后傳遞給形參,而使用帶參數的宏只是進行簡單的字符串替換,不進行表達式求值
2.函數調用是在程序運行時處理的,為形參分配臨時的內存空間,而宏展開是在編譯前進行的,在展開時並不分配內存單元,不進行值的傳遞,同樣也沒有“返回值”的概念
3.函數的實參和形參都必須定義類型,二者的類型要求一致,如不一致,則要進行類型轉換,而宏不存在類型問題,因為宏名無類型,它的參數也無類型,僅是字符串替換,展開時代入指定的字符串即可。宏定義時,字符串可以是任何類型的數據
4.調用函數只會獲得一個返回值,而宏則不然,可設法得到幾個返回值
5.宏展開會促使源程序變長,因為每次展開都使程序增長,而函數調用不會增加代碼長度
6.宏替換不占用運行時間,只占用編譯時間,而函數調用則占用運行時間用於分配單元,保存現場,值傳遞,返回等。所以宏替換可以提供程序的執行效率,但編譯時間會變長
宏的引入帶來的好處
1.宏定義的引入可方便程序的修改。使用簡單宏定義,可用宏替換那些在程序中經常使用的常量,當需要修改該常量時,不用對整個程序進行修改,只修改宏定義的字符串即可,而且當常量比較長時,可以用較短,有意義的標識符來編寫程序,這樣更方便
2.提高程序的運行效率
使用帶參數的宏定義既可完成函數調用的功能,又能減少系統開銷,提高運行效率。宏定義是在預處理階段就進行了宏展開,在執行時不需要替換。宏定義可完成簡單的操作,但復雜的操作還是要由函數來完成,而且宏定義所占用的目標代碼空間相對較大,所以在使用時要依據具體情況決定是否使用宏定義。
3.#define預處理完全沒有把C++的作用域納入考量,絕大多數C++實現都是封裝在命名空間里,這樣的做法有很多優點。但不幸的是,#define的作用域並未被限定在名字空間里
4.宏還可以用於條件編譯,例如
#ifdef WINDOWS
...
#endif
#ifdef LINUX
...
#endif
在應用時,如果要定義一個字面值常量建議使用const替換#define
宏要注意的問題
1.由操作符優先級引起的問題
由於宏只是簡單的替換,宏的參數如果是復合結構,那么通過替換之后可能由於各個參數之間的操作符優先級高於單個參數內部各部分之間相互作用的操作符優先級,如果不用括號保護各個宏參數,就會產生意想不到的情況
如#define ceil_div(x,y) (x+y-1)/y
那么a = ceil_div(b&c,sizeof(int))將被轉化為a=(b&c+sizeof(int)-1)/sizeof(int)//由於+/-的優先級高於&的優先級,那么上面的式子等同於
a= (b&(c+sizeof(int)-1))/sizeof(int);
所以應改為#define ceil_div(x,y) (((x)+(y)-1)/(y))
2.使用宏定義,不允許參數發生變化
例如:
#include<stdio.h> int main()
#define sqrt(a) ((a)*(a)) {
int fsqrt(int a) int a = 10,b=10;
{ int r1,r2;
return a*a; r1 = sqrt(a++);
} r2 = fsqrt(b++);
printf("a=%d,b=%d,r1=%d,r2=%d\n",a,b,r1,r2);
return 0;
}
這段程序的最終結果是a=12,b=11,r1=100,r2=100,這所以a=12,是因為在替換的時候,a++被執行了兩次。要避免這種行為,就要使宏參數不發生變化,如a++;r1=sqrt(a)