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变量是没有办法修改的;