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

news2024/11/15 21:41:17

文章目录

  • 前言
  • 一、不同的存储类型变量,会被存储在什么区?
    • ①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后面是括号,这代表创建了一个整型变量,其值为10

	int* 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 引用的是 返回值y
    larger = 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修饰的参数别名来修改本身的变量s
    cout << str << endl;
}

int main() {
    string s = "Hello, world!";
    str="666";//合法的,因为字符串s本就是一个变量,可以进行修改
    print(s);
    return 0;
}

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

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

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

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

相关文章

漏洞复现-网神SecGate3600防火墙敏感信息泄露漏洞(附漏洞检测脚本)

免责声明 文章中涉及的漏洞均已修复&#xff0c;敏感信息均已做打码处理&#xff0c;文章仅做经验分享用途&#xff0c;切勿当真&#xff0c;未授权的攻击属于非法行为&#xff01;文章中敏感信息均已做多层打马处理。传播、利用本文章所提供的信息而造成的任何直接或者间接的…

Axure之动态面板轮播图

目录 一.介绍 二.好处 三.动态面板轮播图 四.动态面板多方式登录 五.ERP登录 六.ERP的左侧菜单栏 七.ERP的公告栏 今天就到这了哦&#xff01;&#xff01;&#xff01;希望能帮到你了哦&#xff01;&#xff01;&#xff01; 一.介绍 Axure中的动态面板是一个非常有用的组…

Mr. Cappuccino的第65杯咖啡——MacOS安装Docker

MacOS安装Docker 下载Docker安装Docker查看Docker相关信息镜像加速 下载Docker Docker官网 Docker文档中心 Docker桌面版下载地址 安装Docker 查看Docker相关信息 docker --versiondocker info镜像加速 阿里云镜像加速器 "registry-mirrors": ["https://gq8…

Flink-水位线和时间语义

Flink中的时间含义 在实际应用中&#xff0c;事件时间语义会更为常见。一般情况下&#xff0c;业务日志数据中都会记录数据生成的时间戳&#xff08;timestamp&#xff09;&#xff0c;它就可以作为事件时间的判断基础。 在Flink中&#xff0c;由于处理时间比较简单&#xff0c…

4G无线工业级路由器在智能制造设备互联互通中的角色

随着工业技术的不断发展和进步&#xff0c;智能制造已经成为了现代制造业的重要趋势和发展方向。而在智能制造过程中&#xff0c;设备之间的互联互通是至关重要的一环。在这个过程中&#xff0c;4G无线工业级路由器扮演着重要的角色&#xff0c;它提供了稳定可靠的网络连接&…

Linux——进程中被打开的文件

C语言中有着许多对文件操作的函数&#xff0c;包括其他语言也有&#xff0c;但是从语言来了解文件有点浅显计算机的一切都离不开操作系统&#xff0c;那么文件跟操作系统也有着密切的关系&#xff0c;所以我们从系统层面来了解文件&#xff08;在进程中打开的文件&#xff09;文…

轻量封装WebGPU渲染系统示例<49>- 多种灯光多材质(源码)

实现方式: 1. 全局的灯光和阴影。 2. 球体和矩形平面使用了相同的材质对象。 3. 通过材质自动关联和组装对应的渲染材质功能节点。 4. 共享uniform或storage的buffer对象。 5. 共享shader module对象。 当前示例源码github地址: https://github.com/vilyLei/voxwebgpu/b…

优先考虑类型安全的异构容器

在Java中&#xff0c;异构容器是一种可以存储不同类型元素的容器。为了提高类型安全性&#xff0c;可以使用泛型和类型安全的异构容器&#xff0c;而不是传统的非类型安全容器。下面是一个例子&#xff0c;演示如何使用类型安全的异构容器 import java.util.HashMap; import j…

关于MySQL的bigint问题

MySQL的bigint(8)能存多大数值&#xff1f; MySQL的BIGINT(8)可以存储的数值范围是从-9,223,372,036,854,775,808到9,223,372,036,854,775,807。这是因为BIGINT数据类型在MySQL中使用8字节进行存储&#xff0c;每个字节有8位&#xff0c;所以总共可以表示2^64个不同的整数。 …

INFINI Labs 产品更新 | Easysearch 新增快照搜索功能,Console 支持 OpenSearch 存储

INFINI Labs 产品又更新啦~&#xff0c;包括 Easysearch v1.7.0、Console v1.13.0。本次各产品更新了 Easysearch 快照搜索功能&#xff1b;Console 支持 OpenSearch 集群存储系统数据、优化了初始化安装向导流程等。 以下是本次更新的详细说明。 INFINI Easysearch v1.7.0 …

Linux---查看文件内容命令

1. 查看文件内容命令的使用 命令说明cat查看小型文件more分屏查看大型文件 cat命令的效果图 说明: cat命令结合重定向可以完成多个文件的合并gedit 文件编辑命令&#xff0c;可以查看和编辑文件 more命令的效果图 当查看内容信息过长无法在一屏上显示时&#xff0c;可以使…

Unity中实现ShaderToy卡通火(移植篇)

文章目录 前言一、准备好我们的后处理基础脚本1、C#&#xff1a;2、Shader&#xff1a; 二、开始逐语句对ShaderToy进行转化1、首先&#xff0c;找到我们的主函数 mainImage2、其余的方法全部都是在 mainImage 函数中调用的方法3、替换后的代码(已经没报错了&#xff0c;但是效…

Centos7 配置Git

随笔记录 目录 1&#xff0c; 新建用户 2. 给用户设置密码相关操作 3. 为新用户添加sudo 权限 4. 配置Git 4.1 配置Git 4.2 查看id_ras.pub 5, 登录Git 配置SSH 秘钥 6. Centos7 登录Git 7. clone 指定branch到本地 8. 将新代码复制到指定路径 9. 上传指定代码 …

基于ssm电影网站源码和论文

随着Internet的发展&#xff0c;人们的日常生活已经离不开网络。未来人们的生活与工作将变得越来越数字化&#xff0c;网络化和电子化。它将是直接管理电影网站的最新形式。本论文是以构建电影网站为目标&#xff0c;使用 java技术制作&#xff0c;由管理员和用户两大部分组成。…

GEM5 McPAT NoC教程: xml设置汇总-2023版

简介 McPAT的xml有一些参数需要设置&#xff0c;noc的部分很多Gem5ToMcpatparser没有设置&#xff0c;也没有给出如何设置的条件。尤其是和活动相关的total access&#xff0c;不知道具体怎么设置&#xff0c;也不知道如何从gem5 stats.txt中导出。本文提供了2023年的收集到ge…

【强化学习-读书笔记】表格型问题的 Model-Free 方法

参考 Reinforcement Learning, Second Edition An Introduction By Richard S. Sutton and Andrew G. Barto无模型方法 在前面的文章中&#xff0c;我们介绍的是有模型方法&#xff08;Model-Based&#xff09;。在强化学习中&#xff0c;"Model"可以理解为算法…

【CSS】前端点点点加载小点样式css动画过程实现

对话的 ... 加载动画&#xff0c;直接用 CSS 就可以实现&#xff0c;样式可以自己改&#xff0c;逻辑大差不差 <div class"loading-text"><span class"dot1"></span><span class"dot2"></span><span class&quo…

动态面板简介以及ERP原型图案列

动态面板简介以及ERP原型图案列 1.Axure动态面板简介2.使用Axure制作ERP登录界面3.使用Asure完成左侧菜单栏4.使用Axuer完成公告栏5.使用Axuer完成左边侧边栏 1.Axure动态面板简介 在Axure RP中&#xff0c;动态面板是一种强大的交互设计工具&#xff0c;它允许你创建可交互的…

pgAdmin 4的安装与使用

一、pgAdmin 4安装 ①访问pgAdmin官网&#xff0c;下载路径如下&#xff1a; pgAdmin下载 ②选择个人电脑所对应的系统&#xff0c;我的电脑是Windows10系统&#xff0c;所以选择Windows ③选择所需要的pgAdmin版本&#xff0c;我选择的是最新的v7.3版本 ④下载完成后双击pg…

Bootstrap在弹框Povoper中显示图片

项目开发需要实现这个效果&#xff0c;当鼠标划过这个按钮的时候&#xff0c;会显示出指定的图片出来 HTML代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"…