之前面试xx公司的时候被问到关于const常量折叠的问题,当时就挺懵逼的。。。
下去后,查了下,这个常量折叠还是蛮有意思的,工作中也竟然没有注意到这点,不过话说这种写法应该也没人会去写吧。。。
先贴上一段代码:
#include <iostream>
using namespace std;
int main()
{
const int a = 10;
int* pa = const_cast<int*>(&a);
*pa = 20;
printf("&a=%p\n", &a);
printf("pa=%p\n", pa);
printf("a=%d\n", a);
printf("*pa=%d\n", *pa);
return 0;
}
运行上面的代码,就会发现a的地址和pa指向的地址是一模一样的,但是,他们的取值却是不同的,a的值依旧为10,而*pa的值位20。
这个结果挺颠覆我们对指针的理解是不是。地址都是一样的,为何通过指针访问地址修改数据后,a的值没有被变化呢??
利用vs的反汇编调试,断点进去查看,发现汇编码是这样子的:
printf("a=%d\n", a);
00CF1F80 push 0Ah
00CF1F82 push offset string "a=%d\n" (0CF8BE8h)
00CF1F87 call _printf (0CF13F2h)
00CF1F8C add esp,8
printf("*pa=%d\n", *pa);
00CF1F8F mov eax,dword ptr [pa]
00CF1F92 mov ecx,dword ptr [eax]
00CF1F94 push ecx
00CF1F95 push offset string "*pa=%d\n" (0CF8B3Ch)
00CF1F9A call _printf (0CF13F2h)
00CF1F9F add esp,8
通过这个汇编码就能看到,a的值实际上并不是通过内存取出来的,而是直接将0Ah,也就是10这个值直接拿出来了。而pa取值是通过寄存器来内存取值完成的。
这个其实就是所谓的const的内存折叠,其实就是编译器的优化手段,对于const会再预编译时就进行优化处理,其指向的值直接从常量表获取,而不再通过内存访问取值,以此来加快效率。
如果一定要修改const的值,可以加入关键字volatile即可。比如讲上述代码对a的定义改成const volatile int a = 10; 这样子便能告诉编译器,我们的a是可变的,每次取值编译器就会从内存获取。当然,这个volatile的具体优化也取决于编译器。