const常量的存储位置

scorlw 发布于

const常量的存储位置

c++

1、const修饰的量不是常量,仅仅是个只读量。在编译的时候全部替换const变量被赋予的值(这点和C语言的宏相似),在运行的时候该const变量可通过内存进行修改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const int a = 1;//静态存储区
void test1()
{
const int c = 3;//栈区
int *p = (int*)& c;
*p = 4;
//此时内存中的c已经被修改成4
//但在编译的时候c被替换成了3,无法使用通过内存修改后的值
//*p表示c被修改后的值

cout << c << " " << *p << endl;//3 4

p = (int*)& a;
//在编译的时候a被替换成了1,p指向a的地址
cout << a << " " << *p << endl;//1 1
*p = 4;
//修改位于静态存储区的const变量,编译无错,运行就会报异常
//a的值还是1,因为在编译的时候字母a就被替换成1了,运行时会报异常
cout << a << endl;
}

结论

  1. 通过内存(指针)可以修改位于栈区的const变量,语法合乎规定,编译运行不会报错,但是在编译的时候所有用到该常量的地方全部被替换成了定义时所赋予的值,然后再运行的时候无法使用通过指针修改后的值。
  2. 通过内存(指针)修改位于静态存储区的的const变量,语法上没有报错,编译不会出错,一旦运行就会报告异常。

注:通过指针修改在全局区上的const变量,编译可通过,运行就会报异常。

const只在编译期间保证常量被使用时的不变性,无法保证运行期间的行为。程序员直接修改常量会得到一个编译错误,但是使用间接指针修改内存,只要符合语法则不会得到任何错误和警告。

2、const volatile修饰的变量,可以在编译时不用全部替换被const volatile变量被赋予的值,因此可以在运行时使用通过内存修改后的值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void test2()
{
const volatile int c = 3;
//volatile的作用是作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值。
//const volatile被修饰的变量只有在运行的时候才使用其在初始化时被赋予的值
//编译的时候,const volatile修饰的变量不替换
cout << c << endl;//3
void* p = (void*)&c;
(*(int*)p) = 4;
//修改c的值为4
cout << c << endl;//4
//int* 也可以
int *p1 = (int*)& c;
*p1 = 6;
cout << c << endl;//6

p = (void*)& a;
//在运行的时候修改静态存储区的const volatile变量编译不出错,运行报错
(*(int*)p) = 5;
cout << a << endl;
}

结论

  1. 通过内存(指针)可以修改位于栈区的const volatile变量,语法合乎规定,编译运行不会报错,在编译的时候所有用到该常量的地方不会替换成了定义时所赋予的值,在运行的时候可以使用通过指针修改后的值。
  2. 通过内存(指针)修改位于静态存储区的的const volatile变量,语法上没有报错,编译不会出错,一旦运行就会报告异常。

注:通过指针修改在全局区上的const变量,编译可通过,运行就会报异常。

3、const变量的内存位于栈区或者静态存储区,不在符号表(常量表)中:

关于网上所说const修饰的变量存储在符号表中,这个并不完全是对的,const变量的内存位于C++的5大内存中的栈区或者静态存储区。在编译的时候,对于不试图通过内存来修改const变量值的,编译器统统将const变量存放在编译器内部产生的临时列表中,也就是所谓的符号表,该符号表与目标文件连接用的符号表是两个完全不同的东西。此临时符号表的作用就是提高效率,是编译器优化形成了,所以大家不必过多纠结const变量内存存放位置了,它的内存就是位于栈区或者静态存储区。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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
}

在这里插入图片描述

另外:

c语言中const全局变量存储在只读数据段,编译期最初将其保存在符号表中,第一次使用时为其分配内存,在程序结束时释放。

而const局部变量(局部变量就是在函数中定义的一个const变量,)存储在栈中,代码块结束时释放。

在c语言中可以通过指针对const局部变量进行修改,而不可以对const全局变量进行修改。因为const全局变量是存储在只读数据段

c++中,一个const不是必需创建内存空间,而在c中,一个const总是需要一块内存空间。

在c++中是否要为const全局变量分配内存空间,取决于这个const变量的用途,如果是充当着一个值替换(即就是将一个变量名替换为一个值),那么就不分配内存空间,不过当对这个const全局变量取地址或者使用extern时,会分配内存,存储在只读数据段。也是不能修改的。

c++中对于局部的const变量要区别对待:

对于基础数据类型,也就是const int a = 10这种,编译器会把它放到符号表中,不分配内存,当对其取地址时,会分配内存

对于基础数据类型,如果用一个变量初始化const变量,如果const int a = b,那么也是会给a分配内存

对于自定数据类型,比如类对象,那么也会分配内存。

c中const默认为外部连接,c++中const默认为内部连接.当c语言两个文件中都有const int a的时候,编译器会报重定义的错误。而在c++中,则不会,因为c++中的const默认是内部连接的。如果想让c++中的const具有外部连接,必须显示声明为: extern const int a = 10。

原文地址:https://blog.csdn.net/qq_43152052/article/details/99306967?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.channel_param

https://blog.csdn.net/woainilixuhao/article/details/86521357