Summary
1)#define
是前處理器
處理的單元實體之一,在預編譯期進行文字替換
2)#define
定義的巨集可以出現在程式的任意地方
,定義之後的程式碼都可以使用
3)#define
可以定義巨集常量,本質上是字面量
4)define
可以定義表示式,使用上類似函式
;功能可能更強大
(型別可以作為引數、求陣列大小);更容易出錯
(當和其他運算混合在一起時)
5)巨集由於是直接文字替換,所以沒有任何的呼叫開銷
;巨集表示式裡不能出現遞迴
;巨集被前處理器處理,所以編譯器不知道巨集的存在
,自然巨集也不會有作用域的概念
,作用域是針對變數和函式的
。
6)常用的預定義巨集
巨集 | 含義 | 示例 |
__FILE__ | 被編譯的檔名 | file1.c |
__LINE__ | 當前行號 | 25 |
__DATE__ | 編譯時的日期 | Jan 31 2012 |
__TIME__ | 編譯時的時間 | 17:01:01 |
__STDC__ | 編譯器是否遵循標準C規範 | 1 |
巨集定義與使用分析
#define
是前處理器
處理的單元實體之一#define
定義的巨集可以出現在程式的任意位置
#define
定義之後的程式碼都可以使用這個巨集
1、#define定義的巨集常量
#define
定義的巨集常量可以直接使用,本質為字面量
(不會佔用記憶體,字串字面量會存在只讀儲存區)
// test.c中 下面的巨集定義正確麼?
#define ERROR -1
#define PATH1 "D:\test\test.c"
#define PATH2 D:\test\test.c
#define PATH3 D:\test\
test.c
int main()
{
int i = ERROR;
char* p1 = PATH1;
char* p2 = PATH2;
char* p3 = PATH3;
return 0;
}
單步編譯
gcc -E test.c -o test.i ==> 預編譯,生成中間檔案.i
gcc -S test.i -o test.s ==> 編譯,生成彙編檔案.s
分析:在第一步預編譯階段,處理define並生成.i檔案,這時候不會出錯;下一步編譯階段,將中間檔案轉換為彙編檔案時,就會報錯。
原因在於:在預編譯的階段,僅僅是做文字替換,沒有語法檢查
;到了編譯階段,會對替換後的.i檔案進行語法檢查
,再生成彙編檔案,這時候語法檢查就出錯了。
2、#define定義的表示式
#define
表示式的使用類似函式呼叫
#define
表示式可以比函式更強大
#define
表示式比函式更易出錯
#define SUM(a, b) (a) + (b)
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define DIM(a) sizeof(a) / sizeof(*a)
int main()
{
int a = 1;
int b = 3;
int c[2] = {0};
// expected: 4, 1, 2
printf("SUM(a, b) = %d\n", SUM(a, b));
printf("MIN(a, b) = %d\n", MIN(a, b));
printf("size of array = %d\n", DIM(c));
// unexpected: 3
printf("unexpected: %d", MIN(++a, b)); // 期望得到++a和b中的較小的值:2
return 0;
}
分析:
- define表示式
類似
函式呼叫,像上面的例子中,printf中巨集的定義就很像是函式呼叫。 - define表示式可能
比函式更強大
,如上,DIM巨集可以求陣列的大小
,但是在C語言中無法通過函式求一個陣列的大小
,因為當陣列作為函式的引數時,會退化成指標
;再比如,C語言中不能把型別作為引數
,但是巨集就可以。 - define表示式
更容易出錯
,如上,MIN(++a, b)期望得到的值為2,實際得到的值卻是3。由單步編譯得到的結果可以看出,替換後的表示式為((++a) < (b) ? (++b) : (b))
,前置的++被執行了2次,所以得到了3。預編譯器就像是一個傳話筒,在傳話的過程中,就發生了歧義
巨集表示式被前處理器
處理,編譯器並不知道巨集的存在;
巨集表示式用“實參”完全替代形參,不進行任何運算
;
巨集表示式沒有任何的呼叫開銷
:(因為直接進行文字替換,不像函式需要引數入棧、返回等開銷)
巨集表示式中不能出現遞迴定義:(因為巨集只在預處理期進行一次
文字替換,後續的符號編譯器就不認識了)
#define SUM(n) ((n>0) ? (SUM(n-1) + n) : 0)
int s = SUM(10); // 編譯的時候就會報錯,undefined reference SUM
3、巨集定義的常量或表示式是否有作用域限制?
下面的程式合法麼?
void def()
{
#define PI 3.1415926
#define AREA(r) r*r*PI
}
double area(double r)
{
retrun AREA(r);
}
int main()
{
double r = area(10);
return 0;
}
分析:編譯、執行都不會出錯。說明巨集是沒有作用域限制的
。只要定義完,後面的程式碼都可以用。在定義之前使用就會報錯undefined reference。作用域只是針對變數和函式的
,巨集在預處理期被展開,在編譯期已經沒有巨集這個東西了
,所以也就沒法給巨集限定作用域了。
4、 常用的預定義巨集
巨集 | 含義 | 示例 |
__FILE__ | 被編譯的檔名 | file1.c |
__LINE__ | 當前行號 | 25 |
__DATE__ | 編譯時的日期 | Jan 31 2012 |
__TIME__ | 編譯時的時間 | 17:01:01 |
__STDC__ | 編譯器是否遵循標準C規範 | 1 |
本文總結自“狄泰軟體學院”唐佐林老師《C語言進階課程》。
如有錯漏之處,懇請指正。