指针、引用和常量的关系
前言
指针、引用和常量的关系因为不同位置、能够相互嵌套(套娃)而异常复杂。文章简要介绍它们的区别,并总结区分技巧。
指针和引用
指针是对象,引用不是对象
对象的概念宽泛,不易理解。在此可以理解为变量,一个变量是一个对象
- 指针不必须初始化
- 引用只是为一个已经存在的对象所起的另一个名字(别名)
- 引用必须初始化(初始化时的右值是一个已经存在的对象)
示例:
1 | int *p; |
指向引用的指针
不存在该定义。因为引用不是对象,所以不能定义指向引用的指针。
示例:
1 | int val; |
指向指针的引用
因为指针是对象,所以能定义指向指针的引用。
示例:
1 | int *p; |
分析技巧:
从左往右阅读变量定义:
- 离变量名最近的符号,对变量的类型有最直接的影响。表述为:“…类型”
- 其余符号确定变量名作用的类型。表述为:“指向…类型的”
在示例中,离变量名ref最近的符号是&,表述为“引用类型”。其余符号是int *,表述为“指向整型指针类型的”。
从右往左理解变量定义:
- 离变量名最近的符号,对变量的类型有最直接的影响。表述为:“是…类型”
- 其余符号确定变量名作用的类型。表述为:“作用于…类型”
在示例中,离变量名ref最近的符号是&,表述为“变量ref是引用类型”。其余符号是int *,表述为“变量ref作用于整型指针类型”。
综上,“int *&ref = p;”是“指向整型指针类型的引用类型”(阅读定义),本质上是“指针的别名”,右值是指针(理解定义)。
变量和常量
一个变量的值不能被改变,该变量是常量。
变量的概念宽泛,不易理解。在此可以理解为变量是常量,具有值不能被改变的特性
示例:
1 | const int val = 0; //使用“const”限定符修饰“变量” |
指针类型的常量
因为指针是对象,所以能定义指针类型的常量。
示例:
1 | int val = 0; |
分析技巧:
从左往右阅读变量定义:
- 离变量名最近的符号,对变量的类型有最直接的影响。表述为:“…类型”
- 其余符号确定变量名作用的类型。表述为:“指向…类型的”
在示例中,离变量名p”最近的符号是“const”,表述为“常量类型”。其余符号是“int *”,表述为“指向整型指针类型的”。
从右往左理解变量定义:
- 离变量名最近的符号,对变量的类型有最直接的影响。表述为:“是…类型”
- 其余符号确定变量名作用的类型。表述为:“作用于…类型”
在示例中,离变量名“p最近的符号是const,表述为“变量p是常量类型”(虽然将常量表述为一种数据类型不太准确)。其余符号是“int *”,表述为“变量p作用于整型指针类型”。
综上,“int *const p = &val;”是“指向整型指针的常量类型”,即“指针类型的常量”(阅读定义)。
- 不能修改其所指对象的地址(因为自身的常量特性)
- 是否能修改所指对象的值取决于所指对象的类型。若指针所指对象是非常量,则可修改;反之不可(因为所指对象的常量特性)
(理解定义)
引用类型的常量
不存在该定义。因为引用不是对象,所以不能定义引用类型的常量。
示例:
1 | int val = 0; |
指向常量的指针
示例:
1 | const int val = 0; |
分析技巧:
从左往右阅读变量定义:
- 离变量名最近的符号,对变量的类型有最直接的影响。表述为:“…类型”
- 其余符号确定变量名作用的类型。表述为:“指向…类型的”
在示例中,离变量名p最近的符号是*,表述为“指针类型”。其余符号是const int,表述为“指向常量整型(整型常量)类型的”。
从右往左理解变量定义:
- 离变量名最近的符号,对变量的类型有最直接的影响。表述为:“是…类型”
- 其余符号确定变量名作用的类型。表述为:“作用于…类型”
在示例中,离变量名p最近的符号是*,表述为“变量p是指针类型”。其余符号是const int,表述为“变量p作用于常量整型(整型常量)”。
综上,“const int *p = &val;”是“指向整型常量的指针类型”,即“指向常量的指针”(阅读定义)。
- 普通指针不可指向常量
- 指向常量的指针可指向常量
- 指向常量的指针可指向非常量
- 不能修改所指对象的值(因为所指对象的常量特性)
(理解定义)
指向常量的引用
示例:
1 | const int val = 0; |
分析技巧:
从左往右阅读变量定义:
- 离变量名最近的符号,对变量的类型有最直接的影响。表述为:“…类型”
- 其余符号确定变量名作用的类型。表述为:“指向…类型的”
在示例中,离变量名ref最近的符号是&,表述为“引用类型”。其余符号是const int,表述为“指向常量整型(整型常量)类型的”。
从右往左理解变量定义:
- 离变量名最近的符号,对变量的类型有最直接的影响。表述为:“是…类型”
- 其余符号确定变量名作用的类型。表述为:“作用于…类型”
在示例中,离变量名p最近的符号是&,表述为“变量ref是引用类型”。其余符号是const int,表述为“变量ref作用于常量整型(整型常量)”。
综上,“const int &ref = val;”是“指向整型常量的引用类型”,即“指向常量的引用”(阅读定义)。
- 普通引用不可指向常量
- 指向常量的引用可指向常量
- 指向常量的指针可指向非常量
- 不能修改所指对象的值(因为所指对象的常量特性)
(理解定义)
技巧
从左往右阅读变量定义
- 离变量名最近的符号,对变量的类型有最直接的影响。表述为:“…类型”
- 其余符号确定变量名作用的类型。表述为:“指向…类型的”
从右往左理解变量定义
- 离变量名最近的符号,对变量的类型有最直接的影响。表述为:“是…类型”
- 其余符号确定变量名作用的类型。表述为:“作用于…类型”
顶、底层常量
- 针对指针类型的判别,因为指针本身是一个对象,其可以指向另一个对象(概念实际上可以扩展到引用等复合类型)
- 离变量名最近的const符号,是顶层
- 离变量名最远的const符号,是底层
- 顶层常量,表示对象本身是常量
- 底层常量,表示对象所指的对象是常量
示例:
1 | int val = 0; |
- 执行对象的拷贝(赋值)操作时,拷入和拷出的对象必须具有相同的底层常量资格;或者两个对象的数据类型能够相互转换,一般非常量能够转换成常量(除基本类型外)
示例:
1 | int val = 0; |
总结
- 指针、引用和常量的关系因为不同位置、能够相互嵌套(套娃)而异常复杂
- 建议分别或集中对比,简要了解其概念、理解几个简单示例。在理解的基础上总结区分技巧即可
- 实际编程中几乎遇不到、用不到它们的复杂组合,可无需咬文嚼字、刨根问底
作者的话
- 作者反复阅读书籍中对指针、引用和常量的描述,边理解边总结
- 实际上这篇文章内容质量并不好,因为作者在分析时还是很容易懵逼。至少尽力地将自己的理解更好地表述出来
- 文章内容基于读者对指针、引用和常量概念有一定的理解
- 一些详细概念请参阅其他资料
- 文章在描述时有疑惑的地方,请留言,定会一一耐心讨论、解答
- 文章在认识上有错误的地方, 敬请批评指正
- 望读者们都能有所收获
参考资料
《C++ Primer》中文版(第5版)作者:Stanley B. Lippman,Barbara E. Moo,JoséeLaJoie