错误的 MIN MAX 宏定义
朋友面试时遇到的一个问题:
Define a standard macro MAX with 2 parameters. It returns the greater one.
(Please provide program segments)
第一反应是这样的:
#define MYMAX(A, B) (A < B ? B : A)
然而直觉告诉我问题肯定没这么简单,那么坑在哪里?答案是当你使用 ++
操作符的时候:
float a = 2.0f;
float b = MYMAX(a++, 1.5f);
NSLog(@"a = %f, b = %f", a, b);
// a = 4.000000, b = 3.000000
调用者的预期输出应该是 a = 3.000000, b = 2.000000
,而之所以会变成 a = 4.000000, b = 3.000000
,是因为经过宏替换,上面的代码相当于:
float a = 2.0f;
float b = a++ < 1.5f ? 1.5f : a++;
NSLog(@"a = %f, b = %f", a, b);
可以看到,因为前面的条件为 NO,这里 a++
相当于执行了两遍。
正确的 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
根据宏定义,我们来替换刚才的代码:
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__
是一个预定义宏,在编译过程中从 0 开始计数,每次被调用时加 1。因为唯一性,所以通常用于构造独立的变量名。(参考:GCC: Common Predefined Macros)
__NSX_PASTE__
是连接,宏定义中不能直接写 AB
来连接参数,需要写成 A##B
。
假设 COUNTER 的值为 0,上面的代码可以继续被替换为:
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++ 并没有执行。我只能说:
PS: 无论是条件运算符,还是 if
语句中的 a++
,都会执行:
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