与引用一样,可以令指针指向常量或非常量。指向常量的指针不能用于改变其所指对象的值。要想存放常量对象的地址。只能使用指向常量的指针:
const double pi = 3.1.4; //pi是一个常量,它的值不能被改变
double *ptr = &pi //错误:ptr是一个普通指针
const double *cptr = &pi //正确:cptr可以指向一个双精度常量
*cptr = 42 //错误:不能*cptr赋值,因为它是用const修饰的指针常量,不能被修改
不要试图的去改变cptr的值来改变pi 的值。因为你想想,pi是const修饰的double类型的常量,它是不可被改变的。如果你可以去改变cptr的值那么pi的值就会被改变。结果就会与const修饰符相矛盾。
所谓的指向常量的指针或引用,不过是指针或引用“自以为是罢了,他们觉得自己指向了常量,所以自觉地不去改变所指对象的值”。
const指针
指针是对象而引用不是,因此就像其它对象类型一样,允许把指针本身定为常量。常量指针,必须初始化,而且一旦初始化后,则它的值·就不能被改变。把*放在const关键字之前用所以说明指针是一个常量,
const int errNumb = 0;
int *const curErr = &errNumb ;//curErr将一直指向errNumb
const int pi = 3;
int *const pip = &pi //错误:const层级不同,pi的是顶层const,pip是底层const,因为底层const的限制所以pip不能指向pi。后面会解释顶层cosnt
const int *const pip = &pi //正确:pip是一个指向常量对象的常量指针
想要弄清楚声明的含义最有效的方法是从右向左阅读。离curErr 最近的符号是const,意味着pip本身就是一个常量对象,对象的类型由声明符的其余部分确定。声明符中的下一个符号是*,意思就是curErr是一个常量指针。最后,该声明语句的基本数据类型部分确定了常量指针指向的是一个int对象。与之相似,pip是一个常量指针,它指向一个整数型常量.
顶层const
如前所述,指针本身是一个对象,它可以指向另外一个对象。因此,指针本身是不是常量以及指针所指的是不是一个常量就是两个相互独立的问题。用名词顶层const表示指针本身是个常量,而用名词底层const表示指针所指的对象是一个常量。
更一般的,顶层const可以表示任意的对象是常量,这一点对任何数据类型都适用包括指针。底层const则与指针和引用等复合类型的基本类型部分有关。比较特殊的是,指针类型既可以是顶层const也可以是底层const这一点和其他类型相比区别明显:
int i = 0;
int i2 = 10;
int *const p2 = &i2//不能改变p2指向的值, 这是一个顶层const
int *const pl = &i; //不能改变pl指向的值,这是一个顶层const
pl = p2; //错误:不能改变pl指向的值
*pl = 10; //正确:可以改变pl的具体的值
const int ci = 42; //不能改变ci的值,这是一个顶层const
const int *p2 = &ci;//允许改变p2指向的值,这是一个底层const
const int *const p3 = p2; //靠右的const的顶层const,靠左的是底层const
const int &r = ci; //用于声明引用的const都是底层const
当执行对象拷贝操作时。常量是顶层const还是底层const有区别的明显.
底层const的限制不能被忽视。当执行对象的拷贝操作时,拷贝和拷出的对象必须具有相同的底层const资格,或者两个对象的数据类型必须能够转换。一般来说,非常量可以转换成常量。
<pre name="code" class="cpp"> int *p = p3; //错误:p3包含底层const的定义,而p没有
p2 = p3; //正确:p2和p3都是底层const
p2 = &i; //正确:int*能转换成const int*
int &r = ci; //错误:普通的int&不能绑定到int常量上
const int &r = i; //正确:const int &可以绑定到一个普通int上
p3即是顶层const也是底层const,拷贝片时可以不在乎他是一个顶层const,但是必须清楚它指向的对象得是一个常量。因此,不能用p3去初始化p,因为p指向的是一个普通的(非常量)整数。另一方面,p3的值可以赋值给p2,是因为这个两个指针都是底层const,尽管p3同时也是一个常量指针(顶层const),仅就这次赋值而言不会有什么影响。