在C++编程语言中,&符号是一个多义符号,除了可以作为位与运算符、取地址运算符使用之外,最核心的用法是作为引用运算符,用来定义引用类型。引用是C++特有的语法特性,本质上是一个已存在变量的别名,和原变量指向同一块内存空间,对引用的所有操作都会直接作用于原变量本身。

引用的基本定义与初始化
引用定义的语法格式为类型名& 引用名 = 原变量名,需要注意引用必须在定义的时候就完成初始化,且初始化之后不能再绑定到其他变量。下面是一个基础的定义示例:
#include <iostream>
using namespace std;
int main() {
int num = 10;
// 定义num的引用,ref是num的别名
int& ref = num;
cout << "num的值: " << num << endl;
cout << "ref的值: " << ref << endl;
// 修改引用的值,原变量也会同步变化
ref = 20;
cout << "修改ref后num的值: " << num << endl;
cout << "修改ref后ref的值: " << ref << endl;
// 查看两者的地址,确认指向同一块内存
cout << "num的地址: " << &num << endl;
cout << "ref的地址: " << &ref << endl;
return 0;
}
引用的核心特性
引用作为变量的别名,有以下几个核心特性需要开发者牢记:
- 引用必须初始化,不存在空引用,这和指针可以为空指针不同
- 引用一旦绑定到某个变量,就无法再重新绑定到其他变量,相当于从一而终
- 对引用的操作完全等价于对原变量的操作,包括赋值、取值等操作
- 引用本身不占用独立的内存空间,它和原变量共享同一块内存区域
引用作为函数参数
引用最常见的应用场景是作为函数的参数,相比值传递,引用传递可以避免大对象的拷贝开销,同时可以在函数内部修改外部变量的值,相比指针传递,引用传递的语法更简洁,也不需要判断指针是否为空。
#include <iostream>
#include <string>
using namespace std;
// 值传递,修改的是形参,不影响外部实参
void swapByValue(int a, int b) {
int temp = a;
a = b;
b = temp;
}
// 引用传递,修改形参就是修改外部实参
void swapByRef(int& a, int& b) {
int temp = a;
a = b;
b = temp;
}
// 引用传递避免大对象拷贝,比如长字符串
void printString(const string& str) {
cout << "字符串内容: " << str << endl;
}
int main() {
int x = 1, y = 2;
swapByValue(x, y);
cout << "值传递后x: " << x << ", y: " << y << endl; // 输出1,2
swapByRef(x, y);
cout << "引用传递后x: " << x << ", y: " << y << endl; // 输出2,1
string longStr = "这是一个很长的测试字符串,用来演示引用传递避免拷贝的场景";
printString(longStr);
return 0;
}
上面的printString函数中,参数使用const string&是推荐的写法,既可以避免字符串拷贝的开销,又可以保证函数内部不会修改传入的字符串内容。
引用作为函数返回值
引用也可以作为函数的返回值,但是需要注意不能返回局部变量的引用,因为局部变量在函数执行结束后会被销毁,返回的引用会变成悬空引用,访问它会导致未定义行为。通常返回引用用于返回全局变量、静态变量或者传入的引用参数。
#include <iostream>
using namespace std;
// 返回静态变量的引用,合法
int& getStaticNum() {
static int num = 0;
num++;
return num;
}
// 返回传入参数的引用,合法
int& getMax(int& a, int& b) {
return a > b ? a : b;
}
int main() {
int& ref1 = getStaticNum();
cout << "第一次调用: " << ref1 << endl; // 输出1
cout << "第二次调用: " << getStaticNum() << endl; // 输出2
int m = 10, n = 20;
getMax(m, n) = 30; // 可以修改返回值,因为返回的是引用
cout << "修改后n的值: " << n << endl; // 输出30
return 0;
}
引用和指针的区别
很多开发者容易混淆引用和指针,两者的核心差异如下:
| 对比项 | 引用 | 指针 |
|---|---|---|
| 初始化要求 | 必须初始化,无空引用 | 可以不初始化,可以为空指针 |
| 重新绑定 | 一旦绑定无法更改 | 可以指向其他同类型变量 |
| 内存占用 | 不占用独立内存,和原变量共享 | 本身占用内存,存储指向的地址 |
| 操作语法 | 直接使用引用名操作,无需解引用 | 需要*解引用才能操作指向的变量 |
| 多级定义 | 不存在多级引用 | 可以定义多级指针,如int** |
引用的常见使用误区
在使用引用运算符时,开发者经常会犯以下几个错误:
- 定义引用时不初始化,比如
int& ref;,这会直接编译报错 - 返回局部变量的引用,导致悬空引用,程序运行时出现不可预期的错误
- 误以为引用会占用独立内存,对引用取地址时以为得到的是引用自身的内存地址
- 把引用和取地址运算符混淆,比如
int* p = &ref;这里&是取地址运算符,不是引用运算符
需要注意的是,在C++11之后还引入了右值引用,使用&&符号定义,主要用于移动语义和完美转发场景,和本文讲解的左值引用(普通引用)属于不同的概念,后续可以单独学习相关内容。