C++核心编程思路(1):①程序的内存模型②引用的作用

文章目录

  • 前言
  • 一、不同的存储类型变量,会被存储在什么区?
    • ①const修饰的局部变量放在栈区,全局变量放在只读数据区。
    • ②static修饰的全局和局部变量都放在静态区(即数据区中的一个小区)
  • 二、栈区
    • 1.如果在函数A中定义了一个局部变量,那么在主函数里面是无法用取址符&去获取该局部变量的地址的。
    • 2.return可以返回局部变量的值,但是不能返回局部变量的地址。
  • 三、堆区:用new开辟内存空间,用delete销毁内存空间
    • ①在C语言中,可以使用 malloc 函数来动态分配内存空间,并使用 free 函数来释放已分配的内存空间。
    • ②在C++中,可以使用 new 运算符来动态分配内存空间,并使用 delete 运算符来释放已分配的内存空间。
    • ③用new创建的堆区,返回的是这个堆区的地址,所以需要用一个指针去接收。
    • ④new运算符的用法:
  • 三、c++中的引用是干嘛的?
    • ①引用就是给一个变量起一个别名(小名),但是这两个名字代表的是同一块内存空间。
    • ②那变量和其别名的地址一样吗?
      • 答:变量和其别名的地址是相同的。因为引用是变量的别名,它们共享相同的内存地址。在使用引用时,对引用的操作实际上就是对原始变量的操作。
    • ③变量别名有啥用呢?就光是给变量起个小名吗?
      • 答:主要有三个作用:
      • 1.引用可以用作变量的别名,允许使用不同的名称访问同一个变量。
      • 2.作为函数参数:引用可以作为函数的参数,允许在函数内部直接修改传递给函数的变量的值,这样可以避免复制大型对象的开销。(就是类似地址传递)
        • 那既然地址传递和引用别名传递都能切实的改变实参,那为什么要创造引用别名这一传递方式呢?地址传递不是也可以避免复制对象的开销吗?
      • 3.作为函数返回类型:函数的返回值被允许引用别名来代替。这样可以避免对象的复制开销,并且允许对返回的对象进行修改。
        • ①引用作为函数返回值时,不能返回函数中的局部变量。
        • ②函数的引用可以作为左值(即在等号左边)
  • 四、引用的本质到底是什么?——指针常量
    • 引用的本质是一个指针常量,它在创建时被初始化为指向某个对象的地址,并且在其生命周期内不能被修改指向其他对象的地址
  • 五、函数参数传递时,用const修饰引用的参数别名。
    • const常量引用的用处:在函数参数中传递对象,以避免对象(在函数内部)被修改。
      • 常量引用在函数参数中的应用非常常见,它可以避免对象被修改,同时也可以避免对象的复制,提高程序的效率。
  • 总结:引用的最大用处一般是函数的传参,引用可以作为函数的参数,允许在函数内部直接修改传递给函数的变量的值,而无需通过指针或返回值来实现


前言

在嵌入式学习和C语言开发中,已经无数次接触四大内存分区的概念了。
在计算机系统中,内存通常被划分为以下四个主要的分区:

代码区(Text Segment):也称为只读区,用于存储程序的机器指令。在程序执行之前,代码区的内容就已经确定,并且在程序运行期间是不可修改的。

数据区(Data Segment):用于存储全局变量和静态变量。数据区分为两个部分:初始化数据区和未初始化数据区。初始化数据区存储已经初始化的全局变量和静态变量,而未初始化数据区存储未初始化的全局变量和静态变量。

堆区(Heap):用于动态分配内存。在程序运行时,可以通过动态内存分配函数(如malloc、new等)从堆区分配一块内存,然后在不需要时手动释放。

栈区(Stack):用于存储函数调用时的局部变量、函数参数和返回地址等。栈区的内存分配和释放是由编译器自动完成的,遵循“先进后出”的原则。

以STM32的内存为例:其采用自上而下的栈结构。
在这里插入图片描述

一、不同的存储类型变量,会被存储在什么区?

#include <iostream>using namespace std;int a = 1;//全局变量
const int c = 3;//常量,它的值在程序运行期间是不可修改的。
static int d = 4;//全局的静态变量,它的作用域仅限于当前文件,其他文件无法访问到它。
int main() {int b = 2;//局部变量,它只在 main 函数中有效,离开 main 函数后就会被销毁。static int e = 5;//函数中的静态变量,它的作用域仅限于 main 函数内部,但它的生命周期会持续到程序结束cout << "&a = " << dec << (long)&a << endl;cout << "&c = " << dec << (long)&c << endl;cout << "&d = " << dec << (long)&d << endl;cout << "&b = " << dec << (long)&b << endl;cout << "&e = " << dec << (long)&e << endl;system("pause");return 0;
}

在这里插入图片描述
观察他们的地址,可以知道,全局变量、静态变量、常量地址非常靠近,说明他们被存储在同一个数据区。而局部变量则被存储在栈区。

①const修饰的局部变量放在栈区,全局变量放在只读数据区。

②static修饰的全局和局部变量都放在静态区(即数据区中的一个小区)

二、栈区

1.如果在函数A中定义了一个局部变量,那么在主函数里面是无法用取址符&去获取该局部变量的地址的。

取址符 & 用于获取变量的地址,但它只能在变量的作用域内使用。

2.return可以返回局部变量的值,但是不能返回局部变量的地址。

三、堆区:用new开辟内存空间,用delete销毁内存空间

①在C语言中,可以使用 malloc 函数来动态分配内存空间,并使用 free 函数来释放已分配的内存空间。

#include <stdio.h>
#include <stdlib.h>int main() {int* ptr = (int*)malloc(sizeof(int));  // 动态分配一个 int 类型的内存空间*ptr = 10;                             // 在分配的内存空间中存储值// 使用分配的内存空间printf("%d\n", *ptr);free(ptr);  // 释放内存空间return 0;
}

②在C++中,可以使用 new 运算符来动态分配内存空间,并使用 delete 运算符来释放已分配的内存空间。

int* ptr = new int;  // 动态分配一个 int 类型的内存空间
*ptr = 10;          // 在分配的内存空间中存储值// 使用分配的内存空间
cout << *ptr << endl;delete ptr;         // 释放内存空间

③用new创建的堆区,返回的是这个堆区的地址,所以需要用一个指针去接收。

④new运算符的用法:

int* p = new int(10);//int后面是括号,这代表创建了一个整型变量,其值为10int* arr = new int[10];  // 动态分配一个含有10个元素的整型数组// 使用分配的数组for (int i = 0; i < 10; i++) {arr[i] = i + 1;cout << arr[i] << " ";}delete[] arr;  // 释放内存空间

三、c++中的引用是干嘛的?

①引用就是给一个变量起一个别名(小名),但是这两个名字代表的是同一块内存空间。

语法为:数据类型& 别名 = 本名;

	//1.创建一个变量int a = 10;//2.创建一个变量别名,即给一个变量起一个小名//语法为:数据类型 &别名=原名;int& b = a;

好比 name &小明 = 李明;
小明就是李明的别名,二者均指代这同一个人。
所以如果我修改别名b的值,那么同样a的值也会变,因为他们的内存空间是同一块。

②那变量和其别名的地址一样吗?

答:变量和其别名的地址是相同的。因为引用是变量的别名,它们共享相同的内存地址。在使用引用时,对引用的操作实际上就是对原始变量的操作。

③变量别名有啥用呢?就光是给变量起个小名吗?

答:主要有三个作用:

1.引用可以用作变量的别名,允许使用不同的名称访问同一个变量。

	int a = 1;int& b = a;b=10;

把b赋值为10,那么a也就相应的修改为10。

2.作为函数参数:引用可以作为函数的参数,允许在函数内部直接修改传递给函数的变量的值,这样可以避免复制大型对象的开销。(就是类似地址传递)

void add1(int *p1) {//指针的地址传递,切实改变实参*p1 += 1;
}void add2(int& p2) {//引用的别名传递,同样切实的改变实参p2 += 1;
}int main() {int a = 10;add1(&a);//地址传递,传参要取地址add2(a);//引用别名传递,直接是参数名,相当于int& p2 = a;system("pause");return 0;
}
那既然地址传递和引用别名传递都能切实的改变实参,那为什么要创造引用别名这一传递方式呢?地址传递不是也可以避免复制对象的开销吗?

答:
安全性和易用性:使用引用传递时,可以避免指针操作中的空指针和野指针问题,因为引用必须绑定到一个有效的对象上。同时,使用引用传递可以使代码更加简洁和易读,因为不需要使用 * 运算符来访问对象的值。

语义上的区别:引用传递更符合函数参数传递的语义,因为它表明函数需要访问原始对象,而不是创建对象的副本。而地址传递则更适用于需要在函数内部修改指针指向的对象的情况。

3.作为函数返回类型:函数的返回值被允许引用别名来代替。这样可以避免对象的复制开销,并且允许对返回的对象进行修改。

int& getLarger(int& a, int& b) {//函数的返回值,即返回值可以被允许引用别名if (a > b) {return a;}else {return b;}
}int main() {int x = 5;int y = 10;int& larger = getLarger(x, y);//别名laeger作为函数的返回值cout << larger << endl;  // 输出10 ,因为 larger 引用的是 返回值ylarger = 15;  // 修改 larger 所引用的对象cout << larger << endl;  // 输出 15,因为 larger 被修改成15了。return 0;
}
①引用作为函数返回值时,不能返回函数中的局部变量。

啥意思?就是说:如果我的函数里面int 了一个变量a,然后return a.

int& add(){int a=5;return a;
}

那么main函数接收时:

int& value=add();cout << value<< endl;  // 第一次输出5,因为编译器保留了结果cout << value<< endl;  // 第二次是随机值,因为局部变量被销毁后//那块内存空间就不存在了,所以别名value也不知道自己是谁的小名,就混乱了。
②函数的引用可以作为左值(即在等号左边)
int& add(){static int a=5;//修改成静态变量,就不会被销毁了。return a;
}

那么main函数接收时:

int& value=add();add()=1000;//相当于a=1000;
value =1000;//也相当于a=1000;

四、引用的本质到底是什么?——指针常量

这里就涉及一个拗口的知识:
指针常量:指针是常量。指向不能变,里面的值可以变。
常量指针:常量的指针。指向可以变,里面的值是定值不能变。

引用的本质是一个指针常量,它在创建时被初始化为指向某个对象的地址,并且在其生命周期内不能被修改指向其他对象的地址

 //引用别名的本质:是一个指针常量int liming = 10;int& xiaoming = liming;//等效于: int* const xiaoming = &liming;int* const xiaoming = &liming;//定义一个指针常量,指向liming这个变量xiaoming = 15;//等效于:编译器认为就是:*xiaoming = 15;//解引用

五、函数参数传递时,用const修饰引用的参数别名。

const常量引用的用处:在函数参数中传递对象,以避免对象(在函数内部)被修改。

void print(const string& str) {str="666";//非法的,在函数内部,不能通过const修饰的参数别名来修改本身的变量scout << str << endl;
}int main() {string s = "Hello, world!";str="666";//合法的,因为字符串s本就是一个变量,可以进行修改print(s);return 0;
}

常量引用在函数参数中的应用非常常见,它可以避免对象被修改,同时也可以避免对象的复制,提高程序的效率。

总结:引用的最大用处一般是函数的传参,引用可以作为函数的参数,允许在函数内部直接修改传递给函数的变量的值,而无需通过指针或返回值来实现

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

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

相关文章

2019年AMC8数学竞赛真题的典型考点和详细解析

从战争中学习战争。 对于2024年1月19日的AMC8竞赛&#xff0c;最后一个月的时间如何备考和冲刺取得更好的成绩&#xff1f;很多高分考生的经验是刷真题&#xff0c;查漏补缺。那么如何提高刷真题的效率呢&#xff1f;使用六分成长独家制作的在线真题集练习是个不错的方式&…

基于linux系统的Tomcat+Mysql+Jdk环境搭建(四)linux安装Mysql

1.切换到你需要安装mysql的路径 cd /root/usr/ 2.在线安装 安装网上的安装方式都有很多&#xff0c;可以自己百度一下 我们这里是自己搭建测试环境&#xff0c;可以直接选择在线安装&#xff0c;命令如下&#xff1a;yum install mysql-server&#xff0c; 但是我失败了 ┭┮…

【C++11特性篇】C++11中新增的initializer_list——初始化的小利器(2)

前言 大家好吖&#xff0c;欢迎来到 YY 滴C11系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过C的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; 目录 一.探究std::initializer_list是什么…

【MySQL】SQL通用语法 、介绍SQL分类

SQL通用语法 1.SQL语句可以单行或多行书写&#xff0c;以分号结尾 2.MySQL数据库的SQL语句不区分大小写&#xff0c;关键字建议使用大写。 3.注释&#xff1a; 单行注释&#xff1a; -- 或 # 多行注释: /* */ SQL分类 SQL分类主要分为4类 分别是 DDL DML DQL DCL

谷歌浏览器标签页显示内存使用率

Chrome 桌面浏览器的新更新现在可让您查看每个标签页占用了多少内存&#xff0c;这可以帮助您确定哪些标签页占用了多少内存&#xff0c;网站正在减慢您笔记本电脑的速度。 今年早些时候在 Google Chrome 中引入内存节省程序之后&#xff0c;Google 又发布了一项功能&#xff…

论文阅读:PointCLIP V2: Prompting CLIP and GPT for Powerful3D Open-world Learning

https://arxiv.org/abs/2211.11682 0 Abstract 大规模的预训练模型在视觉和语言任务的开放世界中都表现出了良好的表现。然而&#xff0c;它们在三维点云上的传输能力仍然有限&#xff0c;仅局限于分类任务。在本文中&#xff0c;我们首先协作CLIP和GPT成为一个统一的3D开放世…

科大讯飞(深圳)测开面试真题

一面&#xff08;测试组长面&#xff09; 1、上家公司项目以及团队的规模是怎么样的&#xff1f; 2、你负责的项目整体的流程是怎么样的&#xff1f; 3、自动化实施过程中&#xff0c;是如何和业务测试进行沟通的&#xff1f; 4、在上家公司你已经是专职做自动化了&#xf…

医药行业的数据安全革新者:上海迅软DSE成功案例揭秘

随着网络化办公在医药企业中不断的深入应用&#xff0c;企业内部的药品保密配方、研发成果、技术资料等重要信息都散布在电脑或流转于网络之中&#xff0c;同时各种内部系统又集中存放着大量的敏感数据&#xff0c;一旦这些数据资产发生泄密&#xff0c;将对企业的持续运营造成…

基础IO --- 下

目录 1. 理解文件系统中inode的概念 1.1. 了解磁盘 1.1.1. 认识磁盘 1.1.2. 磁盘的物理结构 1.1.3. 简单了解磁盘如何读写数据的 1.1.4. 磁头和盘面没有物理上的接触 1.1.5. 扇区的了解 1.1.6. 如何在物理上找到一个具体的扇区 1.2. 站在OS的角度看待磁盘 1.2.1. …

OSG中几何体的绘制(一)

本章主要介绍一些几何体的绘制方法。绘制几何体在场景中是非常常见的&#xff0c;也是最基本的。在很多应用程序中可以看到相当复杂的场景&#xff0c;但不管场景有多复杂&#xff0c;它们都是由少数几个基本的图形元素构建而成的。只要想想达芬奇那些伟大的作品也是由铅笔和画…

leetcode上升的温度再复习-笛卡尔积(交叉连接)-日期加减函数

重新做上升的温度这一题的时候&#xff0c;看到之前很多的题解&#xff0c;这里结合一些题解看看有什么能学到的&#xff0c;其实这一题我现在返回来看&#xff0c;刚开始看依旧没有思路&#xff0c;还是看了题解才有想法。 理一下学习的思路:这一题就是需要通过连接解题&…

Docker部署wordpress和Jenkins

准备机器&#xff1a; 192.168.58.151 &#xff08;关闭防火墙和selinux&#xff09; 安装好docker服务 &#xff08;详细参照&#xff1a;http://t.csdnimg.cn/usG0s 中的国内源安装docker&#xff09; 部署wordpress: 创建目录&#xff1a; [rootdocker ~]# mkdi…

Unity 使用Input.GetAxis(“Horizontal/Vertical“)移动鼠标没反应的原因

在Unity中&#xff0c;当我们使用Input.GetAxis("Horizontal")和Input.GetAxis("Vertical")通过鼠标移动的返回数值以控制物体移动或其它操作时&#xff0c;却没有反应&#xff0c;令人费解。 首先我们获取返回数值并打印&#xff1a; float horizontal …

TensorFlow神经网络中间层的可视化

TensorFlow神经网络中间层的可视化 TensorFlow神经网络中间层的可视化1. 训练网络并保存为.h5文件2. 通过.h5文件导入网络3. 可视化网络中间层结果&#xff08;1&#xff09;索引取层可视化&#xff08;2&#xff09;通过名字取层可视化 TensorFlow神经网络中间层的可视化 1. …

Redis系列之简单实现watchDog自动续期机制

在分布锁的实际使用中&#xff0c;可能会遇到一种情况&#xff0c;一个业务执行时间很长&#xff0c;已经超过redis加锁的时间&#xff0c;也就是锁已经释放了&#xff0c;但是业务还没执行完成&#xff0c;这时候其它线程还是可以获取锁&#xff0c;那就没保证线程安全 项目环…

完美解决labelimg xml转可视化中文乱码问题,不用matplotlib

背景简述 我们有一批标注项目要转可视化&#xff0c;因为之前没有做过&#xff0c;然后网上随意找了一段代码测试完美&#xff08;并没有&#xff09;搞定&#xff0c;开始疯狂标注&#xff0c;当真正要转的时候傻眼了&#xff0c;因为测试的时候用的是英文标签&#xff0c;实…

基于linux系统的Tomcat+Mysql+Jdk环境搭建(三)centos7 安装Tomcat

Tomcat下载官网&#xff1a; Apache Tomcat - Which Version Do I Want? JDK下载官网&#xff1a; Java Downloads | Oracle 中国 如果不知道Tomcat的哪个版本应该对应哪个版本的JDK可以打开官网&#xff0c;点击Whitch Version 下滑&#xff0c;有低版本的&#xff0c;如…

Flutter实现Android拖动到垃圾桶删除效果-Draggable和DragTarget的详细讲解

文章目录 Draggable介绍构造函数参数说明使用示例 DragTarget 介绍构造函数参数说明使用示例 DragTarget 如何接收Draggable传递过来的数据&#xff1f; Draggable介绍 Draggable是Flutter框架中的一个小部件&#xff0c;用于支持用户通过手势拖动一个子部件。它是基于手势的一…

知识付费小程序开发:技术实践示例

随着知识付费小程序的兴起&#xff0c;让我们一起来看一个简单的示例&#xff0c;使用Node.js和Express框架搭建一个基础的知识付费小程序后端。 首先&#xff0c;确保你已经安装了Node.js和npm。接下来&#xff0c;创建一个新的项目文件夹&#xff0c;然后通过以下步骤创建你…

适用于 Windows 和 Mac 的 10 款最佳照片恢复软件(免费和付费)

丢失照片很容易。这里点击错误&#xff0c;那里贴错标签的 SD 卡&#xff0c;然后噗的一声&#xff0c;一切都消失了。值得庆幸的是&#xff0c;在技术领域&#xff0c;你可以纠正一些错误。其中包括删除数据或格式化错误的存储设备。 那么&#xff0c;让我们看看可用于从 SD …