指针是一个变量,存的是地址;引用是一个别名,本质是被引用对象本身。
本质区别
| 对比点 | 指针(Pointer) | 引用(Reference) |
|---|---|---|
| 本质 | 一个变量 | 另一个变量的别名 |
| 是否占内存 | 有独立内存 | 通常不单独占(编译器实现) |
| 是否可为空 | 可以 nullptr |
必须绑定对象 |
| 是否可改指向 | 可以 | 一旦绑定不能再换 |
| 是否必须初始化 | 可以先定义后赋值 | 必须初始化 |
| 使用方式 | *p 解引用 |
直接当原变量用 |
语法层面对比
指针
cpp
int a = 10;
int* p = &a;
*p = 20; // 修改 a
cout << a; // 20
特点:
p 是变量
p 里存的是 a 的地址
访问对象必须 *p
引用
cpp
int a = 10;
int& r = a;
r = 20; // 修改 a
cout << a; // 20
特点:
r 不是新对象
r 就是 a
用法和普通变量一样
内存层面理解
指针的内存示意
int a = 10;
int* p = &a;
a: 10
p: &a
a 占 4 字节
p 也占 8 字节(64 位系统)
引用的内存示意
int a = 10;
int& r = a;
a (r): 10
r 没有独立身份
编译器内部把 r 当作 a
引用在汇编层面通常被优化成直接访问原变量
能不能为 null?
指针:可以
cpp
int* p = nullptr;
常用于:
表示"暂时不指向任何对象"
链表、树、可选对象
引用:不行
cpp
int& r; // ❌ 编译错误
能不能"改绑"?
指针:可以改指向
cpp
int a = 1, b = 2;
int* p = &a;
p = &b; // 改指向 b
引用:不能改绑
cpp
int a = 1, b = 2;
int& r = a;
r = b; // ❌ 不是改绑定
这行代码的含义是:
把 b 的值赋给 a
绑定关系仍然是:r -> a
函数参数中的区别
指针传参
cpp
void func(int* p)
{
if (p)
*p = 100;
}
int a = 10;
func(&a);
特点:
需要传地址
可能为空(要判空)
更底层、更灵活
引用传参
cpp
void func(int& x)
{
x = 100;
}
int a = 10;
func(a);
特点:
语法更自然
不用判空
更安全
C++ 推荐:能用引用就用引用
什么时候用指针?什么时候用引用?
用引用的场景
函数参数
返回值(不为 null)
表达"别名关系"
不希望对象为空
用指针的场景
可能为空
需要动态内存
需要重新指向
数据结构(链表、树)