錯誤的 MIN MAX 宏定義
朋友面試時遇到的一個問題:
Define a standard macro MAX with 2 parameters. It returns the greater one.
(Please provide program segments)
第一反應(yīng)是這樣的:
#define MYMAX(A, B) (A < B ? B : A)
然而直覺告訴我問題肯定沒這么簡單嗡害,那么坑在哪里?答案是當(dāng)你使用 ++
操作符的時候:
float a = 2.0f;
float b = MYMAX(a++, 1.5f);
NSLog(@"a = %f, b = %f", a, b);
// a = 4.000000, b = 3.000000
調(diào)用者的預(yù)期輸出應(yīng)該是 a = 3.000000, b = 2.000000
,而之所以會變成 a = 4.000000, b = 3.000000
亏娜,是因為經(jīng)過宏替換钳吟,上面的代碼相當(dāng)于:
float a = 2.0f;
float b = a++ < 1.5f ? 1.5f : a++;
NSLog(@"a = %f, b = %f", a, b);
可以看到凫碌,因為前面的條件為 NO,這里 a++
相當(dāng)于執(zhí)行了兩遍餐弱。
正確的 MIN MAX 宏定義
我們測試一下蘋果提供的 MAX 方法:
float a = 2.0f;
float b = MAX(a++, 1.5f);
NSLog(@"a = %f, b = %f", a, b);
// a = 3.000000, b = 2.000000
并沒有剛才那個問題焕檬。
我們來看一下它是如何定義的:
#define __NSX_PASTE__(A,B) A##B
#if !defined(MIN)
#define __NSMIN_IMPL__(A,B,L) ({ __typeof__(A) __NSX_PASTE__(__a,L) = (A); __typeof__(B) __NSX_PASTE__(__b,L) = (B); (__NSX_PASTE__(__a,L) < __NSX_PASTE__(__b,L)) ? __NSX_PASTE__(__a,L) : __NSX_PASTE__(__b,L); })
#define MIN(A,B) __NSMIN_IMPL__(A,B,__COUNTER__)
#endif
#if !defined(MAX)
#define __NSMAX_IMPL__(A,B,L) ({ __typeof__(A) __NSX_PASTE__(__a,L) = (A); __typeof__(B) __NSX_PASTE__(__b,L) = (B); (__NSX_PASTE__(__a,L) < __NSX_PASTE__(__b,L)) ? __NSX_PASTE__(__b,L) : __NSX_PASTE__(__a,L); })
#define MAX(A,B) __NSMAX_IMPL__(A,B,__COUNTER__)
#endif
根據(jù)宏定義姆坚,我們來替換剛才的代碼:
float a = 2.0f;
float b = ({
__typeof__(a++) __NSX_PASTE__(__a,__COUNTER__) = (a++);
__typeof__(1.5f) __NSX_PASTE__(__b,__COUNTER__) = (1.5f);
(__NSX_PASTE__(__a,__COUNTER__) < __NSX_PASTE__(__b,__COUNTER__)) ? __NSX_PASTE__(__b,__COUNTER__) : __NSX_PASTE__(__a,__COUNTER__);
});
NSLog(@"a = %f, b = %f", a, b);
// a = 3.000000, b = 2.000000
__COUNTER__
是一個預(yù)定義宏,在編譯過程中從 0 開始計數(shù)揩页,每次被調(diào)用時加 1旷偿。因為唯一性,所以通常用于構(gòu)造獨立的變量名爆侣。(參考:GCC: Common Predefined Macros)
__NSX_PASTE__
是連接萍程,宏定義中不能直接寫 AB
來連接參數(shù),需要寫成 A##B
兔仰。
假設(shè) COUNTER 的值為 0茫负,上面的代碼可以繼續(xù)被替換為:
float a = 2.0f;
float b = ({
__typeof__(a++) __a0 = (a++);
__typeof__(1.5f) __b0 = (1.5f);
(__a0 < __b0) ? __b0 : __a0;
});
NSLog(@"a = %f, b = %f", a, b);
// a = 3.000000, b = 2.000000
可以看到,這里 a++ 同樣寫了兩次乎赴,但是在 __typeof__()
中的 a++ 并沒有執(zhí)行忍法。我只能說:
PS: 無論是條件運算符,還是 if
語句中的 a++
榕吼,都會執(zhí)行:
float a = 2.0f;
float b;
if (a++ < 1.5f) {
b = 1.5;
} else {
b = a++;
}
NSLog(@"a = %f, b = %f", a, b);
// a = 4.000000, b = 3.000000