C语言中的const和指针,奶奶能看懂吗?
摘要:详细讲解 C++ 中 const 限定符与指针的结合使用,包括常量引用、常量指针的定义规则,以及顶层 const 和底层 const 的区别与应用场景。
上一篇我们讲了指针,这一篇先从 const 讲起。
常量
嗯。const,顾名思义,就是不变。给任何数据类型加上 const,就指明了这个变量不会再变化。任何试图修改变量的操作都会报错,无法通过编译。比如:
const int a = 10;
a = 11; //Error!
当然,常量也必须在定义时初始化。
常量自己不能变,但这不代表不能使用。它可以被用于初始化其它对象:
int b = a * 2;
// b = 20
很简单的东西,不是吗?接下来让我们结合一下上一篇的引用和指针。
常量引用
我们可以使用 const 限定修饰一个引用。由于引用本身就不可以更改它绑定的对象,所以这里 const 只是阻止了对绑定对象的修改而已:
const int a = 10;
int b = 10;
const int &c = a;
const int &d = b;
int &e = a; //Error
a = 11;// Error
b = 11;// OK!
c = 11;// Error
d = 11;// Error
看看上面的代码,对 a,c,d 的修改都会产生编译错误。我们一个个分析:
a 是常量,但是 e 是个普通引用,非常量不能绑定到常量
对 a 的修改是不可行的,因为它是个常量
对 b 的修改是可行的,因为它是整型变量
对 c、d 的修改不可行,因为它们是常量引用,不可修改
也就是说……
const 限定符应用在引用上时,只是让 C++ 认为引用指向的对象不可以被修改,而实际指向的对象到底是否为常量(是否可以修改)是没有影响的。
如果已经在对象上施加 const,那么指向它的引用也必须添加,来保持类型一致。可以这么理解:如果引用不加 const,那么 C++ 就认为引用指向的对象可以修改,这显然和对象的不可修改性不符,编译器不允许这样的事情发生。
试试这样想吧:你可以在可以随意使用的瓶子上贴上勿动的标签,但是不能给不能动的瓶子贴上可动的标签!
还记得我们曾经把引用比作瓶子上贴的标签,那么这里的 const 限定符就好像在标签上加上一句:不能动!
特殊用法
在继续前进之前,我们来看点奇怪的常量引用。
int i = 10;
double s = 3.14;
const int &a = 1;
const int &b = i * 2;
const int &c = s * 2;
wow,这里的3、4、5行居然把表达式赋给引用,会报错的。
既然我都说了是奇怪的引用,当然不会编译错误啦。这其实是常量引用的特殊用法:如果一个引用添加了 const 限定,那么编译器允许使用任意表达式(包括字面值、算式、对象),并且能够自动转换。
你一定已经在学习指针前,了解过自动转换了。如果两个变量的类型不匹配,那么编译器会尝试自动转换。
也就是说,上方代码与下方等效:
int i = 10;
double s = 3.14;
int tmp1 = 1;
const int &a = tmp1;
int tmp2 = i * 2;
const int &b = tmp2;
int tmp3 = s * 2; //自动类型转换
const int &c = tmp3;
// b = 20, c = 6
但也别搞混了,这个只在引用带有 const 时生效,普通引用由于是可变的,所以只能绑定一个数据类型匹配的变量。
恭喜你,你已经掌握了引用中的 const,我们一鼓作气,继续看看指针中的 const。
const 与指针
添加 const 限定
const int a = 10;
int b = 10;
const int *s = &a;
const int *t = &b;
int *s1 = &a; //Error
int *t1 = &b;
为什么 s1 的定义会报错,但是其它就不报错呢?
我们在常量引用中提到,const 只是告诉编译器,认为指向的对象不可变。举一反三,const 应用于指针,则表示认为指针所指的对象(那个地址对应的对象)不可变。这就解释了 3,4 行的定义。
而第五行的报错也和上文所述相似,你所指的对象不可变,又怎么能够让指针认为所指的对象可变呢?这是不符合常理的。
认为指向对象不可变,也就是解引用后获得的对象不能变化:
*s = 11; //Error
*t = 11; //Error
但是和引用不一样,指针是对象,自己是可变的。上面的 const 限定只是让指针认为自己指向的对象不可变,但指针本身指向哪个对象是可变的。
