指针是一种特殊的变量,它存储了一个内存地址。通过指针,我们可以直接访问和修改内存中的数据。而const关键字可以用来指定一个变量为常量,即其值不能被修改。
当将const关键字与指针结合使用时,有两种常见的方式:
int const* a
a
表示一个指向 常量 的指针,即指针指向对象的值不能通过该指针修改,但是该指针的指向(值)可以被修改。例如:情况1:
int num = 10;
int const* a = # // a 是指向常量的指针
num = 20; // 可以修改 num
// *a = 30; // 错误,不能修改指针a指向的值
int num2 = 30;
a = &num2; //可以修改指针的指向
//
情况2:
const int num3 = 40; // num3 是常量,初始化后就不可修改
int const* a1 = &num3; // a1 是指向常量的指针,
//num3 = 50; // 不可以修改,num3为常量
//*a1 = 60; // 错误,不能修改指针a1指向的值
在上面的例子中a不能修改num的值,但是num的值可以通过别的方式修改,因此我们可以这么想: 所谓指向常量的指针(还有后面的引用),不过是指针(引用)"自以为是"罢了,他们觉得自己指向了常量,所以自觉地不去改变所指向对象的值。 然而指针(引用)所指向的值可能是常量也有可能是非常量。
int* const a
指针是对象,因此就像其他类型一样,允许把指针本身定为常量。 常量指针必须初始化,而且一定初始化完成,则他的值(也就是存放在指针中的地址)就不能再改变了。
a表示一个 常量指针,这里的关键字位于 int *前面,说明指针是一个常量,则不能改变指针的值(也就是存放在指针中的地址)。 例如:
情况1:
int num = 10;
int* const a = # // a是常量指针,a将一直指向&num
num = 20; // num不是const,可以修改num
*a = 30; // num不是const,可以通过*a修改num
int num2 = 30;
a = &num2; //错误,a是const指针,指针的值不能被修改
情况2:
const int num = 40; //num 是常量,值不能改变
//int *const b = # //错误,num是常量,num不能被改变,b是常量指针,可以改变指针a指向的值, 所以非法,
const int* const b1=# //正确,a是指向常量的常量指针
int num1 = 50;
//b1=&num1; //错误,a是常量指针,不能改变它的值
//*b1 = 60; //错误,a是指向常量的指针,不能通过a改变a指向对象的值
顶层const说的是对象(整型、浮点型、字符型等等基本类型以及类和指针,但是不包括引用,因为引用不是对象)本身是const, 底层const说的是指针和引用的所指的对象是const。指针类型既可以是顶层const也可以是底层const。
当执行对象拷贝操作时顶层const和底层const有区别:
执行拷贝操作时不会改变被拷贝对象的值,因此,对顶层const,考入和考出的对象是否是常量都没有什么影响。例如:
const int a=10; //顶层const, a是常量
int b = a; //正确,对常量a没有影响
int c=20;
const int d =c; //正确,d的初始值为20,不能再改变d了
int f = 0;
int * e = &f;
int *const h = e; //正确,h是顶层const,h指向e,不能再改变h的指向了
int * g = h //正确,h的指向没有改变
但是底层const的限制不能忽略,当执行对象的拷贝操作时,考入和考出的对象必须具有相同的底层const资格,或者两个对象的数据类型能够转换。一般来说,非常量可以转换成常量,反之则不行。例如:
int A=5;
const int a =10;
int *const b1 = &a; //错误 a是常量,所以指向a的指针应该是指向常量的指针,这样才能保证a不改变,而b1是常量指针,不是指向常量的指针
const int* b2 =&A; //正确, b2属于底层const
int *const b3=&A; //正确, b3属于顶层const
int* b4=b2; //错误, 不能将一个指向常量的指针赋值给一个指向非常量的指针
//我们要想清楚const到底是限制的什么? 我们执行拷贝操作的时候只要不改变const限制的对象的值就行。
int & c1=a; //错误, 不能将一个常量赋值给一个非常量的引用
const int &c2 =a; //正确, c2是指向常量的引用,属于顶层const
&c2 =A; //正确, 非常量可以赋值给一个常量
int &c3=c2; //错误, 不能将一个指向常量的引用赋值给一个非常量引用
可以把引用绑定到const上,像绑定其他对象上一样,我们称之为对常量的引用,与普通引用不同的是,常量的引用不能被用作修改它绑定的对象的值。例如:
const int a=10;
const int &A=a; //A是常量的引用
A=10; //错误,不能修改A所绑定的值
初始化:
引用的类型必须与所引用对象的类型一致,在此条件下:对常量的引用初始化时允许用任意表达式作为初始值,只要该表达式的类型能转换成引用的类型即可,也允许绑定一个非常量的对象、字面值。 例如:
const int &A =40; //允许用字面值作为初始值
int b =20;
const int &B =b; //正确,常量的引用 可以绑定一个非常量对象
B=30; //错误,不能修改B所绑定的值
const int &C = b*2; //正确,可以用表达式结果作为初始值
///
const int &D =20;
int &E = D; //错误,不能将常量引用赋值给一个非常量引用
const double f=20.1;
const int &H=f; //错误,引用的类型必须与所引用对象的类型一致