news 2026/5/12 17:53:04

深入理解C++系列 || NO1.引用()

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
深入理解C++系列 || NO1.引用()

今天在此开启一个新的系列:<<深入理解C++>>.

也开始我的学习之路,主要面向初学者和复习相关的知识.

由于C语言和C++有相当部分的知识重叠,故在此主要更新他们不同的和C++独特内容部分的知识.

持续更新,欢迎大家点赞关注.

前言

在 C++ 中,引用(Reference)是一个非常核心且实用的特性,它为我们提供了一种更安全、更简洁的方式来操作变量。很多初学者会把它和指针搞混,其实两者有着本质的区别。今天,我们就从基础概念讲起,一步步深入理解引用的方方面面。

1.什么是引用?

1.1 &核心定义

形象来说,引用就是变量的 “别名”,就像一个人可以有大名和小名,不管叫哪个,指的都是同一个人。

int a = 10; int &b = a; // 定义引用,b 是 a 的别名

核心注意:

(1).ba指向同一块内存地址.(即a和b本身就是一个东西)

(2). 修改b等价于修改a.

(3). 引用必须在定义时初始化,不能 “空引用”(必须初始化已有变量).

1.2 从内存角度理解

我们可以通过打印出a和b的地址来验证:'

#include <iostream> using namespace std; int main() { int a = 10; int &b = a; cout << "a 的地址:" << &a << endl; cout << "b 的地址:" << &b << endl; cout << "a 的值:" << a << endl; cout << "b 的值:" << b << endl; b = 20; // 修改 b cout << "修改后 a 的值:" << a << endl; return 0; }

运行结果:

从结果中可以看出,变量a和引用b的地址完全相同(即它们本身其实是一种东西),修改其中一个,那么另一个当然也会跟着变化.

1.3 个人理解:

可能还有很多人对于"变量a和引用b本身是一个东西"有些不太理解.

但如果屏幕前的你学过C语言中的#define 宏定义,那么就应该很容易理解&的效果了.

它们非常相像:纯粹是别名,没有实体,不占独立空间

宏定义:

#define b a

你写b编译器直接把它替换成 a.

引用&:

int a = 10; int &b = a;

你写b编译器也直接把它当成 a.

最终总结:

内存里只有一块空间,名字叫a,而b不是新变量,没有自己的盒子

引用就是变量的别名,和宏定义一样,只是换个名字,不是新东西。a 和 b 就是同一个东西,不是两个东西指向同一个地方!

2. 引用&的核心规则(重要)

2.1 引用必须初始化

引用在定义时必须绑定一个已存在的变量,不能定义 “空引用”:

int &b; // ❌ 错误!未初始化 int a = 10; int &b = a; // ✅ 正确!绑定变量 a

2.2 一旦绑定,终身不变

引用绑定一个变量后,就不能再绑定其他变量了:

int a = 10, c = 20; int &b = a; b = c; // ❌ 不是改绑!而是把 c 的值赋给 a,此时 a 变成 20

2.3. 不能绑定常量(除非是 const 引用)

前面看到的都是引用初始化是绑定一个变量,原来是一般不能直接绑定常量

int &ref = 10; // ❌ 错误!不能绑定字面量常量 const int &ref = 10; // ✅ 正确!const 引用可以绑定常量

const引用会为临时常量创建一个临时变量,然后绑定到这个临时变量上。

3. 引用的常用应用场景

3.1 函数传参(最常用)

在将引用传参前我们先复习下之前在C语言中学过的的传值调用和传址调用.

3.1.1传值调用

函数会创建变量的副本,修改副本不会影响原变量.

通俗来说:

实参调用函数时,括号里真实传进去的值 / 变量形参函数定义时,括号里写的接收参数,只是个 “占位名字”.

也就是: 形参是实参的一份拷贝,在某函数中修改了形参并不会影响实参的值.

void func(int x) { x = 100; // 修改的是副本 } int main() { int a = 10; func(a); cout << a; // 输出 10,原变量未被修改 return 0; }

3.1.2 传址调用(指针)

使用指针变量,向函数传入实参变量的地址,这样在函数中通过解引用改变变量的值,也就是直接改变了这个地址所存储变量的值.

指针和引用在一定程度上有点混淆,我们会在最后做个比较.

void func(int* x) //传入实参的地址 { *x = 100; //直接修改地址内的值 } int main(void) { int a = 10; func(&a); cout << a << endl; //输出的是100 return 0; }

3.1.3 引用传递

函数直接操作原变量,无需拷贝,效率更高.

void func(int &x) { x = 100; // 直接修改原变量 } int main() { int a = 10; func(a); cout << a; // 输出 100,原变量被修改 return 0; }

相信不用我强调大家都会自己记起了,变量和引用本质上就是一个东西,故传入a的引用就等于传入了a本身而不是实参a的拷贝(效果上与指针相同).

3.2 传递大对象时避免拷贝

对于大型对象(如结构体、类对象),值传递会拷贝整个对象,因为占用内存较大,所以效率极低,引用传递(直接传入大型对象本身,不需要拷贝)就可以避免这个问题:

struct Student { char name[100]; int age; float score; }; // 值传递:拷贝整个 Student 对象,效率低 void printStudent(Student s) { cout << s.name << endl; } // 引用传递:直接操作原对象,无需拷贝 void printStudent(const Student &s) { cout << s.name << endl; }

注: 大家都知道const修饰的变量不可以被修改,这里使用const引用,既避免了拷贝,又保证了数据不会在函数中被意外修改。

3.3 函数返回引用

函数可以返回引用,但要注意不能返回局部变量的引用:

// 正确:返回静态变量的引用 int& getStaticVar() { static int x = 10; return x; } // 错误:返回局部变量的引用,局部变量在函数结束后会被销毁 int& getLocalVar() { int x = 10; return x; // ❌ 危险!返回了无效内存的引用 } int main() { int &ref = getStaticVar(); cout << ref << endl; // 输出 10 ref = 20; cout << getStaticVar() << endl; // 输出 20 return 0; }

根据局部变量的特性,在该变量离开它的作用域后就会自动销毁,也就是函数中的局部变量不能离开函数体.

那返回它的引用就毫无意义了,因为函数结束后这个局部变量的空间就已经被销毁了.

3.4 引用作为循环变量(遍历容器)

在遍历容器时,使用引用可以避免拷贝元素,同时可以修改元素:

#include <vector> using namespace std; int main() { vector<int> vec = {1, 2, 3, 4, 5}; // 普通遍历:拷贝每个元素 for (int x : vec) { x *= 2; // 修改的是副本,不会影响 vec } // 引用遍历:直接操作原元素 for (int &x : vec) { x *= 2; // 修改 vec 中的元素 } // const 引用遍历:只读,避免拷贝 for (const int &x : vec) { cout << x << " "; } return 0; }

vector相关知识,我们以后再慢慢介绍.这里大家先了解下.

3.5 数组的引用

可以定义数组的引用,语法如下:

int arr[5] = {1, 2, 3, 4, 5}; int (&ref)[5] = arr; // ref 是数组 arr 的引用 ref[0] = 10; // 修改 arr[0] 的值

4. 常引用(const&)详解

4.1 定义与作用

常引用就是用const修饰的引用,它禁止通过引用修改原变量

int a = 10; const int &b = a; b = 20; // ❌ 错误!不能通过 const 引用修改变量 a = 20; // ✅ 正确!可以直接修改原变量

4.2 常引用的特殊特性

(1). 可以绑定常量、临时变量:

const int &ref = 10; // ✅ 绑定字面量常量 const int &ref = a + 5; // ✅ 绑定表达式的临时结果

(2). 可以绑定不同类型的变量(会进行类型转换):

double d = 3.14; const int &ref = d; // ✅ 先把 d 转为 int,再绑定到临时变量

5. 引用 VS 指针 :联系与区别

很多初学者容易混淆引用和指针,这里用表格清晰对比两者的区别:

特性引用(&)指针(*)
定义方式int &ref = a;int *p = &a;
是否必须初始化必须可以不初始化(可以为 nullptr)
是否可以改绑不可以可以指向其他变量
是否可以为 “空”不可以可以为 nullptr
操作方式直接使用变量名需要解引用*p
安全性更安全(无野引用)容易出现野指针、空指针问题
内存占用不占用额外内存(别名)占用 4/8 字节(存储地址)

1. 指针可以实现引用的功能,但引用更安全

// 指针实现类似引用的效果 int a = 10; int *p = &a; *p = 20; // 修改 a 的值 // 引用实现 int &ref = a; ref = 20; // 修改 a 的值

指针需要手动管理地址和解引用,而引用自动处理这些,代码更简洁安全。

2. 指针的灵活性是引用无法替代的

指针可以指向数组的不同元素:

int arr[] = {1, 2, 3, 4, 5}; int *p = arr; p++; // 指向 arr[1]

指针可以动态分配内存:

int *p = new int(10);

6. 总结

引用是 C++ 中非常实用的特性,它的核心优势在于:

  1. 简洁安全:无需手动管理地址和解引用,避免了指针的很多问题
  2. 效率高效:传递大对象时无需拷贝,性能接近指针
  3. 可读性好:代码更直观,不容易出错

引用最常用的场景是函数传参和遍历容器,尤其是const引用,既能保证效率,又能保证数据安全。

在此不胜感谢能够看到这里的同学,如果发现文章有错误还请不吝赐教.

后续内容会不断更新,感兴趣的同学可以关注点赞收藏慢慢观看.

万分感谢!

-------------------------------------------------------------------------------------------

2026.5.11

---14:02

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/12 17:52:10

如何通过开源工具提升开发者效率:Cursor Pro功能完整指南

如何通过开源工具提升开发者效率&#xff1a;Cursor Pro功能完整指南 【免费下载链接】cursor-free-vip [Support 0.45]&#xff08;Multi Language 多语言&#xff09;自动注册 Cursor Ai &#xff0c;自动重置机器ID &#xff0c; 免费升级使用Pro 功能: Youve reached your …

作者头像 李华
网站建设 2026/5/12 17:50:06

告别死记硬背:用Python+Matplotlib自动分析仿真波形,验证你的HDLbits答案

告别死记硬背&#xff1a;用PythonMatplotlib自动分析仿真波形&#xff0c;验证你的HDLbits答案 在数字电路设计的学习过程中&#xff0c;波形验证是不可或缺的一环。许多学习者习惯依赖HDLbits等平台自带的验证工具&#xff0c;却错过了培养独立分析能力的宝贵机会。本文将带你…

作者头像 李华