CC++ & Algorithm

引用的概念与使用

困难5
语言版本:C++
概述:引用就是给变量起一个别名,两个名字指向同一块内存,改一个另一个也会变。

引用:给变量起个"小名"

想象一下,你在学校叫"张明",回家后妈妈喊你"小明",叫的都是同一个人。C++里的引用干的就是类似的事——给一个已经存在的变量起一个别名,两个名字指向同一块内存空间。也就是说,改别名就是改原变量,看别名就是看原变量。

引用最大的特点是:定义时必须绑定一个变量,并且一辈子不能换人。它不像指针那么自由(指针可以指向别处),但也因此更安全、更简单。


1. 引用的三个核心规则

规则说明生活类比
必须初始化声明引用时必须指定它绑定的变量你给朋友起外号,必须得有个朋友才能起
不能改变指向引用一旦绑定,就永远是那个变量,不能再绑别的小名"明明"永远指的是张明,不会变成李华
与原变量同地址引用和原变量是同一块内存的两个名字你的身份证号只有一个,大名和小名都指向同一个你

看这个例子更清楚:

#include <iostream>
using namespace std;

int main() {
    int score = 90;          // 原始变量:考试成绩90分
    int& ref = score;        // ref 是 score 的引用(别名)

    cout << "score = " << score << endl;   // 输出90
    cout << "ref = " << ref << endl;       // 输出90,因为它们是同一个值

    ref = 100;               // 通过引用修改值
    cout << "修改后 score = " << score << endl;   // 输出100

    // 输出地址,看看是不是一样的
    cout << "&score = " << &score << endl;
    cout << "&ref = " << &ref << endl;     // 地址完全相同!

    return 0;
}

输出结果(地址可能不同,但两个地址一样):

score = 90
ref = 90
修改后 score = 100
&score = 0x61ff08
&ref = 0x61ff08

你可以把 ref 当成 score 的"小名",写 ref 就是在操作 score 本身。


2. 引用 vs 指针:谁更顺手?

很多新手会把引用和指针搞混。它们都能间接操作变量,但使用体验差别很大:

特性引用指针
必须初始化是,不初始化就报错可以不初始化,但野指针很危险
能否改变指向不能,一生只绑一个可以,随时指向别处
使用方式像普通变量一样直接用需要 * 解引用才能拿到值
安全性更安全(永远不会是空引用)容易野指针、空指针
有无自己的地址没有,它就是原变量的别名有自己的地址(指针变量存的地址)

生活例子:引用就像你手机里的"快捷拨号",按一下就打给妈妈,不能改成爸爸。指针就像一个"通讯录条目",里面存着别人的号码,你可以擦掉改写成另一个号码,但操作时需要先找到这个条目(取地址 &),再拨号(解引用 *)。


3. 引用在函数中的妙用——不用复制,直接改

引用最常用的地方是函数参数。如果不加引用,参数会被复制一份,函数里改的是副本,不影响原变量。加上引用,函数就能直接修改传入的变量,而且不需要指针那么麻烦。

例子:交换两个数的值

#include <iostream>
using namespace std;

// 使用引用作为参数,函数可以直接修改传入的变量
void swap(int& a, int& b) {   // a 是 x 的引用,b 是 y 的引用
    int temp = a;
    a = b;
    b = temp;
}

int main() {
    int x = 5, y = 10;
    cout << "交换前:x = " << x << ", y = " << y << endl;  // 5, 10
    
    swap(x, y);   // 传的是引用,函数内部改的就是 x 和 y 本身
    
    cout << "交换后:x = " << x << ", y = " << y << endl;  // 10, 5
    return 0;
}

如果不加引用(改成 void swap(int a, int b)),swap(x, y) 只会交换两个副本,xy 的值不变。引用就像给了函数一把"万能钥匙",可以直接打开原变量的门。

例子:通过引用修改数组元素

#include <iostream>
using namespace std;

int main() {
    int scores[3] = {85, 90, 78};   // 三科成绩
    
    // 遍历数组,用引用给每个成绩加5分(平时分加满!)
    for (int i = 0; i < 3; i++) {
        int& ref = scores[i];       // ref 是 scores[i] 的别名
        ref += 5;                   // 直接修改原数组元素
    }
    
    for (int i = 0; i < 3; i++) {
        cout << scores[i] << " ";   // 输出 90 95 83
    }
    return 0;
}

4. 引用作为返回值——小心返回局部变量的引用!

函数也可以返回引用,这样调用者就能直接操作函数内部返回的那个变量。但有一个大坑:千万不要返回局部变量的引用,因为局部变量在函数结束时就被销毁了,引用会指向一块已经被回收的内存,导致崩溃或乱码。

正确用法:返回全局变量、静态变量或参数中传来的引用。

#include <iostream>
using namespace std;

int global = 100;   // 全局变量

// 返回全局变量的引用(安全)
int& getGlobal() {
    return global;   // 返回引用,调用者可以修改 global
}

int main() {
    cout << "原值:" << global << endl;   // 100
    
    getGlobal() = 200;   // 函数返回的是引用,可以直接赋值修改
    cout << "修改后:" << global << endl; // 200
    
    return 0;
}

错误示例(千万不要这样写!)

int& badFunction() {
    int local = 5;   // 局部变量
    return local;    // 危险!函数结束 local 就没了
}

5. 新手常犯的 3 个错误

错误①:忘记初始化引用

int& ref;   // 编译错误!引用必须初始化,否则不知道是谁的小名

正确做法:声明时绑定一个变量。

错误②:试图改变引用的指向

int a = 10, b = 20;
int& ref = a;   // ref 绑定 a
ref = b;        // 这不是让 ref 绑定 b,而是把 b 的值赋给 a(a 变成20)

很多新手以为 ref = b; 会让 ref 转而绑定 b,但实际上它只是把 b 的值赋给 a。引用一辈子只跟第一个变量走。

错误③:返回局部变量的引用(前面已经强调过)


6. 完整可运行示例:用引用实现多重功能

下面这段代码展示了引用在参数、数组操作和返回值中的典型用法:

#include <iostream>
using namespace std;

// 用引用交换两个数
void my_swap(int& x, int& y) {
    int t = x;
    x = y;
    y = t;
}

// 用引用给数组中每个元素加一个值
void add_to_all(int arr[], int size, int add_value) {
    for (int i = 0; i < size; i++) {
        int& ref = arr[i];   // 引用数组元素
        ref += add_value;
    }
}

int main() {
    // 1. 引用基本用法:交换
    int a = 10, b = 20;
    cout << "交换前:a=" << a << " b=" << b << endl;  // 10 20
    my_swap(a, b);
    cout << "交换后:a=" << a << " b=" << b << endl;  // 20 10
    
    // 2. 引用修改数组
    int scores[5] = {80, 85, 90, 75, 88};
    add_to_all(scores, 5, 5);   // 每个成绩加5分
    cout << "加5分后的成绩:";
    for (int i = 0; i < 5; i++) {
        cout << scores[i] << " ";   // 85 90 95 80 93
    }
    cout << endl;
    
    // 3. 引用绑定的不可变性验证
    int c = 100;
    int& ref_c = c;       // ref_c 绑定 c
    int d = 200;
    ref_c = d;            // 这不是换绑定,而是把 d 的值赋给 c(c变成200)
    cout << "c = " << c << ", ref_c = " << ref_c << ", d = " << d << endl;  // 200 200 200
    // 注意:ref_c 仍然绑定的是 c,不是 d
    
    return 0;
}

7. 相关知识点指引

  • 指针:引用和指针是"表兄弟",学完引用可以对比学习指针,理解它们各自的适用场景。
  • 函数参数传递方式:值传递、引用传递、指针传递,这是C++函数设计的核心。
  • 常量引用const int&):既能避免复制,又防止函数内修改原变量,非常常用。
  • 动态内存与指针:处理动态分配的数组或对象时,指针更灵活,引用通常用于函数接口。

掌握了引用,你的C++代码会更简洁、更高效。下次写交换函数时,别忘了用引用代替指针,少敲几个星号,多一份安全!

例题精讲

1单选题

关于C++中的引用,以下说法正确的是?

A引用是一个独立的变量,拥有自己的内存空间
B引用一旦初始化,就不能再指向另一个变量
C引用可以为NULL,表示空引用
D引用和指针本质相同,只是语法写法不同
2单选题

下列代码中,哪个函数能正确交换两个整数的值?

Avoid swap(int a, int b) { int t=a; a=b; b=t; }
Bvoid swap(int *a, int *b) { int t=*a; *a=*b; *b=t; }
Cvoid swap(int &a, int &b) { int t=a; a=b; b=t; }
Dvoid swap(const int &a, const int &b) { int t=a; a=b; b=t; }
3判断题

在C++中,引用定义时如果不初始化,后续可以通过赋值操作使其绑定到某个变量。

4填空题
以下函数使用引用返回数组某个元素的值,请补全代码。

int arr[5] = {1,2,3,4,5};
int& getElement(int index) {
    return ___;
}
int main() {
    getElement(2) = 100; // 将arr[2]修改为100
    cout << arr[2]; // 输出100
    return 0;
}
5填空题
以下函数希望通过引用参数修改外部变量的值,请补全代码。

void increment(___ x) {
    x++;
}
int main() {
    int num = 5;
    increment(num);
    cout << num; // 应输出6
    return 0;
}