探索C嘎嘎的奇妙世界:第四关---引用与内联函数

news2024/11/27 15:52:42

1 引用:

        引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。       

#include<iostream>
using namespace std;

int main()
{
	int a = 0;

	// 引用:b是a的别名

	int& b = a;
	int& c = a;
	int& d = b;

	++d;

	cout << &a << endl;
	cout << &b << endl;
	cout << &c << endl;
	cout << &d << endl;

	return 0;
}

        在上述代码中,我们定义了一个整型变量a=0,然后,这样"int&b=a"定义了一个b,这时就有小伙伴看不懂了?这个"&",不是C语言中的取地址吗?这是什么意思?

        类型& 引用变量名(对象名) = 引用实体,在C++中,"&"被我们的祖师爷赋予了另一种含义"引用"

        引用是给对象起的一个别名,它也是一种复合类型。通过将声明符写成 &d 的形式来定义引用类型,其中 d 是声明的变量名。定义引用时,程序把引用和它的初始值绑定在一起,而不是将初始值拷贝给引用。一旦初始化完成,引用将和它的初始值对象一直绑定在一起。因此引用必须初始化,而且引用不能重新绑定到另一个对象,类型要和绑定的类型一致。引用本身不是独立的数据类型,所以不占用空间,引用的地址就是对象的地址。也就无法定义指向引用的指针,无法声明存储引用类型的数组等.

        在上述代码中,小伙伴们,可以看到b和c都是对a的引用,d是对a的引用,那我们对d进行++操作,会不会改变其他几个呢?

        当然,上面说了,引用就相当于起了个别名,比如:小三是张三的别名,小三吃饭了,那我们猜猜张三吃饭了吗?,肯定也吃了呀.所以引用只是给他换了一个名字,但其实还是本身,所以对d进行++会对其它所有别名进行改变.

        那么引用是用来干嘛的呢?请看下面代码:

void Swap(int* a, int* b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}

void Swap(int& a, int& b)
{
	int tmp = a;
	a = b;
	b = tmp;
}

int main()
{
    int a=10,b=20;

    //Swap(&a,&b);//第一种

    Swap(a,b);//第二种
    
    return 0;
}

        在上述代码中,第一种方法想必是大多数小伙伴常用的指针,那么第二种就是我们今天要学习的"引用"从定义角度和调用角度,二者都有所差距.指针是通过传地址进行改变,而引用是通过给实参取别名,从而进行交换,需要注意的是引用的地址和数据本身的地址是相同的.看起来是不是引用用起来更加方便了?

        同时指针不止能给一些内置类型取别名,还能给指针取别名哦~

int main()
{
	int x = 0;
	int* p1 = &x;
	int*& pr = p1;
	pr = NULL;
    return 0;
}

        在上述代码中,我们用一个指针p1去存储x的地址,用pr去给p1套了一层引用,然后对pr进行置空,这样p1也就置空了.所以这样来看引用还是挺好的

1.1引用的特性:

        1. 引用在定义时必须初始化

void TestRef()
{
   int a = 10;
   // int& ra;   // 该条语句编译时会出错
   int& ra = a;
   int& rra = a;
   printf("%p %p %p\n", &a, &ra, &rra);  
}

        上述代码中如果只是定义一个:"int& ra"而不对其进行初始化的话,编译器会报错,取别名总要有一个主体吧

        2. 一个变量可以有多个引用

        3. 引用一旦引用一个实体,再不能引用其他实体

1.2 常引用:

int main()
{	
    // 权限的平移
	int x = 0;
	int& y = x;

	// 权限的缩小,可以
	const int& z = x;
	//z++; // 不可以
	y++;//可以

	// 权限的放大
	// m只读
	// n变成我的别名,n的权限是可读可写
	// 权限的放大,不可以
	const int m = 0;
	// int& n = m;

	const int& n = m;

    return 0;
}

        上述代码中,第一处:定义了一个int型的x=0,然后给x取了一个别名y,,都是可读可写的, 这样写是合乎语法的;

        第二处:定义了一个const int的z并且给x取别名了,这样写是相当于权限的缩小,因为x是可读可写的,而z是只能读,所以z的权限被缩小了,完全是可以的,同时要注意,我们不能通过z去进行修改,只能通过y去修改,那么就有小伙伴要问了,y改变了,z变了吗?先给结论,z也会跟着变,为什么呢?因为const只是不能去直接修改它所修饰的变量,但是可以间接的去修改;

        第三处:定义了一个const int 型的m,然后定义了一个int型的n给m进行取别名,可不可以呢?肯定是不行的嘛.因为m是只能读不能写的,而n是可读可写的,这样就权限放大了不合乎语法会报错,只有把n也该成const int型的才可以,相当于权限的平移.

        关于引用的东西远不于此过不了多久就会再次相见~~~

1.3 引用的探讨:

        那我们再探讨一个问题:引用开不开空间呢?

        这个问题我们可以从汇编层次来看:

        从这里我们可以看出,在汇编层次上来看,指针和引用好像是你"亲兄弟"一样,哈哈~是的,它们在汇编层面只是寄存器不同,其他都相同,我们都知道指针是开空间的,所以我们可以得出结论了:

        结论:引用在会汇编层面是和指针一样需要开空间的,但是我们在语法上通常认为引用是不开空间的. 

1.4 引用与指针的对比及优缺点:

1.4.1 引用和指针的对比:

1. 引用概念上定义一个变量的别名,指针存储一个变量地址。
2. 引用 在定义时 必须初始化 ,指针没有要求
3. 引用 在初始化时引用一个实体后,就 不能再引用其他实体 ,而指针可以在任何时候指向任何
一个同类型实体
4. 没有 NULL 引用 ,但有 NULL 指针 (这种说法并不绝对)
5. sizeof 中含义不同 引用 结果为 引用类型的大小 ,但 指针 始终是 地址空间所占字节个数 (32
位平台下占 4 个字节 )
6. 引用自加即引用的实体增加 1 ,指针自加即指针向后偏移一个类型的大小
7. 有多级指针,但是没有多级引用
8. 访问实体方式不同, 指针需要显式解引用,引用编译器自己处理
9. 引用比指针使用起来相对更安全

10.内存占用:引用不占内存,而指针占用内存。
11.数组和指针:不能声明引用数组,可以声明指针数组。
12.引用的引用:不能定义引用的引用,而可以定义指针的指针。

1.4.2 引用的优点:

1.性能更好:引用的性能可能会更好,因为指针会导致变量在栈里一定会有地址,可能会引起反复解引用的问题,而引用可以让无需压栈的变量一直在寄存器里,不生成地址。
2.更安全:引用本身是目标变量或对象的别名,对引用的操作本质上就是对目标变量或对象的操作。因此能使用引用时尽量使用引用而非指针。

1.4.3 指针的优点:

1.灵活性高:指针可以指向任意类型的对象,并且可以在运行时动态地改变指向的对象。
2.可以为空:指针可以为空,这在某些情况下是很有用的,例如表示一个不存在的对象或者表示一个结束标志。

        总的来说,引用和指针都有各自的优缺点,在使用时需要根据具体情况进行选择。如果需要高效地传递大型对象或者需要在函数内部修改外部变量的值,引用是一个不错的选择。如果需要灵活地操作对象或者需要处理动态分配的内存,指针可能更合适。

2 内联函数:

2.1 内联函数的概念:

        内联函数是在程序编译时,编译器会将内联函数的代码直接嵌入到调用它的地方,而不是像普通函数那样进行函数调用的一系列操作(如建立栈帧等)。

        以 inline修饰 的函数叫做内联函数, 编译时 C++ 编译器会在 调用内联函数的地方展开 ,没有函数调,用建立栈帧的开销,内联函数提升程序运行的效率。

        如果在上述函数前增加inline关键字将其改成内联函数,在编译期间编译器会用函数体替换函数的调用。

查看方式:

        1. 在release模式下,查看编译器生成的汇编代码中是否存在call Add

        2. 在 debug 模式下,需要对编译器进行设置,否则不会展开 ( 因为 debug 模式下,编译器默认不会对代码进行优化,以下给出vs2013 的设置方式)

2.2 内联函数的特性:

        1. inline是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会 用函数体替换函数调用,缺陷:可能会使目标文件变大,优势:少了调用开销,提高程序运 行效率。

2. inline对于编译器而言只是一个建议,不同编译器关于inline实现机制可能不同,一般建 议:将函数规模较小(即函数不是很长,具体没有准确的说法,取决于编译器内部实现)是递归、且频繁调用的函数采用inline修饰,否则编译器会忽略inline特性。下图为

C++prime 》第五版关于 inline 的建议: 
        
3. inline 不建议声明和定义分离,分离会导致链接错误。因为 inline 被展开,就没有函数地址
了,链接就会找不到.上示例代码:
// F.h
#include <iostream>
using namespace std;
inline void f(int i);
// F.cpp
#include "F.h"
void f(int i)
{
    cout << i << endl;
}



// main.cpp
#include "F.h"
int main()
{
     f(10);
     return 0;
}

在上述代码中,如果强行对内联函数进行声明和定义分离,那么编译器会报一个这样的错误:

// 链接错误:main.obj : error LNK2019: 无法解析的外部符号 "void __cdecl f(int)" (?f@@YAXH@Z),该符号在函数 _main 中被引用

总结:短小函数定义 换用内联函数

        编译器并不一定会完全按照开发者的意愿将函数内联,它会根据实际情况(如函数的复杂性等)来决定是否真正进行内联操作。总的来说,内联函数是一种优化技术,旨在提高程序的性能和可读性,但需要合理使用以避免潜在的问题。

        到此为止,有关引用和内联函数的讲解就结束了~希望这篇博客能给您带来一些启发和思考!那我们下次再一起探险喽,欢迎在评论区进行讨论~~~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1825673.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

谷歌重塑Transformer:无限记忆力,无限长输入,登上Nature

Infini-attention机制为Transformer在具有挑战性的长语境任务中释放出了新的能力&#xff0c;对于调整现有模型以适应长输入也非常实用。 谷歌的最新研究成果Infini-attention机制&#xff08;无限长注意力&#xff09;将内存压缩引入了传统注意力机制&#xff0c;并在单个Tra…

ffmpeg封装和解封装介绍-(8)解封装和封装重构

头文件&#xff1a; xformat.h #pragma once/// 封装和解封装基类#include <mutex> struct AVFormatContext; struct AVCodecParameters; struct AVPacket; struct XRational {int num; ///< Numeratorint den; ///< Denominator }; class XFormat { public:/// &…

注册中心理论学习

注册中心介绍 注册中心&#xff08;也称为服务注册中心或服务发现服务&#xff09;是微服务架构中的一个关键组件&#xff0c;它负责服务的注册与发现。在微服务体系中&#xff0c;服务实例的数量和位置是动态变化的&#xff0c;注册中心提供了一个集中的地方来存储这些信息&a…

比特币全节点搭建

比特币全节点搭建 参考: https://www.cnblogs.com/elvi/p/10203927.html

聚焦赛宁网安竞赛平台+赛事服务,引领网络安全竞赛新潮流

第八届XCTF总决赛将在2024年6月22日于中国成都震撼开启&#xff0c;本届总决赛分为个人Live Solo和团队KOH巅峰对决两个赛道&#xff0c;从个人和团队多角度全方位考察参赛人员的竞技水平。 巅峰对决 智慧的火花在此碰撞 个人Live Solo赛制 Live Solo赛分为晋级赛和Solo赛。…

leetcode刷题记录42-1584. 连接所有点的最小费用

问题描述 给你一个points 数组&#xff0c;表示 2D 平面上的一些点&#xff0c;其中 points[i] [xi, yi] 。 连接点 [xi, yi] 和点 [xj, yj] 的费用为它们之间的 曼哈顿距离 &#xff1a;|xi - xj| |yi - yj| &#xff0c;其中 |val| 表示 val 的绝对值。 请你返回将所有点连…

2.深度学习-线性回归

文章目录 环境配置&#xff08;必看&#xff09;线性回归代码工程运行结果1.对比图2.运行结果 环境配置&#xff08;必看&#xff09; Anaconda-创建虚拟环境的手把手教程相关环境配置看此篇文章&#xff0c;本专栏深度学习相关的版本和配置&#xff0c;均按照此篇文章进行安装…

工业 UI 风格,展现独特魅力

工业 UI 风格&#xff0c;展现独特魅力

「TCP 重要机制」滑动窗口 粘包问题 异常情况处理

&#x1f387;个人主页&#xff1a;Ice_Sugar_7 &#x1f387;所属专栏&#xff1a;计网 &#x1f387;欢迎点赞收藏加关注哦&#xff01; 滑动窗口&粘包问题&异常情况处理 &#x1f349;滑动窗口&#x1f34c;流量控制&#x1f34c;拥塞控制&#x1f34c;延时应答&…

【Agent应用】营销大师 | 文案创作助手

&#x1f495;营销大师 | 文案创作助手&#x1f495; &#x1f525; 解锁无限创意可能&#xff01;想要轻松驾驭朋友圈、小红书、公众号&#xff1f;渴望一键生成PPT提纲、诗词创作、作文灵感&#xff1f;我们应有尽有&#xff01; &#x1f31f; 功能亮点 朋友圈文案&#x1…

为什么文件占用的空间会大于文件实际大小

以windows系统举例&#xff0c;先看几张图&#xff1a; 图1&#xff1a;文件的大小比占用空间更小。 图2&#xff1a;文件的大小和占用空间大小一致。 图3&#xff1a;文件大小比占用空间大小更大。 上面3张图展示了文件大小与占用空间的大小几种情况&#xff0c;前两种场景是…

【Java】解决Java报错:MalformedURLException in URL Construction

文章目录 引言一、MalformedURLException的定义与概述1. 什么是MalformedURLException&#xff1f;2. MalformedURLException的常见触发场景3. 示例代码 二、解决方案1. 检查并纠正URL格式2. 使用URI类进行预处理3. 动态构建URL4. 使用第三方库进行URL验证 三、最佳实践1. 始终…

Internet地址

要求和目的 深入理解java InetAddress类的基本用法&#xff0c;并且可以掌握Java程序的编写和调试。 实验环境 Java语言&#xff0c;PC平台 实验具体内容 设计和编写以下程序&#xff1a; 程序1&#xff1a; 1&#xff09;编写可以获得本机地址的Java程序&#xff0c;然…

k8s学习--helm的详细解释及安装和常用命令

文章目录 Helm简介什么是Helm主要组件核心概念chart结构总结 应用环境一、helm部署二、helm基础使用 Helm简介 什么是Helm Helm 是 Kubernetes 的一个包管理工具&#xff0c;它允许用户定义、安装和升级复杂的 Kubernetes 应用程序。Helm 通过使用 “Charts” 的概念来简化应…

在k8s上部署一个简单的应用

部署一个简单的应用 实验目标&#xff1a; 部署一个简单的 web 应用&#xff0c;比如 Nginx 或者一个自定义的 Node.js 应用。 实验步骤&#xff1a; 创建一个 Deployment。创建一个 Service 来暴露应用。验证应用是否可以通过 Service 访问。 今天我们来做一下昨天分享的可…

HTML静态网页成品作业(HTML+CSS)—— 零食商城网页(1个页面)

&#x1f389;不定期分享源码&#xff0c;关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 &#x1f3f7;️本套采用HTMLCSS&#xff0c;未使用Javacsript代码&#xff0c;共有1个页面。 二、作品演示 三、代…

模型 WOOP

说明&#xff1a;系列文章 分享 模型&#xff0c;了解更多&#x1f449; 模型_思维模型目录。不再拖延和懒惰&#xff0c;让梦想照进现实。 1 WOOP模型的应用 1.1 WOOP模型提高自己健身习惯 如果你想要养成健身的习惯&#xff0c;那么使用WOOP模型来提高自己健身习惯&#xf…

【菜狗学前端】uniapp(vue3|微信小程序)实现外卖点餐的左右联动功能

记录&#xff0c;避免之后忘记...... 一、目的&#xff1a;实现左右联动 右->左 滚动&#xff08;上拉/下拉&#xff09;右侧&#xff0c;左侧对应品类选中左->右 点击左侧品类&#xff0c;右侧显示对应品类 二、实现右->左 滚动&#xff08;上拉/下拉&#xff09;右…

awtk如何实现键盘和输入框

1.创建默认键盘 新建窗体-keyboard 2.新建编辑框 3.设置编辑框属性 4.点击编辑框即可打开默认键盘&#xff0c;若想修改键盘样式可以在默认键盘修改或自定义键盘 5.获取输入字符 widget_t* wifi_edit widget_lookup(win, "edit", TRUE);//获取单行编辑控件 widge…

HAL库开发--串口

知不足而奋进 望远山而前行 目录 文章目录 前言 学习目标 学习内容 开发流程 串口功能配置 串口功能开启 串口中断配置 串口参数配置 查询配置结果 发送功能测试 中断接收功能测试 printf配置 DMA收发 配置 DMA发送 DMA接收(方式1) DMA接收(方式2) 总结 前言…