7、指针与引用:指针概念、指针运算、指针与数组、动态内存分配(new/delete)、引用详解
指针和引用,是C++里绕不开的两座山。很多初学者觉得它们抽象、难懂,甚至有点吓人。说实话,我当年学的时候也懵过一阵子。但等你真正用熟了,你会发现它们其实是C++最强大的工具之一。今天我们就把它彻底讲透。
7.1 指针是什么?—— 地址的“门牌号”
指针,说白了就是一个变量,但它存的不再是普通的值,而是另一个变量的内存地址。你可以把它想象成门牌号——你不需要把整个房子搬过来,只需要知道地址,就能找到它。
核心概念:指针就是地址。指针变量就是用来存放地址的变量。
int a = 42; // 普通变量,存值
int* p = &a; // 指针变量,存a的地址
// 现在 p 指向 a
// 通过 *p 可以访问 a 的值
cout << *p; // 输出 42
这里有个细节:& 是取地址符,* 是解引用符。我刚开始学的时候老搞混,后来记住一句话:声明时带 * 是指针,使用时带 * 是取值。
7.2 指针运算——别小看加减法
指针也能做加减法,但它的逻辑和普通整数不一样。指针加1,不是地址值加1,而是跳过整个类型的大小。
int arr[] = {10, 20, 30, 40};
int* p = arr; // 指向 arr[0]
p++; // 现在指向 arr[1]
cout << *p; // 输出 20
p += 2; // 现在指向 arr[3]
cout << *p; // 输出 40
为什么会这样?因为指针知道它指向的是什么类型。int 占4个字节,所以 p++ 实际上地址增加了4。我在项目中遇到过有人拿指针做减法算偏移量,结果类型搞错了,调试了一下午……嗯,类型匹配真的很重要。
注意:指针减法得到的是两个指针之间的元素个数,不是字节数。两个指针必须指向同一个数组,否则结果是未定义的。
7.3 指针与数组——天生一对
数组名其实就是一个指针常量,指向数组的第一个元素。你想想看,arr[i] 本质上就是 *(arr + i)。
int arr[5] = {1, 2, 3, 4, 5};
// 下面三种写法完全等价
cout << arr[2]; // 3
cout << *(arr + 2); // 3
cout << 2[arr]; // 3 —— 这个有点奇葩,但语法上合法
我个人习惯用数组下标,可读性更好。但理解指针方式能帮你写出更灵活的代码,尤其是在处理动态数组或函数传参时。
小技巧:函数参数里写 int arr[] 和 int* arr 是完全等价的。我建议用 int* arr,因为它明确告诉你传进来的是一个指针。
7.4 动态内存分配——new 和 delete
静态数组的大小在编译时就固定了,但很多时候我们直到运行时才知道需要多少空间。这时候就需要动态内存分配。
int size;
cout << "请输入数组大小: ";
cin >> size;
int* arr = new int[size]; // 在堆上分配内存
// 使用数组
for (int i = 0; i < size; i++) {
arr[i] = i * 10;
}
// 用完一定要释放!
delete[] arr; // 注意是 delete[] 不是 delete
我曾经犯过一个低级错误:用 new 分配了单个对象,却用 delete[] 去释放,结果程序崩溃了。记住:new 配 delete,new[] 配 delete[],千万别混用。
避坑指南:忘记 delete 会导致内存泄漏。程序跑久了,内存越占越多,最后系统卡死。我建议每次写 new 的时候,立刻把对应的 delete 写上,养成习惯。
7.5 引用详解——指针的“语法糖”
引用,说白了就是给变量起个“别名”。它和指针很像,但用起来更自然。
int a = 10;
int& ref = a; // ref 是 a 的引用
ref = 20; // 实际上修改了 a
cout << a; // 输出 20
引用和指针的区别,我总结了一张表:
| 特性 | 指针 | 引用 |
|---|---|---|
| 是否可以为空 | 可以(nullptr) | 不可以,必须初始化 |
| 是否可以重新指向 | 可以 | 不可以,绑定后不能改 |
| 是否需要解引用 | 需要(*p) | 不需要,直接使用 |
| 内存占用 | 通常占8字节(64位) | 不额外占用(编译器优化) |
我个人在写函数参数时,能用引用就不用指针。引用更安全,语法也更简洁。但如果你需要“可空”或者“可重新绑定”,那就只能用指针了。
// 引用作为函数参数——避免拷贝,提高性能
void swap(int& a, int& b) {
int temp = a;
a = b;
b = temp;
}
int x = 5, y = 10;
swap(x, y); // 直接传变量,不用取地址
建议:对于大型对象(比如自定义类、容器),尽量用 const 引用传参。既避免了拷贝开销,又保证了函数内不会意外修改数据。
7.6 知识体系总览
下面这张图帮你理清本章的核心逻辑:
7.7 总结
指针和引用是C++内存管理的核心工具。指针给你最大的灵活性,但也带来了风险;引用则更安全、更简洁,但功能上有所限制。我的建议是:日常开发优先用引用,需要“可空”或“动态绑定”时再用指针。记住,写代码不只是让机器能跑,更要让人能看懂。
一句话记住:指针是地址,引用是别名。new 了就要 delete,引用了就要初始化。