最近遇到了关于指针和const的相关问题,阅读了《C++ Primer(5th)》以及网友的博客后总结了一些方法,总结如下。 如有错误,尽情指正。
常量指针与指针常量
首先需要区分常量指针与指针常量,方法是:从右往左,遇到数据类型如"int"读作“整型”,“const”读作“常量”,“ * ”读作指向,例如下面:
const int p; //整型常量
const int *p; //指向整型常量的指针(从右往左读)
int const *p; //指向常量整型的指针(同上)
int *const p; //常量指针,指向整型
const int *const p; //常量指针,指向整型常量
int const *const p; //常量指针,指向常量整型(同上)
因此从读法上区分指针常量与常量指针就变得十分简单了。对于他们的理解可以是:
对于指针常量,被指向的对象是常量;对于常量指针,是指针本身是常量。
对于指针常量(pointer to const),借助《C++ Primer(5th)》的例子:
const double pi = 3.14; //pi是常量,不能改变
double *ptr = π // 错误,ptr仅仅是普通指针
const double *cptr = π //cptr指向了一个“对象”,这个对象是常量
*cptr = 42; //不能对cptr赋值,因为它已经是一个常量了。
double dval = 3.14; //dval可以改变
cptr = &dval; //可以通过改变dval来改变cptr
所以和常量引用一样,指向常量的指针没有规定所指向的对象必须是常量,它仅仅要求不能借助这个指针改变对象的值,而没有规定那个对象的值不能通过其他方法改变。
#include <iostream>
using namespace std;
int main()
{
/*
int t = 3;
int &r = t;
int newt = 4;
r = newt;
cout << newt << '\n' << r << endl;
*/
//上面可以正常执行为4和4
/*
int t =3;
const int &r = t;
int newt = 4;
r = newt;
cout << newt << '\n' << r << endl;//错误
*/
int i = 3;
const int *p = &i;
int newi = 4;
p = &newi;
cout << newi << endl;
cout << *p << endl;
//可以正常执行为4和4
return 0;
}
对于常量指针(const pointer),必须进行初始化,且一旦初始化完成就无法修改。*在const之前说明指针是一个常量,例如:
#include <iostream>
using namespace std;
int main()
{
int a = 2;
int c = 3;
const int * pi = &a;
cout << pi << '\t' << *pi <<endl;
pi = &c;
cout << pi << '\t' << *pi <<endl;
cout << &c << '\t' << c <<endl;
int * const ptr = &a;
cout << ptr << '\t' << *ptr <<endl;
*ptr = c;
cout << ptr << '\t' << *ptr << endl;
cout << &a << '\t' << a << endl;
/*执行结果如:
0x6dfee4 2
0x6dfee0 3
0x6dfee0 3
0x6dfee4 2
0x6dfee4 3
0x6dfee4 3
*/
return 0;
}
可见作为常量指针,ptr被牢牢扣死在了0x6dfee4这个位置上,因为这个位置就是一个“常量”。
最后总结:看const与 * 的相对位置(从右往左念)
-
常量指针:type * const ptr
-
指针常量:const *type ptr
顶层const与底层const
顶层const:指针本身是一个常量;
底层const:指针所指对象是一个常量;
顶层指针可以表示任意对象是一个常量。
int i = 0;
int *const p1 = &i; //不能改变p1,顶层
const int ci = 42; //不能改变ci,顶层
const int *p2 = &ci;//可改变p2,底层(改变方法参见上面指针常量的例子)
const int *const p3 = p2 //右const为顶层,左const为底层
const int &r = ci //用于声明的const都是底层
i = ci //顶层ci,拷贝没毛病
p2 = p3 // p2,p3指向对象类型相同,p3顶层部分不受影响
当执行拷贝操作的时候,底层与顶层的区分就出来了。
int num_c = 3;
const int *p_c = &num_c; //p_c为底层const的指针
//int *p_d = p_c; //错误,不能将底层const指针赋值给非底层const指针
const int *p_d = p_c; //正确,可以将底层const指针复制给底层const指针
当执行对象拷贝时,两者需要有相同的底层const资格,或者两个对象数据类型能转换。一般来讲,非常量可转换为常量,反之不可。