const变量的实现原理

1        说明

今天试验了下const存储的问题,收获很多,所以做个记录。如果有错误请指针。我的试验环境xp + vc6.0(sp6)。虽然不能代表所有的浏览器,但是也有代表性吧。

考虑const存储问题,我觉得最重要的一点应该是要区分全局const变量和局部const变量。单纯的说const变量是没有意义的。

下面将就1)局部const存储;2)常量替换;3)局部const的修改;4)全局const存储等问题进行讨论,并给出实际的例子说明问题。

2        局部const

2.1    局部const变量存储区域

如下代码:

#include <iostream>

using namespace std;

 

void show(){

       constint a = 100;//局部const常量,存储在函数的栈区,是可以被修改的

}

 

void main(){

       show();

}

以上代码对应的一段汇编代码如下截图:


红色的标记部说明a变量实际上是存储在相对ebp偏移4的空间中。也就是栈中,并不是网络上一视同仁的存储在常量区。这个是“局部const变量”与“全局const变量”的最大的差别。以上的运行结果就不截图了,大家随便运行下就能看出结果的。

结论:局部const变量是存储在const所属最内层作用域(以上即为show函数)的栈区中。

2.2    局部const变量的修改

如下代码:

#include <iostream>

using namespace std;

 

void show(){

       constint a = 100;//局部const常量,存储在函数的栈区,是可以被修改的

       int*p = const_cast<int*>(&a);

       *p=1001;

 

       cout<< &a << endl;

       cout<< p << endl;

       //以上两个输出相同,说明p确实指向了a

 

       cout<< "-----" << endl;

 

       cout<< a << endl;//此处发生了“常量折叠”现象,即a在编译器编译完成后已经被替换为100(立即数)了,根本不是读取内存获取的值

       cout<< *p << endl;//这个才是a对应的内存中的值,可以看到实际上a已经被修改为1001了

}

 

void main(){

       show();

}

输出结果如下截图:


重要的我都在代码中做了注释。这里特别解释下“常量折叠”。常量折叠是编译器的一种行为表现,它会类似宏替换一样将常量也替换,如此,经过编译器的处理,上述代码中的a已经被替换为100(这是一个立即数)了,而不是从a内存空间中读取100,然后输出。这解释了为什么p指向的内存空间明明是1001,而输出确实100的现象。

结论:

1.     常量引用会被编译器进行“常量替换”;

2.     局部const变量存储在栈中,本质上与普通局部变量的存储没有区别,所以是有方法绕过const限制,将const的值修改的。不过需要提出的是,编译器进行常量替换,你的修改是不会对编译器的这种替换行为造成影响的。

3        全局const变量

3.1    全局const变量内存分配

如下代码:

#include <iostream>

using namespace std;

 

const int a = 100;

void main(){

      

}

使用调试,从watch窗口追踪变量,如下截图:


可以发现:

1.     a无法解析;

2.     a的地址无法获取

结论:

这说明,这个全局const变量a根本没有被分配内存。

3.2    全局const变量何时分配内存

如下代码:

#include <iostream>

using namespace std;

 

const int a = 100;

void main(){

 

       constint * p = &a;

      

}

相比较上一个代码,这里仅仅增加了一个代码。根据上一节的结论,a是没有分配存储空间的。那么这里使用&a,试图获取a的地址,能不能成功呢?下面是watch调试窗口的截图:


可以看到,这个时候a,&a和p均有合法的值。这说明a又实实在在的被分配内存了。

留心下上述&p的值,发现,p的地址与a的地址距离特别远。可以猜测,p与a实际上应该不是存储在同一个内存段中的。

按照所查资料等,有结论说a应该是存储在常量区的,为了验证这个想法,我在上述代码中增加了一个常量,用ch保存这个常量的地址。观察可以发现,确实,a的地址与ch的地址十分的接近,a存储于常量区应该是对的。但是,这句话有个大大的前提,就是a真的被分配存储空间了,如上的情况,a并没有被分配存储空间,就没有“存储在常量区”这么一说了。

结论:当全局const变量被取地址时,会导致全局const变量占用存储空间。应该还有其他途径导致全局const变量占用存储空间的。

3.3    全局const变量到底占不占用空间

3.4    全局const变量能修改吗?

按照上面修改局部const变量的方法,构造了如下代码试图修改全局const变量:

#include <iostream>

using namespace std;

 

const int a = 100;

char *ch = "china";

void main(){

 

       int* p = const_cast<int*>(&a);

       *p= 1001;

 

       cout<< a << endl;//注意此处也发生常量替换现象,说明常量替换现象与const变量的种类无关

       cout<< *p << endl;

 

       cout<< "-----------" << endl;

 

       cout<< &a << endl;

       cout<< p << endl;

}

很遗憾,这种修改是不可能的,下面是运行截图。

我的系统是如此报错,我相信有些系统应该是直接fatal错误,提示应该是在只读的内存空间进行写操作。因为上一节说了,全局const变量如果占用内存的话,它占用的常量区,常量区是只读的。所以试图修改全局const是不可能的。

4        结论

1.       分析const变量的内存分配,应该分为局部const变量和全局const变量两种,否则没有意义;

2.       局部const变量一定占用内存空间,并且存储在栈中。实际上与普通的存储在栈上的变量没有区别,是可以被绕过const限制被修改的;

3.       注意,全局const变量的定义是保存在符号表中的,所以符号表中一定有全局const变量的信息。全局const变量可能占用内存空间。这个是否占用内存空间是由编译器的优化策略决定的。比如没有对const进行取地址等操作,那么仅仅需要将const信息保存在符号表中就可以了。而如果有取地址等操作,那么全局const就会被分配存储空间,这个存储空间肯定是在常量区;

4.       全局const变量是没有办法修改的;


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值