关于标题
这篇文章主要介绍const
里面的一个现象。在这个现象里,a
和*&a
是不同的,在大部分情况里,二者应该是一样的。
引入
今天在复习const各种用法时,看到了一篇文章,里面的现象令我不可思议。
这里面有一段代码大概意思是这样的:
const int a = 1;
int* b = (int*)&a;
// 先对a取地址,那就是一个const int*类型的指针,然后把他 强制类型转换 成int*类型的指针
*b = 2;
cout << a << endl;
cout << *b << endl;
请读者猜一下,a
和*b
分别是什么?
大概可能会有读者认为*b = 2
这一步会报错。
但实际上这句话可以正常运行,而且最后的输出是a = 1
和*b = 2
这里可能会有疑问,那就是a
和b
的地址
是不是不一样啊?
于是检测一下:
cout << &a << endl;
cout<< b << endl;
结果表示两行输出的地址是一致的。
为什么从一样的地址取同样长度的内存,输出结果却不一样呢?下面进行简单介绍。
const int* 与 int*
在声明变量a
的时候使用了const
关键字,那就意味着这个变量a
在程序执行过程中是不会变的。
那样的话,a
就会在编译期进行赋值和使用。
然后既然a
不变,那么说明a
是个常量。那么编译器在编译期间会把a
放入常量表里。(这一部分知识在编译原理这一块里。)
然后我们声明了一个指针b
,使其指向a
的地址。
正常情况下
int* b = &a;
是会报错的。因为b
是int*
类型的指针而&a
对应的是一个const int*
类型的指针。
int*
类型的指针是允许修改内存单元里的值的。
而const int*
类型就不允许。
所以出于安全性的考虑,上面这个代码跑不起来。
而如果在这里进行一个强制类型转换,即像文章开头那段代码一样加上(int*)
(这个操作确实可行)
那样就能使得b
指向a
的地址且能通过b
来修改a
内存的值
所以上面的*b = 2
不会报错。
为什么输出不一样?
那么问题就来了。已经通过指针b
修改了a
内存单元内的值,那为什么在cout << a;
的时候仍会输出原来的1
而不是修改后的2
呢?
原因在于,编译器把a
放入了常量表里,所以在cout << a;
时会直接从常量表里寻找a
而不是通过内存读取。
而cout << *b
就不一样了,因为b
不是一个常量,所以需要在运行期间分配空间和赋值等,所以在cout << *b;
时会老老实实从内存里去读取。
这就是两者不一样的原因。
如何解决?
如果希望修改后,cout << a;
的结果是2
,那应该如何操作?
这里我说一下,如果把一个变量声明为const
类型,那就不要对其做修改。这是缺大德的行为!!!
我们继续。
如果希望每次读取变量的值时都从内存里老老实实读取的话,声明时要多加一个关键字volatile
,形式如下。
const volatile int x = 10;
int* p = (int*)&x;
*p = 20;
cout << x << endl;
cout << *p << endl;
这回两行输出就都一样了。
回归标题
如果不加volatile
还想实现修改后输出修改的值,那就可以cout << *&a << endl;
所以有时候这个操作并不一定是“多此一举”
总的来说,如果a
是个const
类型的,而且通过指针间接修改了a
的值,那么a
和*&a
会不一样。
希望大家以后永远不会遇到这些坑吧!嘿嘿嘿
感谢各位读者的阅读。如有错误或者补充,欢迎且十分感谢您在评论区留言。