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 知识体系总览

下面这张图帮你理清本章的核心逻辑:

指针与引用 指针 存储地址的变量 指针运算(加减跳类型) 数组名 == 指针常量 new/delete 动态分配 引用 变量的别名 必须初始化,不可重绑定 函数传参避免拷贝 更安全,语法更简洁 核心:指针灵活但危险,引用安全但受限

7.7 总结

指针和引用是C++内存管理的核心工具。指针给你最大的灵活性,但也带来了风险;引用则更安全、更简洁,但功能上有所限制。我的建议是:日常开发优先用引用,需要“可空”或“动态绑定”时再用指针。记住,写代码不只是让机器能跑,更要让人能看懂。

一句话记住:指针是地址,引用是别名。new 了就要 delete,引用了就要初始化。


公众号:蓝海资料掘金营,微信deep3321