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 限定只是让指针认为自己指向的对象不可变,但指针本身指向哪个对象是可变的。
阅读全文