Appearance
指针 / 引用 / 悬空 / 野 / 常量指针
一、指针 vs 引用的区别(高频)
| 维度 | 指针 | 引用 |
|---|---|---|
| 可变绑定 | 可重新指向别处 ✅ | 一旦绑定不可改 |
| 是否占内存 | 占有(存地址) | 别名本质,物理上可不占位(编译器优化) 实现层面通常也占一个 slot |
| 可否为空 | 可(nullptr/未初始化野指针) | 必须绑定有效对象,无空引用 |
| 多级 | 可 int** | 不可(无引用的引用) |
| 语义 | 值=地址,需解引用 *p | 直接当成别名用 r |
| 安全性 | 需判空、易悬空 | 更安全(无天然空、生命周期经编译器追踪难悬空) |
| 重载 | — | 支持引用重载,引用于左值可区分 |
记法:引用是「已有对象的别名/变量别名」,不 new 不 delete;指针是「存地址的变量」。
传参建议:想在函数内改实参/避免拷贝 → 尽量用引用(const T& 只读);需要可空 / 指向数组循环 / 表示"无值"或所有权 → 用指针。提供 "& 给左值传参可同时保留修改 + 避免拷贝" 的体感答题。
二、悬空指针 vs 野指针
| 类型 | 定义 | 成因 | 结果 |
|---|---|---|---|
| 悬空指针 | 指向已被释放对象 | delete 后没置空、返回局部地址、容器扩容后引用等 | 解引用=UB(可能仍是旧值/崩) |
| 野指针 | 指向随机未知地址 | 未初始化指针 | 解引用=UB(Segfault 常见) |
避免/修复:
- 指针必须初始化(
T* p = nullptr)。 delete后立即置空(delete p; p = nullptr;)仍有人误用 null 不崩。- 优先智能指针杜绝手动 delete。
- 不返回局部对象地址(返回引用给调用方长期用)——局部变量离开函数帧即失效。
三、常量指针 vs 指针常量(必考混淆题)
口诀:看 const 与 * 的相对位置——* 左边修饰"指向的对象",* 右边修饰"指针本身"。
1. 常量指针(pointer to const,指向常量,const 在 * 左)
cpp
const int* p; // p 指向的对象是 const:不能 *p=…
int const* p2; // 等价- 可修改
p本身(改指向),不能通过*p改对象值。
2. 指针常量(const pointer,指针本身是常量,const 在 * 右)
cpp
int* const p; // p 本身 const:不能改 p 指向- 可
*p = …改对象值,不能改 p 本身。
3. 都常量
cpp
const int* const p; // 既不能改指向,也不能改对象值记忆
写一行代码最清楚:
cpp
int x=1,y=2;
const int* a = &x; a = &y; // ✅ 可改指向 *a=… ❌
int* const b = &x; *b = 3; // ✅ 可改值 b=&y ❌四、指针的有趣题
Q1. 空指针/函数指针/成员函数指针
nullptr(C++11)类型std::nullptr_t,可转任何指针类型;比NULL(宏=0)类型安全、不可与整数混淆。- 函数指针:
int(*fp)(int,int)=add;调用fp(1,2)。 - 成员函数指针:
int(A::*fp)()=&A::add;需用(a.*fp)()调用,与对象绑定。
Q2. 指针大小
64 位下指针恒 8 字节(不管指向 int/double/自定义),且不同指针类型都 8。空指针/指向栈/堆地址的指针都是 8。
Q3. 为什么用 void* 不能直接加减?
void 大小不确定(不能求 sizeof(void)),标准不允许 void* 算术,需先转 char*/unsigned char* 再按字节。
Q4. this 指针
- 指向「当前对象」的指针(类型
T* const,不可改 this 本身,可解引用)。 - const 成员函数里的 this 是
const T*,不能借此改对象非 mutable 成员。 - this 在编译期是隐式第一参数(成员函数实现为带 this 的普通函数:
func(T* this, 参数…))。
Q5. 引用与指针底层
引用实现通常就是一个指针(存地址),所以 sizeof(引用变量)=指向类型大小,占用与指针相同 slot。但语义上不是对象,不产生独立赋值/复制为地址的行为(拷贝引用即拷贝对象,除非是 const 引用到临时等)。