相比于C语言指针,C++指针做了大量扩展和优化,新增了引用、空指针常量、智能指针、野指针规避机制等重要特性。本文将从零开始,循序渐进讲解C++指针的基础核心知识点、高频用法、易错坑点,以及C++专属高阶扩展内容,帮你彻底吃透指针,告别模糊认知。
一、指针最核心的底层本质
1.1 什么是指针?
一句话总结:指针就是存储内存地址的变量。
普通变量存储的是「数据值」,指针变量存储的是「另一个变量的内存地址」。
1.2 指针核心运算符
指针的所有操作,都围绕两个运算符展开,是所有知识点的基础:
-
& 取地址符:获取变量的内存地址
-
* 解引用符:通过指针地址,找到并操作对应内存的值
基础代码示例:
#include <iostream>
using namespace std;
int main() {
int a = 10; // 定义普通变量,内存存值10
int* p = &a; // 指针p存储变量a的地址
cout << "a的值:" << a << endl; // 输出10
cout << "a的地址:" << &a << endl; // 输出a的内存地址
cout << "p存储的地址:" << p << endl; // 和&a完全一致
cout << "p解引用的值:" << *p << endl; // 通过地址取值,输出10
*p = 20; // 通过指针修改a的值
cout << "修改后a的值:" << a << endl; // 输出20
return 0;
}
1.3 指针变量的大小
不同类型的指针(int*、char*、double*)大小相同,与类型无关。
核心结论:指针的大小只由操作系统位数决定,和指向的类型无关!
-
32位系统:所有指针大小都是4字节
-
64位系统:所有指针大小都是 8字节
原因:指针存储的是内存地址,系统位数决定了地址的长度,和指向的数据类型无关。
二、C++基础指针核心知识点
2.1 指针的定义规范
标准定义格式:数据类型* 指针变量名
注意:数据类型代表「指针指向的内存的数据类型」,不是指针本身的类型。
避坑重点:连续定义指针时,* 属于变量,不属于类型!
int* p1, p2; // 错误认知:两个指针 | 实际:p1是int*指针,p2是int普通变量
int *p3, *p4; // 正确:两个int类型指针
2.2 空指针、野指针、悬空指针
1. 空指针(合法、安全)
指针指向「编号为0的受保护内存空间」,表示指针暂时不指向任何有效变量。
C++11 推荐用法:nullptr(替代C语言的NULL)
int* p = nullptr; // C++标准空指针,类型安全
// 禁止对空指针解引用!会直接程序崩溃
// *p = 100; // 运行报错
优势:nullptr 是专属空指针类型,避免了C语言NULL(本质是0)的类型混淆问题。
2. 野指针(非法、危险)
指针指向「随机的、未知的内存地址」,没有初始化、没有合法指向。
产生原因:局部指针变量未初始化、指针越界、指向已释放的内存。
危害:程序随机崩溃、内存篡改、隐形Bug。
3. 悬空指针(非法、危险)
指针原本指向合法内存,后续内存被释放/销毁,但指针变量本身没有置空。
核心区别:野指针是一开始就无合法指向,悬空指针是曾经合法,现在失效。
解决方案:内存释放后,立即将指针置为 nullptr。
2.3 指针与数组
数组名本质是数组首元素的常量指针。
核心特性:数组名可以隐式转换为指针,支持指针偏移访问数组元素。
int arr[] = {1,2,3,4,5};
int* p = arr; // 数组名直接赋值指针,等价于 &arr[0]
// 指针偏移访问元素
cout << *(p+1) << endl; // 输出2,p+1跳过一个int字节
cout << arr[2] << endl; // 普通下标访问
cout << *(arr+2) << endl;// 指针方式访问,和下标等价
关键结论:arr[i] == *(arr+i) == *(p+i) == p[i],四种写法完全等价。
2.4 指针与函数
1. 指针做函数参数(实现地址传递)
普通参数传递是「值传递」,函数内修改的是副本,不影响实参;指针传递是地址传递,直接操作原变量内存,可以修改实参。
经典交换变量示例:
void swap(int* a, int* b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main() {
int x = 10, y = 20;
swap(&x, &y); // 传入地址
// 成功交换x、y的值
return 0;
}
2. 函数指针
指向函数的指针,存储函数的代码入口地址,可用于回调函数、多态模拟。
// 定义函数指针:返回值(int)、参数(int,int)
int (*fp)(int, int);//(*fp)中括号不能丢
// 绑定函数
int add(int a, int b) { return a+b; }
fp = add;
// 调用函数
cout << fp(1,2) << endl; // 输出3
- 用途1:实现通用算法,一套代码适配多种逻辑比如写一个万能计算函数,不用写多个if/else判断加减乘除,通过函数指针传入不同运算逻辑,极度精简代码:
// 通用计算函数:接收函数指针,适配任意二元运算 int calc(int a, int b, int (*func)(int, int)) { return func(a, b); } int main() { // 同一套calc函数,实现不同运算 cout << calc(20, 10, add) << endl; // 加法30 cout << calc(20, 10, sub) << endl; // 减法10 return 0; } - 用途2:回调函数(GUI界面、定时器、网络请求核心用法)比如点击按钮后执行指定逻辑,框架内部预留函数指针接口,我们传入自定义函数,触发事件时自动调用。
- 用途3:C++多态底层模拟、动态策略模式,是面向对象多态的底层原理之一。
三、C++指针扩展知识点(区别于C语言)
这部分是C++指针的核心进阶内容,也是工程开发的重点,完全区别于C语言基础指针。
3.1 指针 vs 引用
引用是C++新增的「变量别名」,底层基于指针实现,但使用更安全,是指针的轻量化替代方案。
|
特性 |
指针(pointer) |
引用(reference) |
|---|---|---|
|
定义方式 |
|
|
| 成员访问符 | -> | . |
|
是否可空 |
可以为nullptr |
必须初始化,不能为空 |
|
是否可重定向 |
可以随时指向其他变量 |
一旦绑定,终身不可修改指向 |
|
内存开销 |
占用4/8字节存储地址 |
无额外内存开销(编译器优化) |
|
使用场景 |
动态内存、数组、复杂指向 |
函数参数传递、返回值传递 |
开发原则:能使用引用就不用指针,引用更安全、代码更简洁;需要动态指向、空值判断时,必须用指针。
3.2 const修饰指针
const修饰指针有三种核心场景,看懂位置就能区分功能:
-
const int* p:常量指针,指针指向的值不可改,指针本身地址(即指向)可改
-
int* const p:指针常量,指针本身地址(即指向)不可改,指向的值可改
-
const int* const p:双重常量,地址和值都不可改
记忆技巧:const修饰谁,谁就不可变,靠近*修饰指针,靠近类型修饰值。
3.3 动态内存指针(new/delete)
C++摒弃了C语言的malloc/free,提供new/delete管理动态内存,配合指针使用,是堆内存操作的核心。
// 单个动态内存
int* p = new int(100); // 开辟int内存,初始值100
delete p; // 释放内存
p = nullptr; // 置空避免悬空指针
// 数组动态内存
int* arr = new int[5]{1,2,3,4,5};
delete[] arr; // 数组必须加[]释放
arr = nullptr;
核心区别:new会调用构造函数初始化并开辟内存,malloc只开辟裸内存;delete会调用析构函数并释放内存,free直接释放内存。
3.4 智能指针(C++11)
原生裸指针最大的问题:手动管理内存,容易内存泄漏、悬空指针。
C++11 推出智能指针,基于RAII机制,自动管理内存,无需手动delete,是现代C++开发的主流选择。
三种核心智能指针:
-
unique_ptr:独占式智能指针,同一时间只能有一个指针指向内存,不可拷贝,可移动
-
shared_ptr:共享式智能指针,支持多指针指向同一内存,引用计数机制,计数为0自动释放
-
weak_ptr:弱引用指针,配合shared_ptr使用,解决循环引用内存泄漏问题
简单示例:
#include <memory> // 智能指针头文件
int main() {
unique_ptr<int> p = make_unique<int>(200);
cout << *p << endl; // 正常取值
// 无需手动delete,出作用域自动释放内存
return 0;
}
3.5 this指针
this指针是类的非静态成员函数隐含的参数,指向当前调用该函数的对象本身。
核心作用:区分成员变量和局部变量、返回当前对象、链式调用。
class Student {
public:
// 成员变量
int age;
// 形参age和成员变量age重名
void setAge(int age) {
// 直接写age = age; 会变成自己赋值给自己,无任何效果
// this->age 代表:当前对象的成员变量age
this->age = age;
}
};
特性:this指针是常量指针,不可修改指向。
四、指针高频易错点总结(避坑指南)
-
禁止解引用未初始化的野指针、空指针,直接导致程序崩溃
-
动态内存开辟后,必须对应释放,且释放后置空,避免悬空指针
-
数组指针越界不会编译报错,但会篡改内存,产生隐形Bug
-
函数返回局部变量指针是非法的,局部变量出作用域会销毁,指针变为悬空指针
-
const指针的修饰位置不能混淆,避免读写权限错误
-
现代C++开发优先使用智能指针、引用,尽量少用裸指针
五、全文总结
1. 指针的本质是存储内存地址的变量,核心操作只有取地址&和解引用*;
2. 基础核心:指针与数组、指针传参、空/野/悬空指针是入门必备;
3. C++扩展:引用、const指针、new动态内存、this指针、智能指针是进阶核心,区别于C语言;
4. 开发规范:规避野指针、及时释放内存、优先使用智能指针和引用.

2583

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



