【C++】c++中“引用”的详解

作者:王璐WL日期:2026/2/12

文章目录

  • 1. 引用
    • 1.1 引用的概念和定义
    • 1.2 引用的特性
    • 1.3 引用的使用
    • 小小的延伸
    • 1.4 const引用
    • 1.5 指针和引用的关系(面试常考)

1. 引用

1.1 引用的概念和定义

​ 引⽤不是新定义⼀个变量,⽽是给已存在变量取了⼀个别名,编译器不会为引⽤变量开辟内存空间(指针会开辟空间), 它和它引⽤的变量共⽤同⼀块内存空间。比如:水浒传中林冲,外号豹⼦头

​ 类型&引⽤别名=引⽤对象;

​ C++中为了避免引⼊太多的运算符,会复⽤C语⾔的⼀些符号,⽐如前⾯的<<和>>,这⾥引⽤也和取地址使⽤了同⼀个符号&。

如何区分取地址和引用呢?

&i->在变量之前是取地址

int&->在类型之后是引用

1#include<iostream>  
2using namespace std; 
3int main() 
4{ 
5    int a = 0; 
6    // 引⽤:b和c是a的别名  
7    int& b = a; 
8    int& c = a; 
9    // 也可以给别名b取别名,d相当于还是a的别名  
10    int& d = b;  
11    // 这⾥取地址我们看到是⼀样的  
12    cout << &a << endl; 
13    cout << &b << endl; 
14    cout << &c << endl; 
15    cout << &d << endl; 
16    return 0; 
17}
18

在这里插入图片描述

1#include<iostream>  
2using namespace std; 
3int main() 
4{ 
5    int a = 0; 
6    int& b = a; 
7    int& c = a; 
8    int& d = b; 
9    ++d; //++d就相当于++b,也就是++a
10    cout << &a << endl;//这里的&是取地址 
11    cout << &b << endl; 
12    cout << &c << endl; 
13    cout << &d << endl; 
14    return 0; 
15}
16

在这里插入图片描述

1.2 引用的特性

• 引⽤在定义时必须初始化

1int& b=a;//必须初始化
2int& c;//编译报错
3

• ⼀个变量可以有多个引⽤

1int& b=a;
2int& c=a;
3

• 引⽤⼀旦引⽤⼀个实体,再不能引⽤其他实体

1#include<iosteam>  
2using namespace std; 
3int main() 
4{
5    int a = 10;  
6    int& b = a; 
7    int c = 20; 
8    
9    // 这⾥并⾮让b引⽤c,因为C++引⽤不能改变指向  
10    // 这⾥是⼀个赋值  
11    b = c; 
12    
13    cout << &a << endl; 
14    cout << &b << endl; 
15    cout << &c << endl; 
16    return 0; 
17} 
18
1#include<iostream> 
2using namespace std; 
3typedef int STDataType; 
4typedef struct Stack 
5{ 
6    STDataType* a; 
7    int top; 
8    int capacity; 
9}ST; 
10void STInit(ST& rs, int n = 4) 
11{ 
12    rs.a = (STDataType*)malloc(n * sizeof(STDataType)); 
13    rs.top = 0; 
14    rs.capacity = n; 
15} 
16// 栈顶  
17void STPush(ST& rs, STDataType x) 
18{ 
19    assert(ps); 
20    // 满了, 扩容  
21    if (rs.top == rs.capacity) 
22    { printf("扩容\n"); 
23     int newcapacity = rs.capacity == 0 ? 4 : rs.capacity * 2; 
24     STDataType* tmp = (STDataType*)realloc(rs.a, newcapacity *  sizeof(STDataType)); 
25     if (tmp == NULL) 
26     { 
27         perror("realloc fail"); 
28         return; 
29     } 
30     rs.a = tmp; 
31     rs.capacity = newcapacity; 
32    } 
33    rs.a[rs.top] = x; 
34    rs.top++; 
35} 
36// int STTop(ST& rs) 
37int& STTop(ST& rs) 
38{ 
39    assert(rs.top); 
40    return rs.a[rs.top]; 
41} 
42int main() 
43{ 
44    // 调⽤全局的  
45    ST st1; 
46    STInit(st1); 
47    STPush(st1, 1); 
48    STPush(st1, 2); 
49    cout << STTop(st1) << endl; 
50    STTop(st1) += 10; 
51    cout << STTop(st1) << endl; 
52    return 0; 
53} 
54
1#include<iostream> 
2using namespace std; 
3typedef struct SeqList 
4{ 
5    int a[10]; 
6    int size; 
7}SLT; 
8// ⼀些主要⽤C代码实现版本数据结构教材中,使⽤C++引⽤替代指针传参,⽬的是简化程序,避开复杂的指针,但是很多同学没学过引⽤,导致⼀头雾⽔。  
9void SeqPushBack(SLT& sl, int x) 
10{} 
11typedef struct ListNode 
12{ 
13    int val; 
14    struct ListNode* next; 
15}LTNode, *PNode; 
16// 指针变量也可以取别名,这⾥LTNode*& phead就是给指针变量取别名  
17// 这样就不需要⽤⼆级指针了,相对⽽⾔简化了程序  
18//void ListPushBack(LTNode** phead, int x) 
19//void ListPushBack(LTNode*& phead, int x) 
20void ListPushBack(PNode& phead, int x) 
21{ 
22    PNode newnode = (PNode)malloc(sizeof(LTNode)); 
23    newnode->val = x; 
24    newnode->next = NULL; 
25    if (phead == NULL) 
26    { 
27        phead = newnode; 
28    } 
29    else 
30    { 
31        //... 
32    } 
33} 
34int main() 
35{ 
36    PNode plist = NULL; 
37    ListPushBack(plist, 1); 
38    return 0; 
39}
40

延伸
在这里插入图片描述

1.3 引用的使用

1.引⽤在实践中主要是于引⽤传参和引⽤做返回值中减少拷⻉提⾼效率和改变引⽤对象时同时改变被引⽤对象。

1) 引⽤传参跟指针传参功能是类似的,引⽤传参相对更⽅便⼀些。
2) 传值返回会产生临时变量,传引用返回本质是返回返回对象的引用(别名),从而提高了效率,同时可以改变返回对象(不是所有场景都可以传引用返回,如果是一个返回值是当前函数的局部对象,传引用返回是有风险的,这个后面会深入讲解)

2.指针传地址的时候可能出现二级指针,并且两数交换要在函数内部进行解引用,引用可以很好的提高效率,不过大部分场景可以替代指针,部分场景还是离不开指针,比如链表,树,结点定义位置,原因是c++的引用无法改变指向,结点一定存在改变指向,Java可以

3.C++的引⽤跟其他语⾔的引⽤(如Java)是有很⼤的区别的,除了⽤法,最⼤的点,C++引⽤定义后不能改变指向, Java的引⽤可以改变指向。

1void Swap(int& rx, int& ry) 
2{ 
3    int tmp = rx; 
4    rx = ry; 
5    ry = tmp; 
6}
7void Swap(int* rx,int* ry)
8{
9    int tmp=rx;
10    rx=ry;
11    ry=tmp;
12}
13//这两个函数可以同时存在
14int main() 
15{ 
16    int x = 0, y = 1;
17    Swap(x,y);
18    cout << x <<" " << y << endl; 
19    Swap(&x, &y); 
20    cout << x << " " << y << endl; 
21    return 0; 
22}
23
1//指针
2void Swap(int*& rx, int*& ry) 
3{ 
4    int* tmp = *rx; 
5    *rx = *ry; 
6    *ry = *tmp; 
7}
8void Swap(int** rx,int** ry)
9{
10    int* tmp=*rpx;
11    *rpx=*rpy;
12    *rpy=*tmp;
13}
14//这两个函数不可以同时存在,会出现调用不明确的风险
15int main() 
16{ 
17    int* x = &a;
18    int* y = &b;
19    Swap(x,y);
20    cout << x <<" " << y << endl; 
21    Swap(&x, &y); 
22    cout << x << " " << y << endl; 
23    return 0; 
24}
25
1#include<iosteam>  
2using namespace std; 
3typedef struct SeqList 
4{ 
5    int a[10]; 
6    int size; 
7}SLT; 
8// ⼀些主要⽤C代码实现版本数据结构教材中,使⽤C++引⽤替代指针传参,⽬的是简化程序,避开复杂的指针,但是很多同学没学过引⽤,导致⼀头雾⽔。  
9void SeqPushBack(SLT& sl, int x) 
10{    
11} 
12typedef struct ListNode 
13{ 
14    int val; 
15    struct ListNode* next; 
16}LTNode, *PNode;
17//相当于typedef struct ListNode *PNode;(重命名)
18//typedef struct ListNodeLTNode;
19
20// 指针变量也可以取别名,这⾥LTNode*& phead就是给指针变量取别名  
21// 这样就不需要⽤⼆级指针了,相对⽽⾔简化了程序  
22//void ListPushBack(LTNode** phead, int x) 
23//void ListPushBack(LTNode*& phead, int x) 
24void ListPushBack(PNode& phead, int x) 
25{ 
26    PNode newnode = (PNode)malloc(sizeof(LTNode)); 
27    newnode->val = x; 
28    newnode->next = NULL; 
29    if (phead == NULL) 
30    { 
31        phead = newnode; 
32    } 
33    else 
34    { 
35        //找到尾结点,newnode链接到尾结点 
36    } 
37} 
38int main() 
39{ 
40    PNode plist = NULL; 
41    ListPushBack(plist, 1); 
42    return 0; 
43} 
44

传值返回
func栈帧局部结束后销毁,ret的空间释放,传值返回,返回的是拷贝的临时变量

1int func()
2{
3	int ret=0;
4	return ret;
5}
6int main()
7{
8	int a=func();
9	return 0;
10}
11
12

传引用返回
这个代码很危险,访问的是野指针(与拷贝无关),结果可能是随机值或0
但越界访问为什么不报错呢
因为越界不一定报错,越界是抽查

1int& func()
2{
3	int ret=0;
4	return ret;
5}
6//相当于我们返回了ret的别名
7
8int main()
9{
10	int a=func();
11	cout<<a<<endl;
12	return 0;
13}
14

小小的延伸

有个很神奇的现象

1int& func1()
2{
3    int ret=0;
4    return ret;
5}
6int& func2()
7{
8    int y=123;
9    return y;
10}
11int main()
12{
13	int& a=func1();
14	cout<<a<<endl;
15    func2();
16    cout<<a<<endl;
17	return 0;
18}
19

运行结果
在这里插入图片描述

为什么a的值变成了123?
可以这样理解:func1的栈帧销毁后,系统又将func1的空间分配给了func2,a又是ret的别名,ret的内存空间释放后,a还指向那块空间,而那块空间又变成了y,所以a输出的是y的值

  • 那怎么修改呢
1int& func1()
2{
3    static int ret=0;
4    return ret;
5}
6int& func2()
7{
8    int y=123;
9    return y;
10}
11int main()
12{
13	int& a=func1();
14	cout<<a<<endl;
15    func2();
16    cout<<a<<endl;
17	return 0;
18}
19

1.4 const引用

• 可以引⽤⼀个const对象,但是必须⽤const引⽤。const引⽤也可以引⽤普通对象,因为对象的访问权限在引⽤过程中可以缩⼩,但是不能放⼤。
权限的放大和缩小只存在于指针和引用

• 不需要注意的是类似 int& rb = a* 3; double d = 12.34; int& rd = d; 这样⼀些场景下a*3的和结果保存在⼀个临时对象中, int& rd = d 也是类似,在类型转换中会产⽣临时对象存储中间值,也就是rb和rd引⽤的都是临时对象,⽽C++规定临时对象具有常性,所以这⾥就触发了权限放⼤,必须要⽤常引⽤才可以。

• 所谓临时对象就是编译器需要⼀个空间暂存表达式的求值结果时临时创建的⼀个未命名的对象, C++中把这个未命名对象叫做临时对象。

1int main() 
2{ 
3    const int a = 10; 
4    // 编译报错:error C2440: “初始化”: ⽆法从“const int”转换为“int &”  
5    // 这⾥的引⽤是对a访问权限的放⼤  
6    int& ra = a; 
7    
8	// 这样才可以  
9    const int& ra = a; 
10    
11    // 编译报错:error C3892: “ra”: 不能给常量赋值  
12    ra++; 
13    
14    // 这⾥的引⽤是对b访问权限的缩⼩,可以  
15    int b = 20; 
16    const int& rb = b; 
17    
18    // 编译报错:error C3892: “rb”: 不能给常量赋值  
19    //rb++; 
20    
21    return 0; 
22}
23
1//指针
2const int* p1=&a;
3//报错:不能权限放大
4int* p2=p1;
5
6//可以权限缩小
7int* p3=&e;
8const int* p4=p3;
9
10

权限缩小的应用

1//如果不希望形参的改变影响实参,最好给形参加上const
2void func(const int& x)
3{
4}
5int main()
6{
7	int y=0;
8	func(y);
9	
10	//形参加上const才会成功传参
11	const int z=1;
12	func(z);
13	func(1);
14	
15	return 0;
16}
17

再讲第二个例子之前,先理解下面的例子

1int i=1;
2double d=i;
3
4int p=(int)&i;
5

实际上i先给了一个临时变量,临时变量再给d,&i先强制转成int型,再给一个临时变量,临时变量再给p。临时变量具有常性,就像被const修饰一样

1#include<iosteam> 
2using namespace std; 
3int main() 
4{ 
5    int a = 10; 
6    const int& ra = 30;  
7    
8    // 编译报错: “初始化”: ⽆法从“int”转换为“int &”  
9    int& rb = a * 3;
10      
11    //可以
12    const int& rb = a*3; 
13    
14    double d = 12.34; 
15    // 编译报错:“初始化”: ⽆法从“double”转换为“int &”  
16    int& rd = d;
17    
18    const int& rd = d; 
19    return 0; 
20} 
21

1.5 指针和引用的关系(面试常考)

C++中指针和引⽤在实践中他们相辅相成,功能有重叠性,但是各有⾃⼰的特点,互相不可替代。

• 语法概念上引⽤是⼀个变量的取别名不开空间,指针是存储⼀个变量地址,要开空间。

• 引⽤在定义时必须初始化,指针建议初始化,但是语法上不是必须的。

• 引⽤在初始化时引⽤⼀个对象后,就不能再引⽤其他对象;⽽指针可以在不断地改变指向对象。

• 引⽤可以直接访问指向对象,指针需要解引⽤才是访问指向对象。

• sizeof中含义不同,引⽤结果为引⽤类型的⼤⼩,但指针始终是地址空间所占字节个数(32位平台下占4个字节,64位下是8个字节)

• 指针很容易出现空指针和野指针的问题,引⽤很少出现,引⽤使⽤起来相对更安全⼀些。


【C++】c++中“引用”的详解》 是转载文章,点击查看原文


相关推荐


Node.js 自带“加速器”:node --run 是否比 Bun 更快?
Legend80s2026/2/3

前言 在 JavaScript 后端运行时领域,速度一直是核心战场。近年来,Bun 以其宣称的“一体化”工具链和闪电般的启动速度异军突起,对老牌王者 Node.js 发起了强劲挑战。bun run 的迅捷,让许多开发者开始重新评估他们的工具选择。 然而,Node.js 并未止步。自 v22.0.0 起,它悄然引入了一个专为启动性能而生的秘密武器:node --run。这个内置于 Node.js 核心的命令,旨在以最精简、最直接的方式执行 package.json 中的脚本,宣称要为最常见的用例提


拥抱PostgreSQL支持UI配置化
神奇的程序员2026/1/25

前言 前阵子写的日志分析工具NginxPulse,自开源以来,已过去2周时间,目前GitHub已收获1.5k的star。收到了不少用户的反馈建议,花了点时间将这些问题都处理了下。 本文就跟大家分享下新版本都解决了哪些问题,优化了哪些内容,欢迎各位感兴趣的开发者阅读本文。 抛弃SQLite 有不少用户反馈说日志文件很大的时候(10G+),解析速度非常慢,需要解析好几个小时,解析完成之后数据看板的查询也比较慢(接口响应在5秒左右)。 于是,我重写了日志解析策略(解析阶段不做IP归属地查询,仅入库其他


Mac 科研/论文专用快捷键(Word + LaTeX + Finder) 与文件管理
加油_Yeah2026/1/16

✅ 一、只背 15 个的「Mac 生存快捷键」 只记这 15 个,就能高效 + 不踩坑 🔑 核心通用(8 个) ⌘ + Space —— 全局搜索(秒开文件/程序) ⌘ + Z —— 撤销(后悔药) ⌘ + ⇧ + Z —— 重做 ⌘ + C —— 复制 ⌘ + V —— 粘贴 ⌘ + ⌥ + V —— 移动文件(重点) ⌘ + S —— 保存 ⌘ + Q —— 彻底退出程序 📁 文件 / 窗口(7 个) ⌘ + ⇧ + N


微服务架构核心组件、职责与交互全解析
元Y亨H2026/1/8

微服务架构核心组件、职责与交互全解析 一、 微服务全景架构图(分层) 微服务不再是散乱的工程,而是一个分工明确的矩阵。通过分层,我们可以更清晰地看到请求是如何流转的。 ==================== 流量接入层 (Entrance) ==================== [ 外部客户端:App / H5 / Web / PC ] │ (Restful API / HTTPS) ┌───────


2025年12月总结
袁庭新2025/12/31

大家好,我是袁庭新。2025年的最后一个月已经圆满结束,借此机会对本月的工作进行一次总结与回顾。 课程研发 一直想讲一门如何赚钱的课,这就是《微信商业生态平民创业》这门课设计的初衷,这个月编写了2节这门课的讲义,但还未完成录制,现总计录制了20节,计划是24节课程。 放在以前软件开发和我们绝大多数人是没有关系,随着大模型基础服务平台的崛起,你可能无法想象现在不懂任何编程技术零基础也可快速上手定制开发出自己的智能体应用出来,并上线到各大平台,如微信小程序、豆包等。 这个月我也集中注意力开发了7个实


flink的一阶段提交的流程
飞哥大数据2025/12/21

Flink的一阶段提交流程 Apache Flink 是一个分布式流处理框架,用于高效处理大规模数据流。在 Flink 中,“提交”通常指将作业部署到集群执行的过程。用户提到的“一阶段提交”可能指的是 Flink 中某些特定场景下的简化提交机制,尤其是在事务处理或 Sink 端(输出端)的 Exactly-Once 语义实现中。标准 Flink 作业提交涉及多个步骤,但“一阶段提交”更常见于事务管理上下文,例如当 Sink 系统支持幂等操作时,Flink 可以使用一阶段提交来简化流程,避免两阶


告别AI塑料感:阿里Qwen3-Omni-Flash要把大模型做成真人
墨风如雪2025/12/13

如果是长期关注大模型领域的朋友,大概都会有这样一种感觉:现在的AI虽然智商越来越高,但只要一开口说话,那种特有的“塑料感”还是很难消除。无论是语音的机械停顿,还是多模态交互时的“脑子慢半拍”,都时刻提醒着我们,对面只是个程序。 但在2025年12月9日,这个局面似乎被阿里的Qwen团队撕开了一道口子。 他们正式发布的Qwen3-Omni-Flash-2025-12-01,不再仅仅是在刷榜单上的分数(虽然分数确实也刷得很猛),而是实打实地盯着“像人一样交流”这件事死磕。作为一名在这个圈子里摸爬滚打


web3区块链-小镇店铺的 “借力办事”:call 与 delegatecall 的区别与联系Web3-智能合约-整数溢出攻击:“凭空造币”的秘密
想ai抽2025/12/4

加密小镇上有两家店: A 店(水果店):老板是 Alice,有自己的账本(合约存储),记录着 “苹果库存”(存储变量uint256 public appleStock = 100;),但没学会 “盘点库存”“修改库存” 的方法;B 店(管理咨询店):老板是 Bob,专门帮人做库存管理,有两套核心 “操作手册”(合约函数): checkStock():读取自己账本上的库存,返回数值;addStock(uint256 num):把自己账本上的库存增加num个。 A 店想复用 B 店的方法


Hadoop MapReduce 详解
之歆2026/2/20

想象一个巨大的文档分类任务,一个人处理要一个月。MapReduce 把这个任务分配给一百个人,每人处理一小部分,最后把结果汇总,半小时就完成了!这就是 MapReduce 的威力! 📑 目录 MapReduce 概述名词解释(命令与术语)Map 阶段详解Shuffle 阶段详解Reduce 阶段详解完整 WordCount 示例高级特性MapReduce 与 YARN监控与调试性能优化总结官方文档与参考 🎯 MapReduce 概述 什么是 MapReduce? Map


我的“龙虾”罢工了!正好对比下GLM、MiniMax、Kimi 3家谁更香
飞哥数智谈2026/3/1

OpenClaw(中文名:龙虾,曾用名:Clawdbot、Moltbot)从年底到现在热度持续飙升,而我感觉自己使用的还有些浅,没好意思写相关的内容。 结果今天我的龙虾罢工了,看了下,发现模型的 Coding Plan 到期了。 那正好了解下目前国内几家 Coding Plan 的内容,也方便大家对比。 本文仅涉及 GLM、Minimax、Kimi 3家自有产品的订阅套餐对比,像火山引擎、千问、无问芯穹类的综合套餐未加入对比。 为了方便对比,所有套餐都是采用按月 快速对比 核心指标整理到一

首页编辑器站点地图

本站内容在 CC BY-SA 4.0 协议下发布

Copyright © 2026 XYZ博客