一:背景
如果大家看过 CLR
源码,会发现里面有很多 #define
宏定义,比如说 fusionhelpers.hpp
头文件里。
如果你不熟悉 C ,看到这些 #define 应该会很晕的,这篇我们就来简单聊聊 define 的玩法,其实说白了很简单, #define
就是用一个标识符来包装一段 常量
或者 函数体
,后续要复用这段逻辑时只需用此 标识符
即可。
这里要注意的是:替换发生在 编译时
,如果不相信的话,可以从汇编上做验证。
二:define 的简单使用
1. 用 define 定义常量
这个是最常用的,上一段简单的测试代码:
#include <iostream>#define N 10int main()
{printf("output: s=%d", N);
}output: s=10
接下来我们探究下它的汇编代码。
00531921 push 0Ah
00531923 push offset string "s=%d" (0537B30h)
00531928 call _printf (05310D2h)
0053192D add esp,8
从汇编中可以看出,并没有出现 N
标识符的影子,而是直接将立即数 10
推送到栈上,大概就是下面这样。
printf("s=%d", 10);
相信大家也看到了这个简单替换,如果你还不信的话,我来演示一个 简单替换
的坑,参考如下代码:
#include <iostream>#define N 10+2int main()
{float f = N / 2;printf("output: s=%.1f", f);
}output: s=11.0
哈哈,你是不是天真的以为上面的输出是 s=6
? 那就大错特错了,这个例子很好的反向证明了 确实是 替换
所致。
接下来我们来看下底层汇编
是咋样的。
00f11925 movss xmm0, dword ptr [ConsoleApplication1!_real (00f17b44)]
00f1192d movss dword ptr [ebp-8], xmm00:000> dp 00f17b44 L1
00f17b44 413000000:000> .formats 41300000
Evaluate expression:Hex: 41300000Decimal: 1093664768Octal: 10114000000Binary: 01000001 00110000 00000000 00000000Chars: A0..Time: Sat Aug 28 11:46:08 2004Float: low 11 high 0Double: 5.40342e-315
从汇编代码看,f 的值已经算好了存放在 00f17b44
地址上,值为 41300000
, 通过 .formats 命令可以看出 41300000
转成 float 就是 11,很好的证明了它是在编译时就已经处理好了。
有了这些基础,改进方案就简单了,用 ()
将 define 体包装一下即可,参考如下:
#define N (10+2)
2. 用 define 定义函数
从 CLR 源码上看,不仅仅可以定义 常量,还可以定义复杂的函数,接下来我们就演示一下。
#include <iostream>#define SUM(a,b) a+bint main()
{int a = 10;int b = 20;int sum = SUM(a, b);printf("output: sum=%d", sum);
}output: sum=30
然后再看一下 int sum = SUM(a, b)
的汇编代码。
00581925 mov dword ptr [ebp-8],0Ah
0058192C mov dword ptr [ebp-14h],14h
00581933 mov eax,dword ptr [ebp-8]
00581936 add eax,dword ptr [ebp-14h]
可以看到,那个 Sum
方法直接被 inline
了,高效哈,如果语句多的话,也可以在 #define 中用 {}
括起来,比如下面这样。
#include <iostream>#define SUM(a,b) {int i=a; int j=b; printf("output: %d+%d=%d",i,j,(i+j));}int main()
{int a = 10;int b = 20;SUM(a,b);
}output: 10+20=30
好了,今天就聊这么多,希望对大家有帮助!