📖 学习信息
学习课程:黑马 C++ 零基础入门教程
本日学习:C++ 指针核心全套知识包含:指针的定义与基础使用、指针占用内存空间、空指针、野指针、const 修饰指针的三种形式、指针与数组结合、指针与函数(地址传递)学习目标:彻底理解指针的本质是地址,掌握指针的所有基础用法,区分值传递与地址传递,规避指针常见错误
一、前言
指针是 C++ 最核心也最容易出错的语法,同时也是 C++ 区别于其他高级语言的重要特性。指针的本质是存储内存地址的变量,通过指针可以直接操作计算机内存,这也是 C++ 执行效率高的根本原因。后续学习的数组、字符串、面向对象、动态内存分配、数据结构与算法,全部建立在指针的基础上,必须彻底吃透。
二、指针的定义与基础使用
1. 什么是指针
普通变量存储的是数据本身,而指针变量存储的是数据在内存中的地址。通过指针,我们可以间接访问和修改该地址指向的内存中的数据。
2. 基础语法
// 定义指针:数据类型 *指针变量名;
数据类型 *指针名;
// 给指针赋值:指针名 = &变量名;
// & 是取地址符,获取变量的内存地址
指针名 = &普通变量;
// 解引用:*指针名;
// * 是解引用符,访问指针指向的内存中的数据
*指针名 = 新值;
3. 完整代码示例
#include <iostream>
using namespace std;
int main()
{
// 1. 定义普通变量
int a = 10;
cout << "a的值:" << a << endl;
cout << "a的内存地址:" << &a << endl;
// 2. 定义指针变量,指向a的地址
int *p = &a;
cout << "指针p存储的地址:" << p << endl;
cout << "指针p指向的值:" << *p << endl;
// 3. 通过指针修改a的值
*p = 100;
cout << "修改后a的值:" << a << endl;
cout << "修改后*p的值:" << *p << endl;
return 0;
}
4. 核心概念区分
&a:取变量 a 的内存地址p:指针变量本身,存储的是地址值*p:解引用,访问指针 p 指向的内存中的数据
三、指针占用的内存空间
指针本身也是一个变量,会占用内存空间,其大小只与操作系统的位数有关,与指向的数据类型无关。
1. 内存大小规则
- 32 位操作系统:所有指针都占用 4 字节
- 64 位操作系统:所有指针都占用 8 字节
2. 代码验证
#include <iostream>
using namespace std;
int main()
{
int *p1;
double *p2;
char *p3;
// 输出不同类型指针的大小
cout << "int指针大小:" << sizeof(p1) << "字节" << endl;
cout << "double指针大小:" << sizeof(p2) << "字节" << endl;
cout << "char指针大小:" << sizeof(p3) << "字节" << endl;
return 0;
}
运行结果(64 位系统) :
int指针大小:8字节
double指针大小:8字节
char指针大小:8字节
四、空指针
1. 定义
空指针是指向内存地址 0的指针,用于初始化指针变量,避免指针变成野指针。
2. 语法
// 两种写法等价,推荐第二种(C++11及以上)
int *p = NULL;
int *p = nullptr;
3. 核心注意事项
空指针不能进行解引用操作,因为地址 0 是系统占用的内存区域,不允许用户访问,访问会导致程序崩溃。
int *p = NULL;
*p = 100; // 错误:空指针解引用,程序崩溃
4. 作用
- 初始化指针,避免指针指向随机内存
- 作为函数返回值,表示操作失败
五、野指针
1. 定义
野指针是指向非法内存空间的指针,这些内存空间可能未被分配,或者已经被释放。
2. 常见产生原因
-
指针未初始化
int *p; // 未初始化,指向随机内存地址 *p = 100; // 错误:野指针访问 -
指针释放后未置空
int *p = new int(10); delete p; // 释放内存,但p仍然指向原来的地址 *p = 100; // 错误:野指针访问 -
指针越界访问
int arr[5] = {1,2,3,4,5}; int *p = arr; p += 10; // 超出数组范围,变成野指针
3. 危害与避免方法
-
危害:程序崩溃、数据异常、难以调试的 bug
-
避免方法:
- 所有指针必须初始化(初始化为 NULL 或合法地址)
- 释放内存后立即将指针置为 NULL
- 避免指针越界访问
六、const 修饰指针(重点 + 难点)
const 修饰指针有三种形式,核心区别是指针的指向能不能改和指针指向的值能不能改。
1. 三种形式对比表
表格
| 形式 | 名称 | 指针指向 | 指向的值 | 示例 |
|---|---|---|---|---|
const 数据类型 *指针名; | 常量指针 | 可以改 | 不能改 | const int *p = &a; |
数据类型 * const 指针名; | 指针常量 | 不能改 | 可以改 | int * const p = &a; |
const 数据类型 * const 指针名; | 常量指针常量 | 不能改 | 不能改 | const int * const p = &a; |
2. 记忆口诀
- const 在前,值不能改(常量指针)
- const 在后,指向不能改(指针常量)
- const 前后都有,都不能改
3. 代码示例
#include <iostream>
using namespace std;
int main()
{
int a = 10;
int b = 20;
// 1. 常量指针:值不能改,指向可以改
const int *p1 = &a;
// *p1 = 100; // 错误:指向的值不能改
p1 = &b; // 正确:指向可以改
// 2. 指针常量:指向不能改,值可以改
int * const p2 = &a;
*p2 = 100; // 正确:指向的值可以改
// p2 = &b; // 错误:指向不能改
// 3. 常量指针常量:都不能改
const int * const p3 = &a;
// *p3 = 100; // 错误
// p3 = &b; // 错误
return 0;
}
七、指针与数组
1. 数组名的本质
数组名是指向数组首元素的常量指针,不能被赋值。
int arr[5] = {1,2,3,4,5};
int *p = arr; // 等价于 int *p = &arr[0];
// arr = &arr[1]; // 错误:数组名是常量指针,不能修改指向
2. 用指针遍历数组
利用指针的自增操作,可以遍历数组的所有元素,这是指针最常用的场景之一。
#include <iostream>
using namespace std;
int main()
{
int arr[5] = {1,2,3,4,5};
int *p = arr;
// 用指针遍历数组
for (int i = 0; i < 5; i++) {
cout << *p << " ";
p++; // 指针自增,指向下一个元素
}
return 0;
}
3. 指针与数组的关系
arr[i]等价于*(arr + i)等价于*(p + i)- 数组下标本质上是指针的偏移量
八、指针与函数(地址传递)
1. 什么是地址传递
将变量的内存地址作为实参传递给函数,形参是接收地址的指针。通过地址传递,函数内部可以通过指针直接修改实参的值,这是值传递做不到的。
2. 经典示例:用地址传递交换两个数
#include <iostream>
using namespace std;
// 地址传递:形参是指针
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main()
{
int x = 10;
int y = 20;
cout << "交换前:x = " << x << ",y = " << y << endl;
// 传递变量的地址
swap(&x, &y);
cout << "交换后:x = " << x << ",y = " << y << endl;
return 0;
}
3. 值传递 vs 地址传递 核心区别
表格
| 传递方式 | 本质 | 能否修改实参 | 适用场景 |
|---|---|---|---|
| 值传递 | 拷贝实参的值给形参 | 不能 | 只需要使用实参的值,不需要修改 |
| 地址传递 | 拷贝实参的地址给形参 | 能 | 需要在函数内部修改实参的值 |
九、常见错误与避坑点
- 空指针解引用:永远不要对 NULL 或 nullptr 进行解引用
- 野指针访问:所有指针必须初始化,释放后置空
- const 修饰混淆:牢记口诀,区分常量指针和指针常量
- 指针越界:访问数组时不要超出下标范围
- 数组名赋值:数组名是常量指针,不能修改指向
- 忘记解引用:直接给指针赋值会修改指针的指向,而不是指向的值
十、今日学习总结
-
指针本质:存储内存地址的变量,通过解引用访问指向的内存
-
内存大小:32 位系统 4 字节,64 位系统 8 字节,与类型无关
-
空指针:指向地址 0,用于初始化,不能解引用
-
野指针:指向非法内存,必须避免
-
const 修饰指针:
- 常量指针:
const int *p,值不能改,指向可以改 - 指针常量:
int * const p,指向不能改,值可以改 - 常量指针常量:都不能改
- 常量指针:
-
指针与数组:数组名是指向首元素的常量指针,可用指针遍历数组
-
地址传递:传递变量地址,函数内部可修改实参的值
✍️ 下节预告
下一篇将学习 C++ 结构体核心知识:结构体的定义与初始化、结构体成员访问、结构体数组、结构体指针、结构体作为函数参数(值传递与地址传递) ,掌握结构体可以将不同类型的数据封装成一个整体,是面向对象编程的基础雏形,为后续学习类和对象打下关键过渡基础。