0x00 最初的起点
首先是一道烂大街的题目:以下四个变量有什么区别?
答案很简单:
1. v1是一个指针常量,即指针所指向的值不可改变,但是指针所指向的地址可以改变。
2. v2和v1相同。
3. v3是一个常量指针,即指针所指向的内存地址不可改变。
4. v4是一个指向常量的常量指针,即无论是指向的内存地址,还是内存地址中的值都是不可改变的。
可以看到,这个基本上就是依靠记忆的东西,并没有什么理解上的难点,只是记住之后没过一段时间可能就又混淆了。
记忆方案一
1.如果const位于的左侧,则const就是用来修饰指针所指向的变量,即指针指向为常量;
2.如果const位于的右侧,则const就是修饰指针本身,即指针本身是常量。
恩... 似乎很有规律的样子,但是我记性很差,这种规则对我来说还是太绕,过一段时间就不确定会不会记反了。
记忆方案二
Bjarne在他的The C++ Programming Language里面给出过一个助记的方法:
把一个声明从右向左读。
char * const cp; ( * 读成 pointer to )
cp is a const pointer to char
const char * p;
p is a pointer to const char;
恩。这样确实大大的降低了记混的情况了,但是虽然这种方法已经很简单了,但是对于英语不是母语的人来说,还是需要绕一层的,那么有什么更加简单的记忆方法吗?
记忆方案三
const修饰前面的关键字,只有当const开头时才修饰后面的关键字
即:当const前面是char int
之类的类型关键字时,表示修饰的是指针所指向的内容;而当前面的是指针运算符(*
)的时候,那么表示修饰的是该指针;当const位于表达式首位时,请参考第一条。
0X01实现机制
其实const关键字并没有多么复杂的实现方法,只是在编译器的层面做了modify的限制,而const变量和非const变量在runtime期间其实并无卵分别,最好的证据就是编译过程中的汇编代码了,证据如下:
首先我们有两段简单到妈都不认得的C++代码:
代码一
#include <iostream>
using namespace std;
int main()
{
int a = 100;
return 0;
}
代码二
#include <iostream>
using namespace std;
int main()
{
const int a = 100;
return 0;
}
这两段代码唯一的区别就是,一个int变量有const修饰,而另一个int变量没有const修饰。OK,接下来我们编译成汇编,使用下面的命令:
g++ -S demo.cpp
编译之后得到的汇编代码对比图如下所示:
我们可看到,两者并无什么不同,也就是说,汇编并不会对const的变量做什么特殊的处理,const只是在编译的层面给程序做了限制。
但是,这里又有一个问题,当我们把代码修改成下面那样的时候:
代码一
#include <iostream>
using namespace std;
int main()
{
int a = 100;
cout << a << endl;
return 0;
}
代码二
#include <iostream>
using namespace std;
int main()
{
const int a = 100;
cout << a << endl;
return 0;
}
这里的代码与上面的代码的区别无非是,访问了a的值,按照上面的理论,编译后的代码应该没有不同,但是世事难料,对比图如下:
我们可以看到竟然还是存在差别的,但是根据查阅资料和个人猜测,我认为可能的原因是:const的变量存储的时候确实没有特殊处理,但是在取值时,并不会访问const变量的内存地址,而是通过一个全局符号表(symbol table)替换了该值
,所以才会出现这样的汇编代码的差别。
0X02 最后说的话
说到底还是不懂汇编,有空也可以研究一下,如果有汇编大神路过,也请不吝赐教!