写 C++ 时你一定见过 & 这个符号:它有时出现在变量前面表示"取地址",有时又出现在类型后面,比如 int& x。后者就是本篇的主角——引用(reference)。理解了引用,你就能看懂很多函数签名,也能写出更高效的代码。

什么是引用:变量的别名

引用就是给已经存在的变量起一个"别名",对别名的任何操作,都等价于对原变量本身的操作。

#include <iostream>
using namespace std;

int main() {
    int score = 90;
    int& alias = score;   // alias 是 score 的引用(别名)

    alias = 95;           // 修改 alias 就是修改 score
    cout << score << endl;                    // 输出:95

    cout << &score << endl;                   // 输出:0x7ffc2a8(示意)
    cout << &alias << endl;                   // 与上一行地址完全相同
    return 0;
}

注意最后一组输出:score 和 alias 的地址一模一样,因为它们本来就是同一块内存的两个名字。

引用必须在定义时绑定,且终身不改

引用和普通变量最大的区别有两点:一是定义时必须初始化,二是绑定之后不能再"改嫁"。

int a = 10;
int b = 20;

int& r = a;   // r 从此只代表 a
r = b;        // 注意:这不是让 r 改绑 b,而是把 b 的值赋给 a!

cout << a << endl;  // 输出:20
cout << &r << " " << &a << endl;  // 地址仍然相同

很多初学者以为 r = b 是让引用改指 b,实际上引用一旦绑定就永远不会改变指向,这句代码的效果就是普通的赋值。另外,也不存在"空引用",引用必须一开始就绑定到一个真实存在的变量。

引用作为函数参数:最实用的场景

引用最常见的用途是函数传参。C++ 的函数参数默认是"值传递",函数收到的是一份拷贝,改它不影响外面;换成引用传递,函数里拿到的就是原变量本身。

#include <iostream>
using namespace std;

// 值传递:交换的是拷贝,无效
void swap_bad(int x, int y) {
    int tmp = x; x = y; y = tmp;
}

// 引用传递:交换的是原变量,有效
void swap(int& x, int& y) {
    int tmp = x; x = y; y = tmp;
}

int main() {
    int a = 1, b = 2;
    swap_bad(a, b);
    cout << a << " " << b << endl;  // 输出:1 2(没换过来)
    swap(a, b);
    cout << a << " " << b << endl;  // 输出:2 1(成功交换)
    return 0;
}

除了能修改原变量,引用传参还有一个好处:避免大对象拷贝。比如函数参数是 string 或自定义类时,用 const string& s 既不拷贝又不允许修改,是标准写法。

引用作为函数返回值

函数也可以返回引用,典型用法是"链式访问"某个需要被修改的元素:

#include <iostream>
using namespace std;

int& bigger(int& a, int& b) {
    return (a > b) ? a : b;   // 返回较大者的引用
}

int main() {
    int x = 3, y = 8;
    bigger(x, y) = 100;   // 给返回的引用赋值,等价于 y = 100
    cout << y << endl;    // 输出:100
    return 0;
}

有一条红线必须记住:永远不要返回局部变量的引用。局部变量在函数结束时就被销毁了,返回它的引用等于返回一块已经失效的内存,程序行为不可预测。

引用与指针的区别

引用和指针都能"间接操作变量",但它们是两种东西:

  • 引用定义时必须初始化;指针可以先声明后赋值,甚至暂时指向空。
  • 引用绑定后不能更改;指针随时可以改指别的地址。
  • 不存在空引用;存在空指针。
  • 引用用起来就像原变量本身,不需要 * 解引用;指针必须通过 * 访问目标。

一个简单的选择原则:能在"引用一个确定存在的对象"的场景下用引用,就用引用;需要"可能什么都不指"或"中途改变指向"时,才用指针。

小结

  • 引用是变量的别名,int& r = a; 之后操作 r 就是操作 a,二者地址相同。
  • 引用必须初始化,且绑定后不能改指向,也不存在空引用。
  • 引用传参让函数直接修改原变量,还能避免大对象拷贝,常量引用 const T& 是只读大参数的标准写法。
  • 函数可以返回引用,但严禁返回局部变量的引用。
  • 与指针相比,引用更安全、更直观;需要"可空"或"可改指"时再选指针。