第六章:指针与引用——C++的灵魂所在

说实话,指针和引用这个话题,是很多C++初学者最头疼的部分。我记得自己刚学的时候,也被绕得晕头转向。但等你真正理解了,你会发现——指针就是C++最强大的武器,没有之一。

这一章,我会带你从最基础的概念开始,一步步深入到指针的各种用法。别急,慢慢来。

核心提示:指针存的是地址,引用是别名。这两个概念搞清楚了,后面就顺了。

6.1 指针基础

指针是什么?说白了,指针就是一个变量,它存的是另一个变量的内存地址。你可以通过这个地址,间接访问那个变量。

int a = 10;
int* p = &a;  // p 存的是 a 的地址
cout << *p;   // 输出 10,*p 是解引用

我个人习惯把指针声明写成 int* p,这样一眼就能看出「p 是一个指向 int 的指针」。当然也有人写成 int *p,语法上没问题,但我觉得前者更清晰。

小技巧:声明指针时,最好同时初始化。不初始化的指针是野指针,非常危险。我见过太多因为野指针导致的崩溃了。

6.2 指针运算

指针也能做加减运算,但要注意——它加减的是「指向类型的大小」,不是字节数。

int arr[] = {10, 20, 30, 40};
int* p = arr;       // 指向 arr[0]
p++;                // 现在指向 arr[1]
cout << *p;        // 输出 20

为什么会这样?因为 p++ 实际上移动了 sizeof(int) 个字节(通常是4字节)。这个设计很巧妙,让你不用关心具体字节数,只管「跳到下一个元素」就行。

我曾经在做一个图像处理项目时,用指针运算遍历像素数组,速度比下标访问快了不少。嗯,性能敏感的场景,指针运算是个好选择。

警告:指针运算越界是常见 bug。C++ 不会帮你检查边界,全靠你自己把控。写代码时一定要确认指针还在合法范围内。

6.3 指针与数组

数组名其实就是一个常量指针,指向数组的第一个元素。所以 arr&arr[0] 是等价的。

int arr[5] = {1, 2, 3, 4, 5};
int* p = arr;       // 等价于 int* p = &arr[0];
cout << p[2];      // 输出 3,指针也能用下标

你想想看,既然指针能用下标,数组名也能用指针运算——*(arr + 2) 就是 arr[2]。这两者其实是同一回事。

我记得有一次面试,面试官问我:「数组名和指针有什么区别?」我回答:「数组名是常量指针,不能修改;指针是变量,可以指向别处。」他点了点头。嗯,这个区别很重要。

6.4 指针与函数

指针可以作为函数参数,实现「传址调用」——在函数内部修改外部变量的值。这在需要返回多个结果时特别有用。

void swap(int* a, int* b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

int x = 5, y = 10;
swap(&x, &y);  // 现在 x=10, y=5

还有一种更高级的用法——函数指针。你可以把函数当作参数传递,实现回调机制。

int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }

int operate(int (*func)(int, int), int x, int y) {
    return func(x, y);
}

cout << operate(add, 10, 5);  // 输出 15

我在写一个插件系统时,大量使用了函数指针。每个插件注册自己的处理函数,主程序通过函数指针调用。灵活得很。

6.5 动态内存管理(new/delete)

动态内存分配,说白了就是「在程序运行时,按需申请内存」。C++ 用 newdelete 来管理。

int* p = new int;       // 申请一个 int 大小的内存
*p = 42;
delete p;               // 释放内存

int* arr = new int[10]; // 申请数组
arr[0] = 1;
delete[] arr;           // 释放数组,注意用 delete[]

重要提醒:new 申请的内存,必须用 delete 释放。否则会造成内存泄漏。我曾经在一个服务器程序里,因为忘记释放一个循环中 new 出来的对象,导致内存持续增长,最后服务器挂了。从那以后,我每次 new 都会立刻写对应的 delete

还有一个常见问题:newmalloc 有什么区别?new 会调用构造函数,malloc 不会。所以在 C++ 中,优先用 new

6.6 引用

引用是 C++ 对指针的「友好封装」。它本质上是一个别名,一旦绑定就不能改变。

int a = 10;
int& ref = a;   // ref 是 a 的引用
ref = 20;       // 现在 a 也变成了 20

引用和指针的区别,我总结一下:

特性 指针 引用
是否可以为空 可以(nullptr) 不可以,必须初始化
是否可以重新绑定 可以 不可以
是否需要解引用 需要(*p) 不需要,直接使用
语法简洁性 较复杂 简洁

我个人习惯:能用引用就用引用,语法更干净。但需要「重新指向」或「可能为空」时,就用指针。

避坑指南:不要返回局部变量的引用!局部变量在函数结束后就销毁了,返回它的引用会导致悬空引用。我曾经在代码审查时抓到过这种 bug,排查了好久才找到原因。

知识体系总览

下面这张图,把本章的核心知识点串起来了。你可以把它当作复习提纲。

指针与引用 指针基础 存地址,解引用 指针运算 加减按类型大小 指针与数组 数组名即指针 指针与函数 传址调用、函数指针 动态内存管理 new/delete 引用 别名,不可重新绑定 指针灵活但危险,引用安全但受限 根据场景选择,优先用引用

好了,指针和引用就讲到这里。这些东西需要多练,光看是记不住的。打开你的 IDE,把上面的代码都敲一遍,改一改,看看会发生什么。实践出真知。

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