在C语言中可以使用#define来指定一个标识符(宏名)代表一个字符串。宏定义有两种方式,一种有参数宏定义,一种无参数宏定义。1
2
3
4//无参数宏定义
//有参数宏定义
关于宏的几点说明:
1、宏名一般大写;
2、宏是预编译处理不做语法检查和计算,仅替换;
3、宏定义末尾不加分号;
4、宏使用#define定义,使用#undef取消定义;
5、宏定义可以嵌套,不会递归替换;
6、字符串""中不包含宏,宏定义不分配内存,变量定义分配内存;
7、宏名和参数间的括号不能有空格;
8、#用于在宏定义的参数两端加上"",##用于拼接宏;
9、每次宏展开的结果会被重复扫描,直到没有任何可以展开的宏为止;
10、每展开一个宏,都会记住这次展开,在这个宏展开的结果和后续展开中不会重复展开相同的宏;
11、带参数的宏,先对参数展开,除非宏定义体中有#和##。
针对8,9条给出一个说明例子:demo.c1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int main()
{
foo;
}
/*
先展开foo => foo bar,因为foo已经展开过了,之后的foo不再展开
此时展开bar => foo bar bar foo
通过 gcc -E demo.c 得到如下结果:
int main()
{
foo bar bar foo;
}
说明结论正确。
*/
学习了宏定义以后我们来看一下PHP源码中的宏替换,PHP源码中有一个内存分配的宏替换,这个宏的位置在/usr/src/php-7.1.6/Zend/zend_alloc.h文件的106行:1
2
3
4
5
6
7
8
9
10
11
ZEND_MM_BINS_INFO(_ZEND_BIN_ALLOCATOR_SELECTOR_START, size, y) \
((size <= ZEND_MM_MAX_LARGE_SIZE) ? _emalloc_large(size) : _emalloc_huge(size)) \
ZEND_MM_BINS_INFO(_ZEND_BIN_ALLOCATOR_SELECTOR_END, size, y)
(__builtin_constant_p(size) ? \
ZEND_ALLOCATOR(size) \
: \
_emalloc(size) \
)
我们可以看到里面有四个宏定义,其中ZEND_MM_BINS_INFO和ZEND_MM_MAX_LARGE_SIZE这两个宏在/usr/src/php-7.1.6/Zend/zend_alloc_sizes.h中,详情如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/* num, size, count, pages */
_( 0, 8, 512, 1, x, y) \
_( 1, 16, 256, 1, x, y) \
_( 2, 24, 170, 1, x, y) \
_( 3, 32, 128, 1, x, y) \
_( 4, 40, 102, 1, x, y) \
_( 5, 48, 85, 1, x, y) \
_( 6, 56, 73, 1, x, y) \
_( 7, 64, 64, 1, x, y) \
_( 8, 80, 51, 1, x, y) \
_( 9, 96, 42, 1, x, y) \
_(10, 112, 36, 1, x, y) \
_(11, 128, 32, 1, x, y) \
_(12, 160, 25, 1, x, y) \
_(13, 192, 21, 1, x, y) \
_(14, 224, 18, 1, x, y) \
_(15, 256, 16, 1, x, y) \
_(16, 320, 64, 5, x, y) \
_(17, 384, 32, 3, x, y) \
_(18, 448, 9, 1, x, y) \
_(19, 512, 8, 1, x, y) \
_(20, 640, 32, 5, x, y) \
_(21, 768, 16, 3, x, y) \
_(22, 896, 9, 2, x, y) \
_(23, 1024, 8, 2, x, y) \
_(24, 1280, 16, 5, x, y) \
_(25, 1536, 8, 3, x, y) \
_(26, 1792, 16, 7, x, y) \
_(27, 2048, 8, 4, x, y) \
_(28, 2560, 8, 5, x, y) \
_(29, 3072, 4, 3, x, y)
我们可以把这个宏从代码里面摘出来test_zend.c如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
((size <= _size) ? _emalloc_ ## _size() :
# define _ZEND_BIN_ALLOCATOR_SELECTOR_END(_num, _size, _elements, _pages, size, y) \
)
ZEND_MM_BINS_INFO(_ZEND_BIN_ALLOCATOR_SELECTOR_START, size, y) \
((size <= ZEND_MM_MAX_LARGE_SIZE) ? _emalloc_large(size) : _emalloc_huge(size)) \
ZEND_MM_BINS_INFO(_ZEND_BIN_ALLOCATOR_SELECTOR_END, size, y)
(ZEND_ALLOCATOR(size))
int main()
{
_emalloc(size);
}
接下来我们来一步步展开这个宏:1、展开 _emalloc(size)
展开以后发现ZEND_ALLOCATOR(size)这个宏2、展开 ZEND_ALLOCATOR(size)
展开以后发现ZEND_MM_BINS_INFO(_ZEND_BIN_ALLOCATOR_SELECTOR_START, size, y)这个宏3、展开 ZEND_MM_BINS_INFO(_ZEND_BIN_ALLOCATOR_SELECTOR_START, size, y)
结合前面的ZEND_MM_BINS_INFO(_, x, y)得到如下结果:1
2
3
4
5
6_ZEND_BIN_ALLOCATOR_SELECTOR_START(0, 8, 512, 1, size, y) \
_ZEND_BIN_ALLOCATOR_SELECTOR_START(1, 16, 256, 1, size, y) \
.
.
.
_ZEND_BIN_ALLOCATOR_SELECTOR_START(29, 3072, 4, 3, size, y) \
4、展开 _ZEND_BIN_ALLOCATOR_SELECTOR_START(_num, _size, _elements, _pages, size, y)1
2
3((size <= 8) ? _emalloc_8() : ((size <= 16 ? _emalloc_16() :
.........................................................
((size <= 3072) ? _emalloc_3072() :
5、展开 ZEND_MM_MAX_LARGE_SIZE1
((size <= ((2 * 1024 * 1024) - ((4 * 1024) * (1)))) ? lloc_large(size) : _emalloc_huge(size))
6、展开 ZEND_MM_BINS_INFO(_ZEND_BIN_ALLOCATOR_SELECTOR_END, size, y)1
2
3
4
5
6_ZEND_BIN_ALLOCATOR_SELECTOR_END(0, 8, 512, 1, size, y) \
_ZEND_BIN_ALLOCATOR_SELECTOR_END(1, 16, 256, 1, size, y) \
.
.
.
_ZEND_BIN_ALLOCATOR_SELECTOR_END(29, 3072, 4, 3, size, y) \
7、展开 _ZEND_BIN_ALLOCATOR_SELECTOR_END(_num, _size, _elements, _pages, size, y)1
)))))))))))))))))))))))))))))))
8、到此所有宏展开完毕,组合以后得到如下结果1
2
3(((size <= 8) ? _emalloc_8() : ((size <= 16 ? _emalloc_16() :
.........................................................
((size <= 3072) ? _emalloc_3072() : ((size <= ((2 * 1024 * 1024) - ((4 * 1024) * (1)))) ? lloc_large(size) : _emalloc_huge(size)) )))))))))))))))))))))))))))))))
9、执行 gcc -E test_zend.c 得到结果如下1
2
3
4int main()
{
(((size <= 8) ? _emalloc_8() : ((size <= 16) ? _emalloc_16() : ((size <= 24) ? _emalloc_24() : ((size <= 32) ? _emalloc_32() : ((size <= 40) ? _emalloc_40() : ((size <= 48) ? _emalloc_48() : ((size <= 56) ? _emalloc_56() : ((size <= 64) ? _emalloc_64() : ((size <= 80) ? _emalloc_80() : ((size <= 96) ? _emalloc_96() : ((size <= 112) ? _emalloc_112() : ((size <= 128) ? _emalloc_128() : ((size <= 160) ? _emalloc_160() : ((size <= 192) ? _emalloc_192() : ((size <= 224) ? _emalloc_224() : ((size <= 256) ? _emalloc_256() : ((size <= 320) ? _emalloc_320() : ((size <= 384) ? _emalloc_384() : ((size <= 448) ? _emalloc_448() : ((size <= 512) ? _emalloc_512() : ((size <= 640) ? _emalloc_640() : ((size <= 768) ? _emalloc_768() : ((size <= 896) ? _emalloc_896() : ((size <= 1024) ? _emalloc_1024() : ((size <= 1280) ? _emalloc_1280() : ((size <= 1536) ? _emalloc_1536() : ((size <= 1792) ? _emalloc_1792() : ((size <= 2048) ? _emalloc_2048() : ((size <= 2560) ? _emalloc_2560() : ((size <= 3072) ? _emalloc_3072() : ((size <= ((2 * 1024 * 1024) - ((4 * 1024) * (1)))) ? _emalloc_large(size) : _emalloc_huge(size)) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ));
}
验证正确,这就是宏替换,看源码会有很多这样复杂的宏定义,只要认真仔细,都是可以轻松替换的。赶快去试试吧。