何谓常量折叠?
常量折叠就是将常量表达式计算求值,并用求得的值来替换表达式,放入常量表。可以算作一种编译优化。(预编译阶段)
接下来我们通过一段代码来分析一下了解一下常量折叠及其特性:
int main()
{
const int a = 10;
int b = 2*a;
int c = b;
int *p = (int*)&a;
*p = 100;
cout << a << endl;//输出10
cout<< *p<<endl;//输出100
return 0;
}
1 编译器会为该常量在栈上分配一块空间。
const int a = 10;
0099446E mov dword ptr [a],0Ah
a是一个常量,按理说常量是存储在代码段的,可通过反汇编可以清楚地看到编译器确实为a在栈上分配了一块空间。
在这一点上const常量和宏是有区别的,宏在预编译阶段就完成了替换,编译器并不会为宏分配空间。
2 代码中遇上a的地方,直接替换为a的值
下面我们来比较一下常量折叠和正常变量的赋值
int b = 2*a;//常量折叠赋值
00994475 mov dword ptr [b],14h//真正的实现形式是int b=2*10
int c = b;//正常变量赋值
0099447C mov eax,dword ptr [b]//借助寄存器eax把b的值传递给c
0099447F mov dword ptr [c],eax
3
大家是否对上面代码中最后的输出结果有疑惑?
指针p指向a在栈上的空间,而且明明通过*p=100对该空间上的内容作出了改变,为什么输出的a依旧是10呢?
话不多说,我们来看反汇编:
int *p = (int*)&a;
00994482 lea eax,[a] //通过寄存器eax把a的地址给指针p
00994485 mov dword ptr [p],eax
*p = 100;
00994488 mov eax,dword ptr [p]
0099448B mov dword ptr [eax],64h//修改p指向的空间的内容
cout << a << endl;
00994491 mov esi,esp
00994493 push 99149Ch
00994498 mov edi,esp
0099449A push 0Ah//出现a的地方直接用10代替
0099449C mov ecx,dword ptr ds:[9A00ECh]
009944A2 call dword ptr ds:[9A0094h]
009944A8 cmp edi,esp
009944AA call __RTC_CheckEsp (0991325h)
009944AF mov ecx,eax
009944B1 call dword ptr ds:[9A00F4h]
009944B7 cmp esi,esp
009944B9 call __RTC_CheckEsp (0991325h)
从上面的汇编代码中可以看到,a所在地址上的内容确实已经被p所修改,a的值已被改变,但是在输出a的时候,编译器输出的并不是该空间上的内容,而是直接用10替换了。