当前位置: 首页 > news >正文

浅析C++的指针与引用

浅析C++的指针与引用

文章目录

  • 浅析C++的指针与引用
    • 一、对比引用与指针
    • 二、引用
        • 左值引用
        • 右值引用
        • 引用折叠
    • 三、指针与引用的性能差距
    • 总结

一、对比引用与指针

总论:

引用指针
必须初始化可以不初始化
不能为空可以为空
不能更换目标可以更换目标

引用必须初始化,而指针可以不初始化。

我们在定义一个引用的时候必须为其指定一个初始值,但是指针却不需要。

int &r;    //不合法,没有初始化引用
int *p;    //合法,但p为野指针,使用需要小心

引用不能为空,而指针可以为空。

由于引用不能为空,所以我们在使用引用的时候不需要测试其合法性,而在使用指针的时候需要首先判断指针是否为空指针,否则可能会引起程序崩溃。

void test_p(int* p)
{if(p != null_ptr)    //对p所指对象赋值时需先判断p是否为空指针*p = 3;return;
}
void test_r(int& r)
{r = 3;    //由于引用不能为空,所以此处无需判断r的有效性就可以对r直接赋值return;
}

引用不能更换目标

指针可以随时改变指向,但是引用只能指向初始化时指向的对象,无法改变。

int a = 1;
int b = 2;int &r = a;    //初始化引用r指向变量a
int *p = &a;   //初始化指针p指向变量ap = &b;        //指针p指向了变量b
r = b;         //引用r依然指向a,但a的值变成了b

二、引用

左值引用

常规引用,一般表示对象的身份。

右值引用

右值引用就是必须绑定到右值(一个临时对象、将要销毁的对象)的引用,一般表示对象的值。

右值引用可实现转移语义(Move Sementics)和精确传递(Perfect Forwarding),它的主要目的有两个方面:

  • 消除两个对象交互时不必要的对象拷贝,节省运算存储资源,提高效率。
  • 能够更简洁明确地定义泛型函数。
引用折叠
  • X& &X& &&X&& & 可折叠成 X&
  • X&& && 可折叠成 X&&

例如,当我们有一个模板函数参数声明为 T&&,并且传递给它一个左值时,类型 T 会被推导为左值引用类型。这就是所谓的万能引用,它可以根据传入的参数类型自动调整为左值引用或右值引用。

这里有一个简单的例子来说明引用折叠:

template<typename T>
void forward(T&& arg) {// arg 可以是左值引用或右值引用
}int main() {int x = 10;forward(x);  // x 是左值,T 被推导为 int&forward(10); // 10 是右值,T 被推导为 int&&
}

在这个例子中,forward 函数可以接受任何类型的参数,无论是左值还是右值,而不需要为每种情况编写重载函数。这就是引用折叠和右值引用在现代C++编程中的妙用。

C++的引用在减少了程序员自由度的同时提升了内存操作的安全性和语义的优美性。比如引用强制要求必须初始化,可以让我们在使用引用的时候不用再去判断引用是否为空,让代码更加简洁优美,避免了指针满天飞的情形。除了这种场景之外引用还用于如下两个场景:

引用型参数

一般我们使用const reference参数作为只读形参,这种情况下既可以避免参数拷贝还可以获得与传值参数一样的调用方式

void test(const vector<int> &data)
{//...
}
int main()
{vector<int> data{1,2,3,4,5,6,7,8};test(data);
}

引用型返回值

C++提供了重载运算符的功能,我们在重载某些操作符的时候,使用引用型返回值可以获得跟该操作符原来语法相同的调用方式,保持了操作符语义的一致性。一个例子就是operator []操作符,这个操作符一般需要返回一个引用对象,才能正确的被修改。这就是我们常用的链式调用

vector<int> v(10);
v[5] = 10;    //[]操作符返回引用,然后vector对应元素才能被修改//如果[]操作符不返回引用而是指针的话,赋值语句则需要这样写
*v[5] = 10;   //这种书写方式,完全不符合我们对[]调用的认知,容易产生误解

三、指针与引用的性能差距

指针与引用之间有没有性能差距呢?这种问题就需要进入汇编层面去看一下。我们先写一个test1函数,参数传递使用指针:

void test1(int* p)
{*p = 3;    //此处应该首先判断p是否为空,为了测试的需要,此处我们没加。return;
}

该代码段对应的汇编代码如下:

(gdb) disassemble 
Dump of assembler code for function test1(int*):0x0000000000400886 <+0>:  push   %rbp0x0000000000400887 <+1>:  mov    %rsp,%rbp0x000000000040088a <+4>:  mov    %rdi,-0x8(%rbp)
=> 0x000000000040088e <+8>:  mov    -0x8(%rbp),%rax0x0000000000400892 <+12>: movl   $0x3,(%rax)0x0000000000400898 <+18>: nop0x0000000000400899 <+19>: pop    %rbp0x000000000040089a <+20>: retq   
End of assembler dump.

上述代码1、2行是参数调用保存现场操作;第3行是参数传递,函数调用第一个参数一般放在rdi寄存器,此行代码把rdi寄存器值(指针p的值)写入栈中;第4行是把栈中p的值写入rax寄存器;第5行是把立即数3写入到rax寄存器值所指向的内存中,此处要注意(%rax)两边的括号,这个括号并并不是可有可无的,(%rax)和%rax完全是两种意义,(%rax)代表rax寄存器中值所代表地址部分的内存,即相当于C++代码中的*p,而%rax代表rax寄存器,相当于C++代码中的p值,所以汇编这里使用了(%rax)而不是%rax。

我们再写出参数传递使用引用的C++代码段test2:

void test2(int& r)
{r = 3;    //赋值前无需判断reference是否为空return;
}

这段代码对应的汇编代码如下:

(gdb) disassemble 
Dump of assembler code for function test2(int&):0x000000000040089b <+0>:  push   %rbp0x000000000040089c <+1>:  mov    %rsp,%rbp0x000000000040089f <+4>:  mov    %rdi,-0x8(%rbp)
=> 0x00000000004008a3 <+8>:  mov    -0x8(%rbp),%rax0x00000000004008a7 <+12>: movl   $0x3,(%rax)0x00000000004008ad <+18>: nop0x00000000004008ae <+19>: pop    %rbp0x00000000004008af <+20>: retq   
End of assembler dump.

我们发现test2对应的汇编代码和test1对应的汇编代码完全相同,这说明C++编译器在编译程序的时候将指针和引用编译成了完全一样的机器码。所以C++中的引用只是C++对指针操作的一个“语法糖”,在底层实现时C++编译器实现这两种操作的方法完全相同


总结

​ C++中引入了引用操作,在对引用的使用加了更多限制条件的情况下,保证了引用使用的安全性和便捷性,还可以保持代码的优雅性。在适合的情况使用适合的操作,引用的使用可以一定程度避免“指针满天飞”的情况,对于提升程序稳定性也有一定的积极意义。最后,指针与引用底层实现都是一样的,不用担心两者的性能差距

相关文章:

浅析C++的指针与引用

浅析C的指针与引用 文章目录 浅析C的指针与引用一、对比引用与指针二、引用左值引用右值引用引用折叠 三、指针与引用的性能差距总结 一、对比引用与指针 总论&#xff1a; 引用指针必须初始化可以不初始化不能为空可以为空不能更换目标可以更换目标 引用必须初始化&#xff…...

【消息队列开发】 实现消息删除逻辑

文章目录 &#x1f343;前言&#x1f332;实现步骤&#x1f6a9;检验参数的合法性&#x1f6a9;读取Message数据&#x1f6a9;二进制转为message&#x1f6a9;isValid 设置为无效&#x1f6a9;写入文件&#x1f6a9;更新统计文件&#x1f6a9;特别注意&#x1f6a9;完整代码 ⭕…...

【golang】28、用 httptest 做 web server 的 controller 的单测

文章目录 一、构建 HTTP server1.1 model.go1.2 server.go1.3 curl 验证 server 功能1.3.1 新建1.3.2 查询1.3.3 更新1.3.4 删除 二、httptest 测试2.1 完整示例2.2 实现逻辑2.3 其他示例2.4 用 TestMain 避免重复的测试代码2.5 gin 框架的 httptest 一、构建 HTTP server 1.1…...

296.【华为OD机试】污染水域 (图的多源BFS—JavaPythonC++JS实现)

🚀点击这里可直接跳转到本专栏,可查阅顶置最新的华为OD机试宝典~ 本专栏所有题目均包含优质解题思路,高质量解题代码(Java&Python&C++&JS分别实现),详细代码讲解,助你深入学习,深度掌握! 文章目录 一. 题目-污染水域二.解题思路三.题解代码Python题解代码…...

C语言——动态内存分配

前言&#xff1a;通过前面的学习&#xff0c;我们知道C语言中在内存中开辟空间的方法有&#xff1a;变量和数组。既然拥有了开辟空间的方法&#xff0c;我们为什么还要学习动态内存分配呢&#xff1f; int val 20; //在内存中开辟四个字节的空间 int arr[10] { 0 }; //在内…...

瑞_23种设计模式_策略模式

文章目录 1 策略模式&#xff08;Strategy Pattern&#xff09;★1.1 介绍1.2 概述1.3 策略模式的结构1.4 策略模式的优缺点1.5 策略模式的使用场景 2 案例一2.1 需求2.2 代码实现 3 案例二3.1 需求3.2 代码实现 4 JDK源码解析&#xff08;Comparator&#xff09; &#x1f64a…...

使用 OpenAI 的 text-embedding 构建知识向量库并进行相似搜索

OpenAI的embedding模型的使用 首先第一篇文章中探讨和使用了ChatGPT4的API-Key实现基础的多轮对话和流式输出&#xff0c;完成了对GPT-API的一个初探索&#xff0c;那第二步打算使用OpenAI的embedding模型来构建一个知识向量库&#xff0c;其实知识向量库本质上就是一个包含着一…...

设计模式学习笔记 - 规范与重构 - 5.如何通过封装、抽象、模块化、中间层解耦代码?

前言 《规范与重构 - 1.什么情况下要重构&#xff1f;重构什么&#xff1f;又该如何重构&#xff1f;》讲过&#xff0c;重构可以分为大规模高层重构&#xff08;简称 “大型重构”&#xff09;和小规模低层次重构&#xff08;简称 “小型重构”&#xff09;。大型重构是对系统…...

YOLOv9实例分割教程|(二)验证教程

专栏地址&#xff1a;目前售价售价59.9&#xff0c;改进点30个 专栏介绍&#xff1a;YOLOv9改进系列 | 包含深度学习最新创新&#xff0c;助力高效涨点&#xff01;&#xff01;&#xff01; 一、验证 打开分割验证文件&#xff0c;填入数据集配置文件、训练好的权重文件&…...

python 基础知识点(蓝桥杯python科目个人复习计划63)

今日复习内容&#xff1a;做题 例题1&#xff1a;蓝桥骑士 问题描述&#xff1a; 小蓝是蓝桥王国的骑士&#xff0c;他喜欢不断突破自我。 这天蓝桥国王给他安排了N个对手&#xff0c;他们的战力值分别为a1,a2,...,an&#xff0c;且按顺序阻挡在小蓝的前方。对于这些对手小…...

IAB视频广告标准《数字视频和有线电视广告格式指南》之 简介、目录及视频配套广告 - 我为什么要翻译介绍美国人工智能科技公司IAB系列(2)

写在前面 谈及到中国企业走入国际市场&#xff0c;拓展海外营销渠道的时候&#xff0c;如果单纯依靠一个小公司去国外做广告&#xff0c;拉渠道&#xff0c;找代理公司&#xff0c;从售前到售后&#xff0c;都是非常不现实的。我们可以回想一下40年前&#xff0c;30年前&#x…...

Python网络基础爬虫-python基本语法

文章目录 逻辑语句if,else,elifforwhile异常处理 函数与类defpassclass 逻辑语句 熟悉C/C语言的人们可能很希望Python提供switch语句&#xff0c;但Python中并没有这个关键词&#xff0c;也没有这个语句结构。但是可以通过if-elif-elif-…这样的结构代替&#xff0c;或者使用字…...

产品推荐 - 基于星嵌 OMAPL138+国产FPGA的DSP+ARM+FPGA三核开发板

1 评估板简介 基于TI OMAP-L138&#xff08;定点/浮点DSP C674xARM9&#xff09; FPGA处理器的开发板&#xff1b; OMAP-L138是TI德州仪器的TMS320C6748ARM926EJ-S异构双核处理器&#xff0c;主频456MHz&#xff0c;高达3648MIPS和2746MFLOPS的运算能力&#xff1b; FPGA…...

【微服务学习笔记(一)】Nacos、Feign、Gateway基础使用

【微服务学习笔记&#xff08;一&#xff09;】Nacos、Feign、Gateway基础使用 总览Nacos安装配置Nacos注册中心服务多级存储模型负载均衡规则环境隔离 配置管理配置拉取配置热更新多服务共享配置 Feign远程调用配置性能优化Fegin使用 统一网关Gateway搭建网关路由断言工厂&…...

使用maven打生产环境可执行包

一、程序为什么要打包 程序打包的主要目的是将项目的源代码、依赖库和其他资源打包成一个可执行的文件或者部署包&#xff0c;方便程序的发布和部署。以下是一些打包程序的重要理由&#xff1a; 方便部署和分发&#xff1a;打包后的程序可以作为一个独立的实体&#xff0c;方便…...

springboot+ssm基于vue.js的客户关系Crm管理系统

系统包含两种角色&#xff1a;管理员、用户&#xff0c;主要功能如下。 ide工具&#xff1a;IDEA 或者eclipse 编程语言: java 数据库: mysql5.7 框架&#xff1a;ssmspringboot都有 前端&#xff1a;vue.jsElementUI 详细技术&#xff1a;springbootSSMvueMYSQLMAVEN 数据库…...

github 中的java前后端项目整合到本地运行

前言: 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;不提供完整代码&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01; 本文章未…...

分布式ID(7):Zookeeper实现分布式ID生成

1 原理 实现方式有两种,一种通过节点,一种通过节点的版本号 节点的特性持久顺序节点(PERSISTENT_SEQUENTIAL) 他的基本特性和持久节点是一致的,额外的特性表现在顺序性上。在ZooKeeper中,每个父节点都会为他的第一级子节点维护一份顺序,用于记录下每个子节点创建的先后顺序…...

钉钉小程序 - - - - - 如何通过一个链接打开小程序内的指定页面

方式1 钉钉小程序 scheme dingtalk://dingtalkclient/action/open_mini_app?miniAppId123&pagepages%2Findex%2Findex%3Fx%3D%25E4%25B8%25AD%25E6%2596%2587 方式2 https://applink.dingtalk.com/action/open_mini_app?type2&miniAppIdminiAppId&corpIdcorpId&…...

Java代码基础算法练习---2024.3.14

其实这就是从我学校的资源&#xff0c;都比较基础的算法题&#xff0c;先尽量每天都做1-2题&#xff0c;练手感。毕竟离我真正去尝试入职好的公司&#xff08;我指的就是中大厂&#xff0c;但是任重道远啊&#xff09;&#xff0c;仍有一定的时间&#xff0c;至少要等我升本之后…...

深入剖析AI大模型:大模型时代的 Prompt 工程全解析

今天聊的内容&#xff0c;我认为是AI开发里面非常重要的内容。它在AI开发里无处不在&#xff0c;当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗"&#xff0c;或者让翻译模型 "将这段合同翻译成商务日语" 时&#xff0c;输入的这句话就是 Prompt。…...

CTF show Web 红包题第六弹

提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框&#xff0c;很难让人不联想到SQL注入&#xff0c;但提示都说了不是SQL注入&#xff0c;所以就不往这方面想了 ​ 先查看一下网页源码&#xff0c;发现一段JavaScript代码&#xff0c;有一个关键类ctfs…...

【OSG学习笔记】Day 18: 碰撞检测与物理交互

物理引擎&#xff08;Physics Engine&#xff09; 物理引擎 是一种通过计算机模拟物理规律&#xff08;如力学、碰撞、重力、流体动力学等&#xff09;的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互&#xff0c;广泛应用于 游戏开发、动画制作、虚…...

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...

ETLCloud可能遇到的问题有哪些?常见坑位解析

数据集成平台ETLCloud&#xff0c;主要用于支持数据的抽取&#xff08;Extract&#xff09;、转换&#xff08;Transform&#xff09;和加载&#xff08;Load&#xff09;过程。提供了一个简洁直观的界面&#xff0c;以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...

论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)

笔记整理&#xff1a;刘治强&#xff0c;浙江大学硕士生&#xff0c;研究方向为知识图谱表示学习&#xff0c;大语言模型 论文链接&#xff1a;http://arxiv.org/abs/2407.16127 发表会议&#xff1a;ISWC 2024 1. 动机 传统的知识图谱补全&#xff08;KGC&#xff09;模型通过…...

PAN/FPN

import torch import torch.nn as nn import torch.nn.functional as F import mathclass LowResQueryHighResKVAttention(nn.Module):"""方案 1: 低分辨率特征 (Query) 查询高分辨率特征 (Key, Value).输出分辨率与低分辨率输入相同。"""def __…...

Netty从入门到进阶(二)

二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架&#xff0c;用于…...

PostgreSQL——环境搭建

一、Linux # 安装 PostgreSQL 15 仓库 sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-$(rpm -E %{rhel})-x86_64/pgdg-redhat-repo-latest.noarch.rpm# 安装之前先确认是否已经存在PostgreSQL rpm -qa | grep postgres# 如果存在&#xff0…...

PHP 8.5 即将发布:管道操作符、强力调试

前不久&#xff0c;PHP宣布了即将在 2025 年 11 月 20 日 正式发布的 PHP 8.5&#xff01;作为 PHP 语言的又一次重要迭代&#xff0c;PHP 8.5 承诺带来一系列旨在提升代码可读性、健壮性以及开发者效率的改进。而更令人兴奋的是&#xff0c;借助强大的本地开发环境 ServBay&am…...