🐸 青蛙编程网
首页 / C++ / C++ 基础 / 6. 指针与引用

指针与引用:C++ 的灵魂难关

这是什么:指针 = 记着变量住哪间房的门牌号;引用 = 变量的另一个名字,改它就是改本人。

为什么用:前面一路顺利,现在到了 C++ 的"劝退名场面"——指针。别慌:上一章的函数默认只收复印件,想让它真正改动外面的变量,就得靠指针和引用搭把手。

如何用:先记住"内存像一排带门牌号的房间"这个比喻,用 & 和 * 写一次取地址、解引用,再动手写个 int& 函数改外部变量。

内存与地址:先建立直觉

电脑内存像一排带门牌号的房间,每个房间住着一个数据。int age = 18; 就是把 18 住进某个房间;而指针,存的就是门牌号——"18 住在 1024 号房"。

& 取地址,* 解引用

#include <iostream>

int main() {
    int age = 18;
    int* p = &age;  // p 是指针:存着 age 的门牌号

    std::cout << age << std::endl;   // 房间里的住户
    std::cout << &age << std::endl;  // 房间的门牌号
    std::cout << *p << std::endl;    // 顺着门牌号进屋,找到住户
    return 0;
}

你会看到(地址一串十六进制数,每台电脑、每次运行都不同)

18
0x7ffee3b4a9dc
18
符号 名字 作用
&age 取地址 问出"age 住在哪个门牌号"
int* p 声明指针 p 这个变量专门用来存地址
*p 解引用 顺着门牌号进屋,拿到里面的数据

& 和 * 是一对互逆操作:*(&age) 绕一圈还是 age 本尊。

空指针 nullptr

指针可以不指向任何有效地址,这时用 nullptr(C++11 起标准写法,替代老旧的 NULL):

int* p = nullptr;  // 空指针,不指向任何对象

if (p != nullptr) {
    std::cout << *p;  // 只有非空才解引用,避免崩溃
}

绝不要对 nullptr 解引用(*nullptr),程序会直接崩溃(段错误)。

引用:变量的"别名"

还记得上一章"传值改不动原件"吗?把参数写成 int& n(引用传递),函数拿到的就不是复印件,而是同一个变量的别名:

#include <iostream>

void addTen(int& n) {  // n 是外面那个数的别名
    n += 10;           // 改 n 就是改本人
}

int main() {
    int a = 1;
    addTen(a);
    std::cout << a << std::endl;
    return 0;
}

你会看到

11

引用出生就必须绑定,终生不换人:

int a = 1, b = 2;
int& r = a;  // r 是 a 的别名
r = 100;     // 改 r 就是改 a,a 变成 100
// r = &b;    // 错误:引用不能重新绑定到 b(这行甚至无法编译,只作示意)

指针 vs 引用对比

指针 int* p 引用 int& r
本质 存门牌号的变量 别名,出生就必须绑定且终生不换人
可以为空 可以(nullptr,空指针) 不行
能否改指别人 能(p = &other;) 不能
用法 要 *p 解引用 当普通变量直接用
初始化 声明后可先不赋值 声明时必须初始化

一句话:指针是写着门牌号的纸条,引用是给房间起的第二个名字。

什么时候才需要指针

好消息:现代 C++ 里,引用、标准库容器和智能指针能覆盖绝大多数场景。裸指针主要出现在:底层开发、手工实现数据结构(比如链表)、和 C 语言代码打交道。零基础阶段的目标只有一个——见到 int*、&x、*p 不心慌。等学到 STL 和数据结构,你会回来和它们真正交朋友。

❓ 常见问题

Q1:指针和引用选哪个?

能用引用就优先用引用(更安全、语法更简洁)。需要"可以为空""可以改指别人""需要动态内存管理"时,才上指针。

Q2:为什么要学指针?现代 C++ 不都用智能指针了吗?

理解裸指针是看懂智能指针、看懂底层数据结构、看懂 C 语言代码的前提。零基础阶段混个脸熟即可,深入后再系统学。

Q3:*p 和 `p` 有什么区别?**

*p 是解引用一次,拿到 p 所指的数据;**p 是解引用两次,p 本身是个"指向指针的指针"(二级指针)。入门阶段用不到二级指针,先理解一级就够了。

⚠️ 新手翻车现场

翻车一:对空指针解引用

int* p = nullptr;
std::cout << *p;  // 程序直接崩溃(段错误)

问题:nullptr 不指向任何有效内存,顺着门牌号进屋,房间根本不存在。

修正:解引用前先判空:if (p != nullptr) { std::cout << *p; }

翻车二:引用没有初始化

int& r;  // 错误:引用声明时必须初始化

问题:引用本质是别名,必须出生就绑定到某个变量,不能单独存在。

修正:int a = 1; int& r = a;

翻车三:把指针当普通变量用,忘了 *

int age = 18;
int* p = &age;
std::cout << p;  // 打印的是门牌号(地址),不是 18

问题:想打印房间里的住户(18),得用 *p 解引用。

修正:std::cout << *p;

🎯 什么时候用指针/引用

场景 推荐 原因
函数要修改外部变量 引用 int& 语法简单、不能为空、更安全
需要可空或可重新指向 指针 int* 支持 nullptr,支持 p = &other
动态内存(new/delete) 智能指针(后续章节) 避免手动内存管理带来的泄漏
零基础入门 混个脸熟即可 不要在第一关卡住

📝 本章小结

  • &age 取地址(门牌号),*p 解引用(进屋拿数据)
  • 指针 int* p 存地址,可以为空(nullptr),可以改指别人
  • 引用 int& r 是别名,必须初始化,终生不换人
  • 不要对 nullptr 解引用,程序会崩溃
  • 记忆口诀:& 问门牌,* 顺着找;引用是别名,改它就是改本人。

动手改

  1. 声明一个 double x = 9.5;,打印 &x(注意它的类型是 double*)
  2. 写 void swap(int& a, int& b) 交换两个数,在 main 里验证真的换了
  3. 把 swap 的参数改回 int a, int b,再跑一次——为什么换不动了?亲手体会传值 vs 传引用
  4. 写一个函数 void setZero(int* p),用指针把外面的一个整数设为 0,验证有效

下一章:类与对象,终于要"造东西"了 🐸

📥 下载本章代码

ZIP 内含本章全部代码文件与说明,解压即用,与教程内容同步生成。