前言:
今日看cpp的内存管理这块,偶然想到加关键字const修饰的变量位于哪块内存区?遂在网上一顿搜索和群里与大神交流,得出以下结论。
结论:
1、const修饰的量不是常量,仅仅是个只读量。在编译的时候全部替换const变量被赋予的值(这点和C语言的宏相似),在运行的时候该const变量可通过内存进行修改:
1.1)通过内存(指针)可以修改位于栈区的const变量,语法合乎规定,编译运行不会报错,但是在编译的时候所有用到该常量的地方全部被替换成了定义时所赋予的值,然后再运行的时候无法使用通过指针修改后的值。
1.2)通过内存(指针)修改位于静态存储区的的const变量,语法上没有报错,编译不会出错,一旦运行就会报告异常。
注:通过指针修改在全局区上的const变量,编译可通过,运行就会报异常。
const只在编译期间保证常量被使用时的不变性,无法保证运行期间的行为。程序员直接修改常量会得到一个编译错误,但是使用间接指针修改内存,只要符合语法则不会得到任何错误和警告。因为编译器无法得知你是有意还是无意的修改,但是既然定义成const,那么程序员就不应当修改它,不然直接使用变量定义好了。
测试代码如下:
const int a = 1;
void test1()
{
const int c = 3;
int* p = (int*)& c;
*p = 4;
//在编译的时候c被替换成了3,无法使用通过内存修改后的值
//*p表示c被修改后的值
cout << c << " " << *p << endl;
p = (int*)& a;
//在编译的时候a被替换成了1,p指向a的地址
cout << a << " " << *p << endl;
//修改位于静态存储区的const变量,编译无错,运行就会报异常
*p = 4;
//a的值还是1,因为在编译的时候字母a就被替换成1了,运行时会报异常
cout << a << endl;
}
2、const volatile修饰的变量,可以在编译时不用全部替换被const volatile变量被赋予的值,因此可以在运行时使用通过内存修改后的值:
2.1)通过内存(指针)可以修改位于栈区的const volatile变量,语法合乎规定,编译运行不会报错,在编译的时候所有用到该常量的地方不会替换成了定义时所赋予的值,在运行的时候可以使用通过指针修改后的值。
2.2)通过内存(指针)修改位于静态存储区的的const volatile变量,语法上没有报错,编译不会出错,一旦运行就会报告异常。
注:通过指针修改在全局区上的const变量,编译可通过,运行就会报异常。
测试代码如下:
void test2()
{
const volatile int c = 3;
//const volatile被修饰的变量只有在运行的时候才使用其在初始化时被赋予的值
//编译的时候,const volatile修饰的变量不替换
cout << c << endl;
void* p = (void*)&c;
(*(int*)p) = 4;
//修改c的值为4
cout << c << endl;
p = (void*)& b;
//在运行的时候修改静态存储区的const volatile变量编译不出错,运行报错
(*(int*)p) = 5;
cout << b << endl;
}
3、const变量的内存位于栈区或者静态存储区,不在符号表(常量表)中:
关于网上所说const修饰的变量存储在符号表中,这个并不完全是对的,const变量的内存位于C++的5大内存中的栈区或者静态存储区。在编译的时候,对于不试图通过内存来修改const变量值的,编译器统统将const变量存放在编译器内部产生的临时列表中,也就是所谓的符号表,该符号表与目标文件连接用的符号表是两个完全不同的东西。此临时符号表的作用就是提高效率,是编译器优化形成了,所以大家不必过多纠结const变量内存存放位置了,它的内存就是位于栈区或者静态存储区。
如果还有兴趣研究这个的,大家看看汇编代码不就完了,我本人菜鸡现在还不会。。还有关于这个的讨论,大家可以看看。。
测试代码如下:(该代码等学了汇编在做深入研究,现在先放一放。。)
const int i = 100;
int n = 0;
class CTest
{
public:
CTest() :j(0), l(0) {};
int l;
const int j;
static const int k = 102;
};
void test3()
{
CTest ct;
int m = 0;
const int o = 0;
long addri = (long)& i;
long addrl = (long)& ct.l;
long addrj = (long)& ct.j;
long addrk = (long)& ct.k;
long addrm = (long)& m;
long addrn = (long)& n;
long addro = (long)& o;
cout << "addr i=" << addri << endl; //addr i = 2989028
cout << "addr l=" << addrl << endl; //addr l = 15989288
cout << "addr j=" << addrj << endl; //addr j = 15989292
cout << "addr k=" << addrk << endl; //addr k = 2988856
cout << "addr m=" << addrm << endl; //addr m = 15989276
cout << "addr n=" << addrn << endl; //addr n = 29985882
cout << "addr o=" << addro << endl; //addr o = 15989264
}