#define 識別符號 替換列表
替換列表可以是數值常數、字元常數、字串常數等,故可以把宏定義理解為使用識別符號表示一常數,或稱符號常數。#define PI 3.1416 //正確,該行#前允許有空格 int a;#define N 5 //錯誤,該行#前不允許有空格外的其他字元
#define N =5 //雖語法正確,但前處理器會把N替換成=5 int a[N]; //錯誤,因為宏替換之後為 int a[=5];宏定義不是語句,是預處理指令,故結尾不加分號。如果不小心新增了分號,雖然有時該宏定義沒問題,但在宏替換時,可能導致 C 語法錯誤,或得不到預期結果。例如:
#define N 5; //雖語法正確,但會把N替換成5; int a[N]; //語法錯誤,宏替換後,為int a[5;];錯誤
#define N 3+2 int r=N*N;宏替換後為:
int r=3+2*3+2; //r=11如果採用如下形式的宏定義:
#define N (3+2) int r=N*N;則宏替換後,為:
int r=(3+2)*(3+2); //r=25
#define USA "The United States of America"該宏定義中替換列表為字串常數,如果該串較長,或為了使替換列表的結構更清晰,可使用續行符 把該串分若干行來寫,除最後一行外,每行行尾都必須加續行符 。
printf("%sn",USA);則輸出結果為:The United States of America
#define 識別符號(引數1,引數2,...,引數n) 替換列表
例如,求兩個引數中最大值的帶參宏定義為:#define MAX(a,b) ((a)>(b)?(a) : (b))當有如下語句時:
int c=MAX(5,3);前處理器會將帶引數的宏替換成如下形式:
int c=((5)>(3)?(5) : (3));故計算結果c=5。
#undef 識別符號
說明:#define MAX (a,b) ( (a) > (b) ? (a) : (b) ) //錯誤的帶參宏定義格式2) 宏替換列表中每個引數及整個替換列表,都必須用一對小括號 () 括起來,否則可能會出現歧義。
#include <stdio.h> #define MUL(a,b) (a*b) int main (void) { int c; c=MUL(3,5+1); printf("c=%dn",c); return 0; }分析:
#include <stdio.h> #define MUL(a,b) ((a)*(b))//修改處1 int main (void) { int c; c=MUL(3,(5+1);//修改處2 printf("c=%dn",c); return 0; }
接下來將從呼叫發生時間、引數型別檢查、引數是否需要空間、執行速度等幾個主要方面進行對比分析帶參宏定義與函數呼叫的差異。
在源程式進行編譯之前,即預處理階段進行宏替換;而函數呼叫則發生在程式執行期間。
函數引數型別檢查嚴格。程式在編譯階段,需要檢查實參與形參個數是否相等及型別是否匹配或相容,若引數個數不相同或型別不相容,則會編譯不通過。
在預處理階段,對帶參宏呼叫中的引數不做檢查。即宏定義時不需要指定引數型別,既可以認為這是宏的優點,即適用於多種資料型別,又可以認為這是宏的一個缺點,即型別不安全。故在宏呼叫時,需要程式設計者自行確保宏呼叫引數的型別正確。
函數呼叫時,需要為形參分配空間,並把實參的值複製一份賦給形參分配的空間中。而宏替換,僅是簡單的文字替換,且替換完就把宏名對應識別符號刪除掉,即不需要分配空間。
函數在編譯階段需要檢查引數個數是否相同、型別等是否匹配等多個語法,而宏替換僅 是簡單文字替換,不做任何語法或邏輯檢查。
函數在執行階段引數需入棧和出棧操作,速度相對較慢。
由於宏替換是文字替換,即如果需替換的文字較長,則替換後會影響程式碼長度;而函數不會影響程式碼長度。
故使用較頻繁且程式碼量較小的功能,一般採用宏定義的形式,比採用函數形式更合適。前面章節頻繁使用的 getchar(),準確地說,是宏而非函數。
為了使該宏呼叫像函數呼叫,故把該宏設計成了帶引數的宏定義:
#define getchar() getc(stdin)
故呼叫該宏時,需要加括號,即傳空引數:getchar()。