宏魔法

本文最后更新于:2024年3月6日

宏在c语言的底层开发中被大量使用,但是宏的可读性和可维护性都比较差,所以本文将其称之为“黑魔法”。

最简单的宏就是定义一些常数,比如

1
#define PI 3.1415926

另一种宏的行为类似函数,比如MIN(x,y)。对于这个宏,我们可以很快写出一个简单版本

1
#define MIN(x,y) x < y ? x : y

但是我们很快就会遇到问题,比如说2 * MIN(3,4)的值是4,而不是我们预期的6。把宏展开以后我们就知道发生了什么

1
2 * 3 < 4 ? 3 : 4;

运算优先级展开前后不相符导致了问题,加括号就可以了嘛。

所以我们可以写出第二个版本

1
#define MIN(x,y) ((x) < (y) ? (x) : (y))

更进一步地,我们把xy也加上了括号。

还没结束,考虑下面的代码

1
2
3
4
int a = 1;
b = MIN(a++, 3)
// a=3, b=2
b = ((a++) < (3) ? (a++) : (3))

a进行了两次自增,改变了原来的逻辑。

我们可以借助GNU C扩展来解决这个问题。

1
2
3
4
5
6
#define min(a,b)             \
({ \
__typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a < _b ? _a : _b; \
})

({...})是GNU C的赋值扩展,会将最后一次的表达式的赋值作为返回。