目录
什么是指针?整体性把握一下!
定义
指针(Pointer)是一种特殊的变量,它存储的不是普通的数据值(如 5, ‘A’, 3.14),而是另一个变量在内存中的地址。
为什么需要指针?
指针的出现解决了编程中的许多难题:
- 高效的函数参数传递:当需要向函数传递一个大型结构体或对象时,直接传递整个数据会产生巨大的开销(需要完整复制一份)。而传递指针(通常只有 4 或 8 个字节),函数可以直接在原始数据上进行修改,效率极高。
- 实现动态内存管理:在程序运行时,我们需要动态地创建和销毁变量(例如,创建一个大小不固定的数组)。这只能通过
new(或malloc) 在堆上分配内存,并通过指针来访问这块新内存。 - 处理复杂数据结构:链表、树、图等高级数据结构的核心就是节点之间的“链接”,这种链接关系正是通过指针来实现的。
- 直接操作内存:在某些底层编程场景(如操作系统、嵌入式开发),需要直接操作硬件内存地址,指针是实现这一功能的唯一途径。
指针的基本语法
1. 声明一个指针
声明指针需要使用 *(星号)符号。
// 语法: 数据类型 *指针名;
int *p_num; // p_num 是一个指向 int 类型数据的指针
double *p_price; // p_price 是一个指向 double 类型数据的指针
char *p_char; // p_char 是一个指向 char 类型数据的指针
2. 获取地址(& 运算符)
如果我们想得到一个变量的地址,需要使用取地址运算符 &。
int age = 25;
int *p_age = &age; // 将 age 变量的地址赋值给 p_age 指针
现在,p_age 中存储的就是 age 变量的内存地址。
3. 解引用(* 运算符)
解引用就是通过指针,访问它所指向地址的值。
int age = 25;
int *p_age = &age;
cout << "通过变量访问 age: " << age << endl; // 输出 25
cout << "通过指针访问 age: " << *p_age << endl; // 输出 25
*p_age 的意思就是:“找到 p_age 中存储的地址,然后去该地址读取值”。
4. 修改指针指向的值
既然可以访问,当然也可以修改。
int score = 88;
int *p_score = &score;
cout << "修改前 score: " << score << endl; // 输出 88
*p_score = 95; // 通过指针修改 score 的值
cout << "修改后 score: " << score << endl; // 输出 95
指针的运算
指针可以进行有限的算术运算,最常见的是指针的递增和递减。
关键规则:指针的移动单位不是固定的字节,而是它所指向的数据类型的大小。
int arr[5] = {10, 20, 30, 40, 50};
int *p_arr = arr; // 数组名 arr 会“衰变”为其首元素的地址
cout << "p_arr 指向的值: " << *p_arr << endl; // 输出 10
p_arr++; // p_arr 向后移动一个 int 的距离(通常是 4 字节)
cout << "p_arr++ 后指向的值: " << *p_arr << endl; // 输出 20
p_arr--; // p_arr 向前移动一个 int 的距离
cout << "p_arr-- 后指向的值: " << *p_arr << endl; // 输出 10
p_arr++实际上执行的是p_arr = p_arr + sizeof(int)。- 指针的加减运算在遍历数组时非常有用。
指针的特殊类型
1. 空指针 (nullptr / NULL)
空指针是一个不指向任何有效内存地址的指针。它是一个特殊的值,用于表示“没有指向任何东西”。
在 C++11 及以后版本,推荐使用 nullptr。
int *p_null = nullptr; // 推荐
// int *p_null = NULL; // C 风格,也可以
// 在使用前必须检查指针是否为空
if (p_null != nullptr) {
cout << *p_null << endl; // 不会执行到这里
} else {
cout << "p_null 是一个空指针,不能解引用!" << endl;
}
解引用空指针会导致程序崩溃(段错误 Segment Fault),所以这是一个必须遵守的规则。
2. 指针和数组的关系
在 C/C++ 中,数组名和指向数组首元素的指针在很多情况下可以互换使用。
int numbers[3] = {1, 2, 3};
int *p = numbers; // p 指向 numbers[0]
// 使用指针访问数组元素
cout << p[0] << endl; // 输出 1
cout << p[1] << endl; // 输出 2
cout << *(p + 2) << endl; // 输出 3
// 数组名也可以看作一个常量指针
// numbers++; // 错误!数组名是常量指针,不能修改
3. 指针和函数
a) 指针作为函数参数
这是指针最常见的用途之一,实现“传地址调用”。
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main() {
int x = 10, y = 20;
cout << "交换前: x = " << x << ", y = " << y << endl;
swap(&x, &y); // 传递 x 和 y 的地址
cout << "交换后: x = " << x << ", y = " << y << endl;
return 0;
}
如果这里传递的是 x 和 y 的值(swap(x, y)),函数内的交换不会影响外部的 x 和 y。
b) 函数指针
函数指针是一个指向函数的指针,它存储的是函数的入口地址。
// 定义一个函数
int add(int a, int b) {
return a + b;
}
int main() {
// 声明一个函数指针 p_func
// 它指向一个返回 int, 参数为 (int, int) 的函数
int (*p_func)(int, int);
// 将 add 函数的地址赋给 p_func
p_func = add;
// 通过函数指针调用函数
int result = p_func(5, 3);
cout << "结果: " << result << endl; // 输出 8
return 0;
}
函数指针在回调、实现多态等高级编程中非常有用。
指针的风险与最佳实践
指针功能强大,但也非常危险。不当使用会导致严重问题。
-
野指针
- 定义:一个指向“无效内存区域”的指针。它可能未被初始化,或者指向的内存已经被释放。
- 危害:解引用野指针会导致程序崩溃或不可预测的行为。
- 预防:永远在定义指针时立即初始化。如果暂时没有指向的对象,就初始化为
nullptr。
int *p_bad; // 危险!野指针 int *p_safe = nullptr; // 安全 -
内存泄漏
- 定义:当使用
new(或malloc) 在堆上分配了内存后,如果没有使用delete(或free) 释放它,并且指向这块内存的指针丢失了,这块内存就再也无法被访问和回收,造成了泄漏。 - 危害:长期运行的程序会因内存泄漏而耗尽所有可用内存,最终崩溃。
- 预防:对于
new出来的内存,必须有对应的delete。在现代 C++ 中,推荐使用智能指针(std::unique_ptr,std::shared_ptr)来自动管理内存,避免忘记delete。
- 定义:当使用
-
重复释放
- 定义:对同一块内存调用两次
delete。 - 危害:导致未定义行为,通常程序会崩溃。
- 预防:确保每个
new只对应一个delete。
- 定义:对同一块内存调用两次
现代 C++ 的替代方案(智能指针)
为了解决原始指针带来的内存管理难题,C++11 引入了智能指针。它们是封装了原始指针的类对象,能够在其生命周期结束时自动释放所指向的内存,极大地提高了代码的安全性和可维护性。
std::unique_ptr:独占所有权的指针。一个对象只能被一个unique_ptr拥有。std::shared_ptr:共享所有权的指针。多个shared_ptr可以指向同一个对象,它会自动记录引用计数,当最后一个shared_ptr被销毁时,内存才会被释放。
示例:
#include <iostream>
#include <memory> // 包含智能指针头文件
void useSmartPointer() {
// 使用 unique_ptr 自动管理内存
std::unique_ptr<int> p_unique = std::make_unique<int>(100);
std::cout << "unique_ptr 的值: " << *p_unique << std::endl;
// p_unique 的生命周期结束时,delete 会被自动调用
} // p_unique 在这里被销毁,内存自动释放
int main() {
useSmartPointer();
std::cout << "函数执行完毕,内存已自动释放。" << std::endl;
return 0;
}
总结表格
| 特性 | 描述 |
|---|---|
| 核心概念 | 指针是存储内存地址的变量。 |
& 运算符 | 取地址,用于获取变量的内存地址。 |
* 运算符 | 解引用,用于访问指针指向地址的值。 |
| 主要用途 | 高效参数传递、动态内存管理、构建复杂数据结构。 |
| 关键类型 | 空指针 (nullptr)、函数指针。 |
| 主要风险 | 野指针、内存泄漏、重复释放。 |
| 现代方案 | 智能指针 (std::unique_ptr, std::shared_ptr) 用于自动内存管理。 |
(PS:面试问到了)
2096

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



