前言

指针、引用和常量的关系因为不同位置、能够相互嵌套(套娃)而异常复杂。文章简要介绍它们的区别,并总结区分技巧。


指针和引用

指针是对象,引用不是对象

对象的概念宽泛,不易理解。在此可以理解为变量,一个变量是一个对象

  • 指针不必须初始化
  • 引用只是为一个已经存在的对象所起的另一个名字(别名)
  • 引用必须初始化(初始化时的右值是一个已经存在的对象

示例:

1
2
3
4
5
6
int *p;	

int val; //一个已经存在的“对象”
int &ref = val;

int &ref1; //×

指向引用的指针

不存在该定义。因为引用不是对象,所以不能定义指向引用的指针
示例:

1
2
3
int val;
int &ref=val;
int &*p=ref; //×

指向指针的引用

因为指针是对象,所以能定义指向指针的引用
示例:

1
2
int *p;
int *&ref = p;

分析技巧

从左往右阅读变量定义:

  • 离变量名最近的符号,对变量的类型有最直接的影响。表述为:“…类型
  • 其余符号确定变量名作用的类型。表述为:“指向类型的

在示例中,离变量名ref最近的符号是&,表述为“引用类型”。其余符号是int *,表述为“指向整型指针类型的”。

从右往左理解变量定义:

  • 离变量名最近的符号,对变量的类型有最直接的影响。表述为:“…类型”
  • 其余符号确定变量名作用的类型。表述为:“作用于…类型”

在示例中,离变量名ref最近的符号是&,表述为“变量ref引用类型”。其余符号是int *,表述为“变量ref作用于整型指针类型”。

综上,“int *&ref = p;”是“指向整型指针类型的引用类型”(阅读定义),本质上是“指针的别名”,右值是指针(理解定义)。


变量和常量

一个变量的不能被改变,该变量常量。

变量的概念宽泛,不易理解。在此可以理解为变量常量,具有不能被改变的特性

示例:

1
const int val = 0;	//使用“const”限定符修饰“变量”

指针类型的常量

因为指针是对象,所以能定义指针类型的常量
示例:

1
2
int val = 0;
int *const p = &val;

分析技巧

从左往右阅读变量定义:

  • 离变量名最近的符号,对变量的类型有最直接的影响。表述为:“…类型
  • 其余符号确定变量名作用的类型。表述为:“指向类型的

在示例中,离变量名p”最近的符号是“const”,表述为“常量类型”。其余符号是“int *”,表述为“指向整型指针类型的”。

从右往左理解变量定义:

  • 离变量名最近的符号,对变量的类型有最直接的影响。表述为:“…类型”
  • 其余符号确定变量名作用的类型。表述为:“作用于…类型”

在示例中,离变量名“p最近的符号是const,表述为“变量p常量类型”(虽然将常量表述为一种数据类型不太准确)。其余符号是“int *”,表述为“变量p作用于整型指针类型”。

综上,“int *const p = &val;”是“指向整型指针常量类型”,即“指针类型的常量”(阅读定义)。

  • 不能修改其所指对象的地址(因为自身的常量特性)
  • 是否能修改所指对象的取决于所指对象的类型。若指针所指对象是非常量,则可修改;反之不可(因为所指对象的常量特性)

(理解定义)


引用类型的常量

不存在该定义。因为引用不是对象,所以不能定义引用类型的常量
示例:

1
2
int val = 0;
int &const ref = val; //×

指向常量的指针

示例:

1
2
const int val = 0;
const int *p = &val;

分析技巧

从左往右阅读变量定义:

  • 离变量名最近的符号,对变量的类型有最直接的影响。表述为:“…类型
  • 其余符号确定变量名作用的类型。表述为:“指向类型的

在示例中,离变量名p最近的符号是*,表述为“指针类型”。其余符号是const int,表述为“指向常量整型(整型常量)类型的”。

从右往左理解变量定义:

  • 离变量名最近的符号,对变量的类型有最直接的影响。表述为:“…类型”
  • 其余符号确定变量名作用的类型。表述为:“作用于…类型”

在示例中,离变量名p最近的符号是*,表述为“变量p指针类型”。其余符号是const int,表述为“变量p作用于常量整型(整型常量)”。

综上,“const int *p = &val;”是“指向整型常量指针类型”,即“指向常量的指针”(阅读定义)。

  • 普通指针不可指向常量
  • 指向常量的指针可指向常量
  • 指向常量的指针可指向非常量
  • 不能修改所指对象的值(因为所指对象的常量特性)

(理解定义)


指向常量的引用

示例:

1
2
const int val = 0;
const int &ref = val;

分析技巧

从左往右阅读变量定义:

  • 离变量名最近的符号,对变量的类型有最直接的影响。表述为:“…类型
  • 其余符号确定变量名作用的类型。表述为:“指向类型的

在示例中,离变量名ref最近的符号是&,表述为“引用类型”。其余符号是const int,表述为“指向常量整型(整型常量)类型的”。

从右往左理解变量定义:

  • 离变量名最近的符号,对变量的类型有最直接的影响。表述为:“…类型”
  • 其余符号确定变量名作用的类型。表述为:“作用于…类型”

在示例中,离变量名p最近的符号是&,表述为“变量ref引用类型”。其余符号是const int,表述为“变量ref作用于常量整型(整型常量)”。

综上,“const int &ref = val;”是“指向整型常量引用类型”,即“指向常量的引用”(阅读定义)。

  • 普通引用不可指向常量
  • 指向常量的引用可指向常量
  • 指向常量的指针可指向非常量
  • 不能修改所指对象的值(因为所指对象的常量特性)

(理解定义)


技巧

从左往右阅读变量定义

  • 离变量名最近的符号,对变量的类型有最直接的影响。表述为:“…类型
  • 其余符号确定变量名作用的类型。表述为:“指向类型的

从右往左理解变量定义

  • 离变量名最近的符号,对变量的类型有最直接的影响。表述为:“…类型”
  • 其余符号确定变量名作用的类型。表述为:“作用于…类型”

顶、底层常量

  • 针对指针类型的判别,因为指针本身是一个对象,其可以指向另一个对象(概念实际上可以扩展到引用等复合类型)
  • 离变量名最近的const符号,是顶层
  • 离变量名最远的const符号,是底层
  • 顶层常量,表示对象本身是常量
  • 底层常量,表示对象所指的对象是常量

示例:

1
2
3
int val = 0;
int *const p1; //顶层常量,不能改变p1的值
const int *p2; //底层常量,不能通过p2改变所指对象val的值
  • 执行对象的拷贝(赋值)操作时,拷入和拷出的对象必须具有相同的底层常量资格;或者两个对象的数据类型能够相互转换,一般非常量能够转换成常量(除基本类型外

示例:

1
2
3
4
5
6
7
8
9
10
11
int val = 0;
int *p1 = &val;
const int *p2 = &val;
const int *p3 = &val;

p1 = p2; //× 不具有相同的底层常量资格,常量不能够转换成非常量
p2 = p3; //√ 具有相同的底层常量资格
p2 = &val; //√ 非常量能够转换成常量

const int val2 = 0;
val = val2; //√ 基本类型的常量能够转换成非常量

总结

  • 指针、引用和常量的关系因为不同位置、能够相互嵌套(套娃)而异常复杂
  • 建议分别或集中对比,简要了解其概念、理解几个简单示例。在理解的基础上总结区分技巧即可
  • 实际编程中几乎遇不到、用不到它们的复杂组合,可无需咬文嚼字、刨根问底

作者的话

  • 作者反复阅读书籍中对指针、引用和常量的描述,边理解边总结
  • 实际上这篇文章内容质量并不好,因为作者在分析时还是很容易懵逼。至少尽力地将自己的理解更好地表述出来
  • 文章内容基于读者对指针、引用和常量概念有一定的理解
  • 一些详细概念请参阅其他资料
  • 文章在描述时有疑惑的地方,请留言,定会一一耐心讨论、解答
  • 文章在认识上有错误的地方, 敬请批评指正
  • 望读者们都能有所收获

参考资料

《C++ Primer》中文版(第5版)作者:Stanley B. Lippman,Barbara E. Moo,JoséeLaJoie