核心要点速览
- 指针:存储内存地址的变量,支持空值(nullptr)、动态指向、多级访问,存在野指针风险
- 引用:变量的别名,必须初始化、不可改指向、无空引用,语法简洁安全
- 访问效率:两者基本一致
- 适用场景:需空值 / 动态指向 / 多级访问用指针;需简洁语法 / 确保有效 / 避免误操作引用
一、指针
指针本质是 “存储内存地址的变量”,支持对内存的间接访问。
1. 关键类型与特性
(1)野指针
- 定义:未初始化、已释放或越界访问的指针(指向随机 / 无效内存)。
- 风险:访问野指针会导致内存错误(崩溃、数据错乱),是常见 bug 来源。
- 示例(错误):
int* p; // 未初始化,野指针
*p = 10; // 未定义行为(访问随机内存)
```
- 规避:指针定义时立即初始化(如`int* p = nullptr;`),释放后及时置空(`p = nullptr;`)。
#### (2)空指针(nullptr)
- 定义:C++11 标准的空指针常量,替代旧版`NULL`(`NULL`本质是`0`,易引发歧义)。
- 用途:表示指针 “无指向”,避免野指针,可通过条件判断安全使用。
- 示例:
```cpp
int* p = nullptr;
if (p == nullptr) { // 安全判断空指针
// 避免无效访问
}
```
#### (3)函数指针
- 定义:指向函数的指针,语法:`返回类型 (*指针名)(参数类型列表)`。
- 核心用途:作为函数参数 / 返回值,实现回调函数(如排序算法中的比较器)。
- 示例:
```cpp
// 目标函数:两数相加
int add(int a, int b) { return a + b; }
int main() {
int (*func_ptr)(int, int) = add; // 函数指针指向add
int res = func_ptr(3, 5); // 调用:res=8(等价于(*func_ptr)(3,5))
return 0;
}
(4)指针数组与数组指针
核心原则:[]优先级高于*,()可提升*的优先级,需通过语法结构判断本质。
| 类型 | 语法格式 | 核心逻辑(优先级分析) | 示例 |
|---|---|---|---|
| 指针数组 | 类型* 数组名[数组长度]; | []优先级高,先构成数组,元素是指针 | int* arr[3];(3 个 int * 指针的数组) |
| 数组指针 | 类型 (*指针名)[数组长度]; | ()提升*优先级,先是指针,指向 “固定长度的数组” | int (*p)[3];(指向含 3 个 int 的数组) |
- 示例解析:
int a[3] = {1,2,3};
int* arr[3]; // 指针数组:每个元素是int*,可指向不同int变量
int (*p)[3] = &a; // 数组指针:p指向整个数组a,*p等价于a(数组首地址)
2. 指针的核心操作
- 解引用:
*p,访问指针指向的变量值。 - 取地址:
&变量,获取变量的内存地址,赋值给指针。 - 算术运算:仅对 “指向数组的指针” 有意义,步长 = 指向类型的大小(如
int* p,p++移动 4 字节)。
二、引用
引用本质是 “变量的别名”,语法上无独立内存(编译器通常用指针实现,但对外隐藏细节),核心优势是简洁安全。
1. 核心特性
-
必须初始化:定义时必须绑定已存在的变量(无空引用),否则编译报错。
int a = 10; int& r = a; // 正确:绑定变量a // int& r; // 错误:未初始化 // int& r = nullptr; // 错误:无空引用 -
不可改指向:绑定后无法切换到其他变量,赋值操作是修改 “绑定变量的值”。
int b = 20; r = b; // 不是更改绑定,而是将a的值改为20(a=20,r仍绑定a) -
内存特性:
sizeof(r) = sizeof(原变量)(无额外内存开销)。cout << sizeof(r); // 4字节(与int类型大小一致)
2. 应用
(1)函数参数(避免拷贝 + 修改实参)
- 替代指针,语法更简洁,无野指针风险,适合大对象传递(无拷贝开销)。
- 示例:
void update(int& x) { x *= 2; } int a = 5; update(a); // a=10(直接修改实参)
(2)函数返回值(避免拷贝)
-
可返回 “全局变量、静态变量、类成员变量” 的引用(生命周期长于函数),避免返回对象的拷贝开销。
-
禁忌:禁止返回局部变量的引用(函数结束后局部变量销毁,引用变为悬垂引用)。
-
示例(正确):
int g_val = 10; int& get_global() { return g_val; } // 返回全局变量引用,安全 -
示例(错误):
int& get_local() { int x = 20; return x; // 错误:x是局部变量,函数结束后销毁 }
三、本质区别
- 指针:独立的变量,存储目标变量的内存地址(占用 4/8 字节内存,与系统位数相关)。
- 引用:非独立变量,是目标变量的 “别名”(语法层面无独立内存,编译器通常用指针实现,但对外隐藏指针操作)。
四、特性对比表
| 对比特性 | 指针(Pointer) | 引用(Reference) |
|---|---|---|
| 初始化 | 可延迟初始化(但未初始化是野指针) | 必须定义时初始化,且绑定已存在变量 |
| 指向可修改性 | 可重新指向其他变量(p = &b; 合法) | 绑定后不可更改指向(r = b; 是修改原变量值) |
| 空值支持 | 支持(int* p = nullptr;) | 无空引用(必须绑定有效变量) |
| 解引用操作 | 需显式用 *(*p = 10;) | 无需解引用(r = 10; 直接修改绑定变量) |
| 算术运算 | 支持(步长 = 指向类型大小) | 不支持(非独立对象,无地址可运算) |
| 多级嵌套 | 支持(int** p 指向指针的指针) | 不支持(int&& r 是右值引用,非 “引用的引用”) |
| 语法复杂度 | 较高(需关注*/&/ 空指针 / 野指针) | 较低(简洁直观,无指针操作风险) |
五、补充知识点
1. 作为函数参数的传递机制
(1)指针传参
- 本质:“值传递地址”—— 函数接收的是实参地址的拷贝,修改指针本身(如
p = &b;)不影响实参指针;但修改指针指向的内容(*p = 10;)会影响实参变量。 - 示例:
void func(int* p) { *p = 20; // 影响实参变量(修改指向的内容) p = nullptr; // 不影响实参指针(仅修改拷贝) } int a = 10; int* ptr = &a; func(ptr); // a=20,ptr仍指向a(未变)
(2)引用传参
- 本质:传递 “变量别名”—— 函数内修改引用(
r = 20;)直接修改实参;且无法修改引用的指向(语法禁止)。
2. 返回值的 “悬垂问题”
- 指针与引用的共同禁忌:禁止返回局部变量(生命周期随函数结束而销毁)。
- 返回局部变量的指针:得到野指针(指向已释放内存),访问即未定义行为。
- 返回局部变量的引用:得到悬垂引用(绑定已销毁变量),访问即未定义行为。
3. const 修饰
(1)const 修饰指针(左定值,右定向)
- 核心口诀:
const靠近谁,就限制谁 —— 左定值(限制指向的内容不可改),右定向(限制指针本身不可改)。const int* p; // 常量指针(左定值):指向的内容不可改(*p不能赋值),指针可改指向 int* const p; // 指针常量(右定向):指针本身不可改指向,指向的内容可改(*p可赋值) const int* const p; // 指向常量的指针常量:内容和指向均不可改
(2)const 引用(const T&)
- 核心特性:不可通过引用修改绑定变量,但变量本身可被其他方式修改。
- 关键用途:可绑定临时对象(字面量、表达式结果),常用于函数参数(接收任意类型参数,避免拷贝)。
- 示例:
const int& r1 = 10; // 合法:const引用绑定字面量(临时对象) const int& r2 = 3 + 4; // 合法:绑定表达式结果 int a = 5; const int& r3 = a; // r3 = 10; // 错误:const引用不可修改绑定变量 a = 10; // 合法:变量本身可通过其他方式修改(r3的值同步变为10)

808

被折叠的 条评论
为什么被折叠?



