本课时有配套视频讲解,购买后即可观看(永久有效)
指针基础
一、课上练习
编程练习
二、知识总结
✨ 指针的核心思想
指针是一种特殊的变量,只能用来存储内存的地址,使用 * 号表示其是一个指针。指针是 C++ 中非常重要的概念,它让我们能够直接操作内存,实现更灵活的数据处理。
下面通过一个例子来理解指针的核心原理:
int a = 42;
int *p = &a;1变量名 内存地址 存储的值
2------ -------- --------
3a 0x1000 42
4p 0x2000 0x1000 ──────┐
5 │
6 ┌──────────────────────────┘
7 │ p 存储了 a 的地址
8 ▼
9 0x1000 → 42 (*p 可以访问 a 的值)指针p本身也是一个变量,有自己的地址(0x2000),但它存储的内容不是普通数据,而是另一个变量a的地址(0x1000)。通过*p(解引用)就能顺着这个地址找到a的值 42。
✨ 指针定义
定义指针时需要指明指针指向内存所存储的数据类型及指针名称。
常见的指针定义方式如下:
指针定义代码示例
int *ptr_i; // 指针指向地址只能存储int类型数据
double *ptr_d; // 指针指向地址只能存储double类型数据
char *ptr_c; // 指针指向地址只能存储char类型数据
bool *ptr_b; // 指针指向地址只能存储bool类型数据✨ 指针赋值
指针有三种常见的赋值方式:
赋值为空指针,表示指针不指向任何有效地址:
赋值为空代码示例
int *ptr = nullptr;赋值为一个已存在变量的内存地址,使用取地址运算符 &:
赋值为变量地址代码示例
int number = 5;
int *ptr = &number;赋值为动态开辟的内存地址,使用 new 关键字:
赋值为动态内存地址代码示例
int *ptr = new int;✨ 指针的使用
使用指针内的地址
使用指针中存储的地址,直接输出指针即可。
输出指针地址代码示例
int number = 5;
int *ptr = &number;
cout << ptr << endl; // 输出number变量的地址使用指针内地址上的数据
使用指针中存储的地址中保存的数据,需要使用指针解引用的方式,即在指针名字之前加一个 * 号。
指针解引用代码示例
int number = 5;
int *ptr = &number;
cout << *ptr << endl; // 输出number变量内存储的数据,即5使用指针变量自己的地址
使用指针变量自己的地址时,需要使用取地址运算符,即在指针的名字前面加上一个 & 符号。
获取指针自身地址代码示例
int number = 5;
int *ptr = &number;
cout << &ptr << endl; // 输出ptr变量的地址✨ 按指针传参
按指针传递是指将实际参数的地址传递给函数参数,通过指针操作可以修改实际参数的值。
优点:
- 可以通过传递指针实现类似按引用传递的效果
- 适合需要传递可变长数组或动态分配内存的情况
缺点:
- 需要处理指针的合法性和内存管理问题,容易引发内存泄漏或非法访问
- 语法相对复杂,容易出错
按指针传参代码示例
1void foo(int *x) {
2 *x = 20;
3}
4
5int main() {
6 int a = 10;
7 foo(&a);
8 // a的值被修改为20
9 return 0;
10}✨ 动态内存
动态内存是指在程序运行时动态分配的内存,使用 new 的方式开辟,使用 delete 的方式释放。
动态内存代码示例
int *ptr = new int(3);
*ptr = 5;
delete ptr;
ptr = nullptr;动态内存使用注意事项:
- 使用过后一定需要使用
delete进行释放 - 若释放了一个指针指向的动态内存,一定要将指针赋值为
nullptr,否则指针就会变成野指针/悬挂指针
使用动态内存的优点:
- 可以灵活分配内存
- 节省内存空间
- 可以用来实现并处理复杂的大型数据结构
- 可以用来增加程序的可扩展性
- 可以支持多线程编程
✨ 指针的执行示例
下面通过具体的内存示意图,展示指针的工作过程。
例1:指针指向已有变量
int number = 42;
int *ptr = &number;变量名 内存地址(示意) 存储的值
------ ------------ --------
number 0x1000 42
ptr 0x2000 0x1000 (存储的是number的地址)1操作:
2cout << number; → 输出 42 (直接访问变量的值)
3cout << &number; → 输出 0x1000 (获取变量的地址)
4cout << ptr; → 输出 0x1000 (指针存储的地址)
5cout << *ptr; → 输出 42 (解引用:访问指针指向地址上的值)
6cout << &ptr; → 输出 0x2000 (指针变量自身的地址)例2:通过指针修改变量的值
int a = 10;
int *p = &a;
*p = 20; // 通过指针修改a的值
cout << a; // 输出20,a的值被修改了执行前: 执行 *p = 20 后:
a [0x1000]: 10 a [0x1000]: 20
p [0x2000]: 0x1000 p [0x2000]: 0x1000(不变)*p = 20的含义是:找到p指向的地址(0x1000),将该地址上的值修改为 20。因为a就在这个地址上,所以a的值也变了。
例3:按指针传参的执行过程
1void swap(int *x, int *y) {
2 int temp = *x;
3 *x = *y;
4 *y = temp;
5}
6
7int main() {
8 int a = 3, b = 7;
9 swap(&a, &b);
10 // 现在 a=7, b=3
11}1调用前:a=3(地址0x1000), b=7(地址0x1004)
2调用 swap(&a, &b):x=0x1000, y=0x1004
3
4执行 temp = *x; → temp = 3
5执行 *x = *y; → 地址0x1000的值改为7,即a=7
6执行 *y = temp; → 地址0x1004的值改为3,即b=3
7
8返回后:a=7, b=3 → 交换成功!例4:动态内存的生命周期
int *ptr = new int(100); // 在堆上分配内存,存储值100
cout << *ptr; // 输出 100
*ptr = 200; // 修改为 200
delete ptr; // 释放内存
ptr = nullptr; // 避免野指针new int(100)后: ptr → [堆内存: 100]
*ptr = 200后: ptr → [堆内存: 200]
delete ptr后: ptr → [已释放内存] ← 危险!此时ptr是野指针
ptr = nullptr后: ptr → [空] ← 安全✨ 指针的解题步骤
使用指针解决问题的一般思路:
- 明确目的:确定是否需要指针——通常在以下情况使用指针:
- 需要在函数中修改外部变量的值(按指针传参)
- 需要动态分配内存(运行时决定数组大小)
- 需要通过地址直接操作数组元素
- 定义指针并初始化:定义指针后一定要初始化,要么指向有效地址,要么设为
nullptr - 使用指针:通过
*解引用访问或修改数据,通过指针名直接获取地址 - 释放内存:如果使用了
new,用完后一定要delete,并将指针设为nullptr
✨ 指针的常见错误
- 使用未初始化的指针:定义指针后没有赋值就直接使用(如
int p; p = 5;),指针指向的是随机地址,会导致程序崩溃 - *混淆
的两种含义*:在定义时intp 中的表示"这是一个指针";在使用时p表示"解引用,访问指针指向的值"。两者含义不同 - 忘记取地址符
&:将变量赋给指针时忘记加&,如int p = number;应该写成int p = &number; - delete 后不置空:释放动态内存后忘记将指针设为
nullptr,之后再次使用该指针会导致未定义行为(野指针/悬挂指针) - new 和 delete 不匹配:
new分配的单个变量用delete释放,new[]分配的数组用delete[]释放,两者不能混用