免費咨詢刑事辯護(hù)在線律師廣西seo快速排名
文章目錄
- 在#define中使用參數(shù)
- 參考資料
在#define中使用參數(shù)
在#define中使用參數(shù)可以創(chuàng)建外形和作用與函數(shù)類似的類函數(shù)宏。帶有
參數(shù)的宏看上去很像函數(shù),因為這樣的宏也使用圓括號。類函數(shù)宏定義的圓
括號中可以有一個或多個參數(shù),隨后這些參數(shù)出現(xiàn)在替換體中,如圖所
示。
下面是一個類函數(shù)宏的示例:
#define SQUARE(X) X*X
在程序中可以這樣用:
z = SQUARE(2);
這看上去像函數(shù)調(diào)用,但是它的行為和函數(shù)調(diào)用完全不同。程序示了類函數(shù)宏和另一個宏的用法。該示例中有一些陷阱,請仔細(xì)閱讀。
/* mac_arg.c -- 帶參數(shù)的宏 */
#include <stdio.h>
#define SQUARE(X) X*X
#define PR(X) printf("The result is %d.\n", X)
int main(void)
{
int x = 5;
int z;
printf("x = %d\n", x);
z = SQUARE(x);
printf("Evaluating SQUARE(x): ");
PR(z);
z = SQUARE(2);
printf("Evaluating SQUARE(2): ");
PR(z);
printf("Evaluating SQUARE(x+2): ");
PR(SQUARE(x + 2));
printf("Evaluating 100/SQUARE(2): ");
PR(100 / SQUARE(2));
printf("x is %d.\n", x);
printf("Evaluating SQUARE(++x): ");
PR(SQUARE(++x));
printf("After incrementing, x is %x.\n", x);
return 0;
}
這里,SQUARE 是宏標(biāo)識符,SQUARE(X)中的 X 是宏參數(shù),X * X 是替
換列表。程序清單 16.2 中出現(xiàn)SQUARE(X)的地方都會被X * X替換。這與前
面的示例不同,使用該宏時,既可以用X,也可以用其他符號。宏定義中的
X由宏調(diào)用中的符號代替。因此,SQUARE(2)替換為2 * 2,X實際上起到參數(shù)
的作用。
然而,稍后你將看到,宏參數(shù)與函數(shù)參數(shù)不完全相同。下面是程序的輸
出。注意有些內(nèi)容可能與我們的預(yù)期不符。實際上,你的編譯器輸出甚至與
下面的結(jié)果完全不同。
x = 5
Evaluating SQUARE(x): The result is 25.
Evaluating SQUARE(2): The result is 4.
Evaluating SQUARE(x+2): The result is 17.
Evaluating 100/SQUARE(2): The result is 100.
x is 5.
Evaluating SQUARE(++x): The result is 42.
After incrementing, x is 7.
前兩行與預(yù)期相符,但是接下來的結(jié)果有點奇怪。程序中設(shè)置x的值為
5,你可能認(rèn)為SQUARE(x+2)應(yīng)該是 7 * 7,即 49。
但是,輸出的結(jié)果是 17,這不是一個平方值!導(dǎo)致這樣結(jié)果的原因是,我們前面提到過,預(yù)處理器不做計算、不求值,只替換字符序列。預(yù)處理器把出現(xiàn)x的地方都替換成x+2。因此,x * x變成了x+2*x+2。如果x為5,那么該表達(dá)式的值為:
5+2*5+2 = 5 + 10 + 2 = 17
該例演示了函數(shù)調(diào)用和宏調(diào)用的重要區(qū)別。函數(shù)調(diào)用在程序運行時把參
數(shù)的值傳遞給函數(shù)。宏調(diào)用在編譯之前把參數(shù)記號傳遞給程序。這兩個不同
的過程發(fā)生在不同時期。是否可以修改宏定義讓SQUARE(x+2)得36?
當(dāng)然可以,要多加幾個圓括號:
#define SQUARE(x) (x)*(x)
現(xiàn)在SQUARE(x+2)變成了(x+2)*(x+2),在替換字符串中使用圓括號就得
到符合預(yù)期的乘法運算。
但是,這并未解決所有的問題。下面的輸出行:
100/SQUARE(2)
將變成:
100/2*2
根據(jù)優(yōu)先級規(guī)則,從左往右對表達(dá)式求值:
(100/2)2,即502,得100。把SQUARE(x)定義為下面的形式可以解決這種混亂:
#define SQUARE(x) (x*x)
這樣修改定義后得100/(2*2),即100/4,得25。
要處理前面的兩種情況,要這樣定義:
#define SQUARE(x) ((x)*(x))
因此,必要時要使用足夠多的圓括號來確保運算和結(jié)合的正確順序。
盡管如此,這樣做還是無法避免程序中最后一種情況的問題。
SQUARE(++x)變成了++x*++x,遞增了兩次x,一次在乘法運算之前,一次
在乘法運算之后:
++x*++x = 6*7 = 42
由于標(biāo)準(zhǔn)并未對這類運算規(guī)定順序,所以有些編譯器得 76。而有些編
譯器可能在乘法運算之前已經(jīng)遞增了x,所以77得49。在C標(biāo)準(zhǔn)中,對該表
達(dá)式求值的這種情況稱為未定義行為。無論哪種情況,x的開始值都是5,雖
然從代碼上看只遞增了一次,但是x的最終值是7。
解決這個問題最簡單的方法是,避免用++x 作為宏參數(shù)。一般而言,不
要在宏中使用遞增或遞減運算符。但是,++x可作為函數(shù)參數(shù),因為編譯器
會對++x求值得5后,再把5傳遞給函數(shù)。
參考資料
《C Primer Plus》