在VS2019编译器下,使用const_cast可以强制消除表达式的const性质(也可以加上,但很少用)
看如下代码
#include <iostream>
using namespace std;
int main()
{
const int a = 1;
const int* p1 = &a;
int* p2 = const_cast<int*>(p1);
*p2 = 2;
cout << a << " " << &a << endl;
cout << *p2 << " " << p2 << endl;
return 0;
}
p2通过const_cast消除了p1的const属性,因此预计输出 a=*p=2; &a=p;
而实际输出结果:
发现尽管p2指向与a所在同一块内存,但是它们输出的结果却不相同。原因为何?通过反汇编看一下:
可以发现在输出*p2值时,是从栈中地址取值,而输出a时,直接将1取出。
查阅了一些资料,这与const常量在内存中的存储方式和编译器优化有关:
const全局变量存放在全局区中
全局区中主要存放的数据有:全局变量、静态变量、常量(如字符串常量)
全局区的叫法有很多:全局区、静态区、数据区、全局静态区、静态全局区
这部分可以细分为data区和bss区
在程序结束后由操作系统释放
通过内存(指针)修改位于静态存储区的的const变量,语法上没有报错,编译不会出错,一旦运行就会报告异常。
const局部变量存放在栈上,和普通局部变量无差别,可以用指针间接修改const值。
存放函数的参数值,局部变量的值等,由编译器自动分配释放。
通过内存(指针)可以修改位于栈区的const变量,语法合乎规定,编译运行不会报错,但是在编译的时候所有用到该常量的地方全部被替换成了定义时所赋予的值,然后再运行的时候无法使用通过指针修改后的值。
对于基础数据类型,也就是const int a = 10这种,编译器会把它放到符号表中,不分配内存,当对其取地址时,会分配内存
对于基础数据类型,如果用一个变量初始化const变量,如果const int a = b,那么也是会给a分配内存
对于自定数据类型,比如类对象,那么也会分配内存
引用《C++编程思想》中的一段
C++中的const默认为内部连接,也就是说,const仅在const被定义过的文件里才是可见的,而在连接时不能被其他编译单元看到。当定义一个const时,必须赋一个值给它,除非用extern作出了清楚的说明。
通常C++编译器并不为const创建存储空间,相反它把这个定义保存在它的符号表里。但是extern强制进行了存储空间分配(另外还有一些情况,如取一个const的地址,也要进行存储空间分配),由于extern意味着使用外部连接,因此必须分配存储空间,这也就是说有几个不同的编译单元应当能够引用它,所以它必须存储空间。
此处应该是c++里的常量折叠(或者常量替换)是将const常量放在符号表中,而并不给其分配内存。编译器直接进行替换优化。
因此,在编译期间,用一个常量替换了a,这个常量不含有a真正的地址,只是一份在寄存器中的拷贝,输出a时,调用的是该常量的值,而不是被改变过的a源地址所含的值。
解决方法:可以通过volalite关键字(取消编译器的优化)
可以看出此时a的值与*p2相等了。
但是a经过volatile修饰后输出的地址变为了1,经查阅cout << 似乎没有对volatile int*做重载运算符的操作,根据VS的提示,应该是被隐式转换为了bool
类型,空指针为false,输出0,其他输出1
可以这样打印地址。
可以在&a前加上(void*)求取正确地址