跳转至

指针与引用

变量、值和地址

int a = 10;

这句话表示:

  • 定义了一个整型变量 a
  • 给它赋值为 10

从程序表面看,a 里存的是 10

但从计算机内部看,变量并不是“飘在空中”的,它一定放在内存中的某个位置。

所以一个变量,实际上有两层信息:

  • 地址

这里的地址,可以理解成变量在内存中的“位置编号”或者“门牌号”。

int a = 10;
cout << a << endl;    // 输出值
cout << &a << endl;   // 输出地址

指针

指针是一个专门用来存放地址的变量。

普通变量存的是普通数据,比如整数、字符、实数。

而指针变量存的不是这些值,它存的是“某个变量所在的位置”。

int a = 10;
int *p = &a;

这句话的意思是:

  • a 是一个整型变量
  • &aa 的地址
  • p 是一个指针变量
  • p 中存放的是 a 的地址

也可以用一句更直观的话来理解:

指针 p 指向变量 a

指针的声明

类型 *指针变量名;

int *p;
char *q;
double *r;

指针也要分类型,

如果不知道那个地址里存的是哪种类型的数据,程序就无法正确理解那块内存里的内容。

引用

引用是一个已有变量的别名。

所谓“别名”,就是另一个名字。

它不是独立开辟出来的新变量,而是给原来的变量再起一个名字。

int a = 10;
int &b = a;

这句话表示:

  • a 是一个整型变量
  • ba 的引用
  • ba 的另一个名字

所以此时:

  • ab 表示的是同一个对象
  • ab 会跟着变
  • ba 也会跟着变

引用的声明

类型 &引用名 = 变量名;

int a = 10;
int &b = a;

这表示 ba 的引用。

引用在定义时就必须绑定到某个变量。

它不能只声明,不绑定。

引用和原变量的关系

引用不是复制,也不是重新存一份值。

它本质上还是在表示原来的那个变量。

所以可以把引用理解为:

同一个人有两个名字。

int a = 10;
int &b = a;

cout << a << endl;
cout << b << endl;

输出的都是同一个值。

如果再写:

b = 20;

那么 a 也会变成 20

因为这里不是“把 20 赋给一个新变量”,而是“通过 b 这个名字去修改 a”。

指针与引用的核心区别

1、指针是变量,引用是别名

指针

指针本身是一个变量。

它有自己单独的存储空间,里面存的是某个地址。

引用

引用不是一个独立的新变量。

它只是已有变量的另一个名字。

2、指针存地址,引用直接代表原变量

指针

指针变量中保存的是地址,所以:

  • p 表示地址
  • *p 才表示那个地址中的值

引用

引用没有“先存地址,再取值”这一层。

它直接就当原变量使用。

所以:

  • b 直接就是原变量的别名
  • 不需要额外写 *

3、指针可以改变指向,引用一般不能改绑

int a = 10, b = 20;
int *p = &a;
p = &b;

这里是可以的,因为指针里存的是地址,地址可以换。

但引用不同:

int a = 10, b = 20;
int &r = a;

此后 r 就一直是 a 的别名。

它不能再变成 b 的别名。

这也是引用和指针一个非常本质的差别。

指针的基本用法

1、取地址运算符 &

int a = 10;
cout << &a << endl;

这里 &a 表示变量 a 在内存中的地址。

2、用指针保存地址

int a = 10;
int *p = &a;

这里:

  • &aa 的地址
  • p 保存了这个地址

所以 p 指向 a

这时候:

cout << p << endl;

输出的是地址。

3、解引用运算符 *

* 出现在已经定义好的指针变量前面时,表示:

取出这个指针所指向地址中的值

例如:

int a = 10;
int *p = &a;
cout << *p << endl;

这里:

  • p 是地址
  • *p 是这个地址里的值

由于 p 里存的是 a 的地址,所以 *p 就表示 a 的值。

4、通过指针访问和修改数据

int a = 10;
int *p = &a;

cout << *p << endl;
*p = 20;
cout << a << endl;

这里:

  • *p 先访问到 a 的值
  • 再通过 *p = 20 修改了 a

所以可以理解成:

指针像一条线,先找到地址,再通过这个地址操作对应的数据。

引用的基本用法

1、定义引用

int a = 10;
int &b = a;

此时 ba 的引用。

也就是说:

  • b 就是 a
  • a 也是 b

它们是同一个对象的两个名字。

2、像普通变量一样使用引用

int a = 10;
int &b = a;

cout << b << endl;
b = 20;
cout << a << endl;

这里:

  • 读取 b,就是读取 a
  • 修改 b,就是修改 a

所以引用最大的特点就是:

用起来像普通变量,但本质上不是一份新的数据。

3、引用必须在定义时初始化

int a = 10;
int &b = a;

这是正确的。

而下面这种写法不行:

int &b;

因为引用不是独立变量,它必须一开始就说明“它是谁的别名”。

*& 的含义

1、* 的两种常见含义:

第一种:声明指针

int *p;

表示 p 是一个指向 int 的指针。

第二种:解引用

*p

表示取出指针 p 所指向地址中的值。

所以同样要看场景:

  • 放在定义里,表示“这是个指针”
  • 放在表达式里,表示“取这个指针指向的值”

2、& 的两种常见含义

第一种:取地址

&a

第二种:声明引用

int &b = a;

表示 ba 的引用。

所以要根据上下文判断:

  • 放在表达式里,常表示取地址
  • 放在类型后面,常表示引用

易错点

1、a&ap*p 分别是什么

int a = 10;
int *p = &a;

那么:

  • a 是变量中的值
  • &a 是变量 a 的地址
  • p 是存放地址的指针变量
  • *pp 指向地址中的值

最关键的两组关系是:

  • p = &a
  • *p = a

2、指针变量和它指向的数据不是一回事

int a = 10;
int *p = &a;

这里:

  • p 自己是一个变量
  • p 里存的是地址
  • *p 才是这个地址里的值

所以不能把 p*p 混为一谈。

3、引用不是拷贝

int a = 10;
int &b = a;

这不是说“把 a 的值复制给 b”。

而是说:

4、ba 表示的是同一个变量

所以引用和普通赋值完全不是一回事。