用一個巨集實現求兩個數中的最大數
最常見的實現方法
在面試或者筆試中,經常會碰到“用一個巨集實現求兩個數中的最大數”這個題目,大家看到這個問題,覺得很容易實現,認為這有什麼難度呢,隨手就是一個:
#define MAX(x, y) \
((x) > (y) ? (x) : (y))
注:用括號將巨集定義整個括起來,在任何時候,都是一個好習慣。
如果能寫出上邊這個巨集,你這道題的考試就能交差了,然後覺得對自己來說就是隨手一寫的事兒,那可就大錯特錯了。因為以上寫法的巨集定義,雖然也能拿到分數,但是在面試者或者筆試閱卷者看來,你也不過如此,你也只是茫茫人海中平凡的一員。那麼對於這道平淡無奇的題目來說,如何給考官一個眼前一亮,豁然開朗的印象,可以嘗試下使用下邊幾種方法來實現。
上邊那個巨集定義,一般情況下,是可以滿足需求的,但是對於一些引數具有副作用的情況,就很容易出現意想不到的結果了。比如:
int a = 1;
int b = 10;
int max = MAX(a++, b++);
// 巨集定義展開:
((a++) > (b++) ? (a++) : (b++));
以上例子,結果會根據編譯器的差異,產生一些意外的結果,這些絕對不會是程式開發者想要的結果,自己可以思考下...
防止引數副作用的實現方法
為了防止巨集定義的兩個引數存在副作用的情況,可以將傳遞給巨集定義的引數,在對比之前,保留一份備份,用備份引數來進行對比,總不會錯了吧,並且這樣實現,引數的副作用僅計算一次,不會影響對比的結果,實現方式如下MAX_2:
#define MAX_2(x, y) ({\
int _x = (x); \
int _y = (y); \
_x > _y ? _x : _y; \
})
然而,很快就發現,以上MAX_2巨集定義,僅僅是用在對比兩個int型引數時,實際情況可能對比的是unsigned char,或者其他的型別,那麼這個巨集定義也不能很好地實現預期效果。
指定引數型別的實現方法
繼續改進,將要對比的引數型別以一個引數的形式傳遞給巨集定義,比如下面MAX_3:
#define MAX_3(type, x, y) ({\
type _x = (x); \
type _y = (y); \
_x > _y ? _x : _y; \
})
這樣,巨集定義要對比的兩個引數的引數型別,以引數的形式傳遞給巨集定義,在巨集定義中,type引數,將是巨集定義中傳遞的那個引數型別,使用方法如下:
unsigned char c = 'A';
unsigned char d = 'B'
MAX_3(unsigned char, c, d);
MAX_3巨集定義,很好地實現了對於不同型別的兩個引數求最大值的功能,但是先不要太高興,因為MAX_3還是存在些缺點的,比如,對於一些粗心大意,導致傳遞的兩個引數,存在和第一個引數型別不一致的情況,如下:
int a = 100;
unsigned char c = 'H';
MAX_3(unsigned char, a, c);
以上情況,可能只是手誤,但是這個意外確實存在了,而MAX_3巨集定義也確實會正常執行,但是結果可能就不是實現者的本意,而在程式碼中也很難被查出來,大家應該都有花費大量時間查Bug,最後發現是一個小符號錯誤的情況,太無奈,不再多說...
相對最安全的實現方法
以上情況,也是有方法的,比如下面這個MAX_4巨集定義:
#define MAX_4(x, y) ({\
typeof(x) _x = (x); \
typeof(y) _y = (y); \
(void)(&_x == &_y); \
_x > _y ? _x : _y; \
})
MAX_4巨集定義,通過typeof關鍵字,來獲取引數的型別,並儲存引數的一份拷貝,防止引數副作用影響對比結果,再通過(void)(&_x == &_y);來對比兩個引數型別,如果不是同一種型別,在編譯階段就會報出warning,引起開發者注意,提前消滅隱患。
總結
經過以上幾種寫法的對比,會發現最後一種MAX_4巨集定義的使用還是很安全的。如果應試者能夠在筆試中很快地寫出MAX_4巨集定義的實現方式,我相信絕對會給考官們眼前一亮,甚至是驚豔的效果。
如果以上四種方式都達不到你需要的效果,那麼我也沒辦法了,因為MAX_4巨集定義可以說是我的認知範圍內,最安全的實現“巨集定義求兩個數中的最大值”的方法了。隨時歡迎朋友們分享更好的實現方法來學習。