【C++ 六】内存分区、引用

news2024/11/25 11:53:09

内存分区、引用


文章目录

  • 内存分区、引用
  • 前言
    • 1 内存分区模型
      • 1.1 程序运行前
      • 1.2 程序运行后
      • 1.3 new 操作符
    • 2 引用
      • 2.1 引用基本使用
      • 2.2 引用注意事项
      • 2.3 引用做函数参数
      • 2.4 引用做函数返回值
      • 2.5 引用本质
      • 2.6 常量引用
  • 总结


前言

本文包含内存分区、引用基本使用、引用注意事项、引用做函数参数、引用做函数返回值、引用本质、常量引用。


1 内存分区模型

C++程序在执行时,将内存大方向划分为4个区域

(1)、代码区:存放函数体的二进制代码,由操作系统进行管理的

(2)、全局区:存放全局变量和静态变量以及常量

(3)、栈区:由编译器自动分配释放,存放函数的参数值,局部变量等

(4)堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收

内存四区意义:

不同区域存放的数据,赋予不同的生命周期,给我们更大的灵活编程

1.1 程序运行前

在程序编译后,生成了exe可执行程序,未执行该程序前分为两个区域

代码区:

(1)、存放 CPU 执行的机器指令

(2)、代码区是共享的,共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码即可

(3)、代码区是只读的,使其只读的原因是防止程序意外地修改了它的指令

全局区:

(1)、全局变量和静态变量存放在此

(2)、全局区还包含了常量区,字符串常量和其他常量也存放在此

该区域的数据在程序结束后由操作系统释放

// c-const   g-global   l-local(局部)

#include <iostream>  // 包含标准输入输出流文件
using namespace std;  // 使用标准命名空间

// 全局变量
int g_a = 10;
int g_b = 10;

// 全局常量
const int c_g_a = 10;
const int c_g_b = 10;

int main() {

	// 局部变量
	int a = 10;
	int b = 10;

	// 打印地址
	cout << "局部变量a地址为: " << (int)&a << endl;  // 5961520:栈区
	cout << "局部变量b地址为: " << (int)&b << endl;  // 5961508

	cout << endl;

	cout << "全局变量g_a地址为: " << (int)&g_a << endl;  // 10272820:全局区
	cout << "全局变量g_b地址为: " << (int)&g_b << endl;  // 10272824

	cout << endl;

	// 静态变量;在普通变量前面加static,属于静态变量
	static int s_a = 10;
	static int s_b = 10;

	cout << "静态变量s_a地址为: " << (int)&s_a << endl;  // 10272828
	cout << "静态变量s_b地址为: " << (int)&s_b << endl;  // 10272832

	cout << endl;

	// 字符串常量
	cout << "字符串常量地址为: " << (int)&"hello world" << endl;  // 10263396
	cout << "字符串常量地址为: " << (int)&"hello world1" << endl;  // 10263404

	cout << endl;

	// const 修饰的常量
	// const 修饰的全局常量
	cout << "全局常量c_g_a地址为: " << (int)&c_g_a << endl;  // 10264208
	cout << "全局常量c_g_b地址为: " << (int)&c_g_b << endl;  // 10264212

	cout << endl;

	// const 修饰的局部常量
	const int c_l_a = 10;
	const int c_l_b = 10;
	cout << "局部常量c_l_a地址为: " << (int)&c_l_a << endl;  // 5961496:栈区
	cout << "局部常量c_l_b地址为: " << (int)&c_l_b << endl;  // 5961484

	cout << endl;

	system("pause");  // 相当于在本地 Windows 调试器中的:请按任意键继续...;暂停,方便看清楚输出结果

	return 0;  // 程序正常退出
}

在这里插入图片描述

在这里插入图片描述

1.2 程序运行后

栈区:

(1)、由编译器自动分配释放, 存放函数的参数值,局部变量等

(2)、注意事项:不要返回局部变量的地址,栈区开辟的数据由编译器自动释放

#include <iostream>  // 包含标准输入输出流文件
using namespace std;  // 使用标准命名空间

// 栈区数据注意事项:不要返回局部变量的地址
// 栈区的数据由编译器管理和释放

int* func(int b)  // int类型的指针,代表int类型的地址;形参数据也会放在栈区
{
	b = 100;
	int a = 10;  // 局部变量;存放在栈区,栈区的数据在函数执行完后自动释放
	return &a;   // 返回局部变量的地址
}

int main() {

	// 利用指针,接收func函数的返回值,指针指向a数据地址
	int* p = func(1);

	cout << *p << endl;  // 10;*解引用        // 第一次可以打印正确的数字,是因为编译器做了保留
	cout << *p << endl;  // 267955168(乱码)   // 第二次这个数据就不再保留了

	system("pause");  // 相当于在本地 Windows 调试器中的:请按任意键继续...;暂停,方便看清楚输出结果

	return 0;  // 程序正常退出
}

堆区:

(1)、由程序员分配释放,若程序员不释放,程序结束时由操作系统回收

(2)、在C++中主要利用new在堆区开辟内存

#include <iostream>  // 包含标准输入输出流文件
using namespace std;  // 使用标准命名空间

int* func()
{
	// 利用new关键字,可以将数据开辟到堆区
	// 指针本质也是局部变量,放在栈上,指针保存的数据是放在堆区
	int* a = new int(10);  // 返回内存编号地址;存放的也是地址;new int(10)
	return a;
}

int main() {

	int* p = func();

	cout << *p << endl;  // 10
	cout << *p << endl;  // 10

	system("pause");  // 相当于在本地 Windows 调试器中的:请按任意键继续...;暂停,方便看清楚输出结果

	return 0;  // 程序正常退出
}

1.3 new 操作符

(1)、C++ 中利用 new 操作符在堆区开辟数据

(2)、堆区开辟的数据,由程序员手动开辟,手动释放,释放利用操作符 delete

(3)、语法: new 数据类型

(4)、利用 new 创建的数据,会返回该数据对应的类型的指针

示例1: 基本语法

#include <iostream>  // 包含标准输入输出流文件
using namespace std;  // 使用标准命名空间

int* func()
{
	// 利用new关键字,可以将数据开辟到堆区
	// 指针本质也是局部变量,放在栈上,指针保存的数据是放在堆区
	int* a = new int(10);  // 返回内存编号地址;存放的也是地址;new int(10)
	return a;
}

int main() {

	int* p = func();  // 10

	cout << *p << endl;  // 10
	cout << *p << endl;  // 10

	// 堆区的数据,由程序员管理开辟,程序员管理释放
	delete p;  // 利用delete释放堆区数据

	// cout << *p << endl; //报错,释放的空间不可访问;内存已被释放,再次访问就是非法操作,会报错

	system("pause");  // 相当于在本地 Windows 调试器中的:请按任意键继续...;暂停,方便看清楚输出结果

	return 0;  // 程序正常退出
}

示例2:开辟数组

#include <iostream>  // 包含标准输入输出流文件
using namespace std;  // 使用标准命名空间

int main() {

	int* arr = new int[10];  // 10代表数组有10个元素

	for (int i = 0; i < 10; i++)
	{
		arr[i] = i + 100;  // 给10个元素赋值 100~109
	}

	for (int i = 0; i < 10; i++)
	{
		cout << arr[i] << " ";
	}

	cout << endl;

	// 释放数组 delete 后加 []
	delete[] arr;
	
	cout << endl;

	system("pause");  // 相当于在本地 Windows 调试器中的:请按任意键继续...;暂停,方便看清楚输出结果

	return 0;  // 程序正常退出
}

在这里插入图片描述

2 引用

2.1 引用基本使用

**作用: ** 给变量起别名

语法: 数据类型 &别名 = 原名

在这里插入图片描述

#include <iostream>  // 包含标准输入输出流文件
using namespace std;  // 使用标准命名空间

int main() {

	int a = 10;

	// 数据类型 &别名 = 原名
	int& b = a;

	cout << "a = " << a << endl;  // 10
	cout << "b = " << b << endl;  // 10

	b = 100;

	cout << "a = " << a << endl;  // 100
	cout << "b = " << b << endl;  // 100
	
	cout << endl;

	system("pause");  // 相当于在本地 Windows 调试器中的:请按任意键继续...;暂停,方便看清楚输出结果

	return 0;  // 程序正常退出
}

在这里插入图片描述

2.2 引用注意事项

(1)、引用必须初始化

(2)、引用在初始化后,不可以改变

在这里插入图片描述

#include <iostream>  // 包含标准输入输出流文件
using namespace std;  // 使用标准命名空间

int main() {

	int a = 10;
	int b = 20;

	// int &c;   // 错误,引用必须初始化
	int& c = a;  // 一旦初始化后,就不可以更改
	c = b;       // 这是赋值操作,不是更改引用

	cout << "a = " << a << endl;  // 20
	cout << "b = " << b << endl;  // 20
	cout << "c = " << c << endl;  // 20
	
	cout << endl;

	system("pause");  // 相当于在本地 Windows 调试器中的:请按任意键继续...;暂停,方便看清楚输出结果

	return 0;  // 程序正常退出
}

在这里插入图片描述

2.3 引用做函数参数

作用: 函数传参时,可以利用引用的技术让形参修饰实参

优点: 可以简化指针修改实参

#include <iostream>  // 包含标准输入输出流文件
using namespace std;  // 使用标准命名空间

// 1、值传递
void mySwap01(int a, int b) {
	int temp = a;
	a = b;
	b = temp;
}

// 2、地址传递
void mySwap02(int* a, int* b) {  // 传值使用&取值符,接收时用指针*
	int temp = *a;
	*a = *b;
	*b = temp;
}

// 3、引用传递
void mySwap03(int& a, int& b) {  // 引用,&a相当于实参a的别名,别名与原名一致,修改别名a就相当于修改a
	int temp = a;
	a = b;
	b = temp;
}

int main() {

	int a = 10;
	int b = 20;

	mySwap01(a, b);  // 值传递,形参不会修饰实参
	cout << "a:" << a << " \tb:" << b << endl;  // a:10 b:20

	mySwap02(&a, &b);  // 地址传递,形参会修饰实参
	cout << "a:" << a << " \tb:" << b << endl;  // a:20 b:10

	mySwap03(a, b);  // 引用传递,形参会修饰实参
	cout << "a:" << a << " \tb:" << b << endl;  // a:20 b:10 因为mySwap02()将实参修改了,在mySwap02()的基础上再进行更换
	
	cout << endl;

	system("pause");  // 相当于在本地 Windows 调试器中的:请按任意键继续...;暂停,方便看清楚输出结果

	return 0;  // 程序正常退出
}

在这里插入图片描述

2.4 引用做函数返回值

作用: 引用是可以作为函数的返回值存在的

注意: 不要返回局部变量引用

用法: 函数调用作为左值

#include <iostream>  // 包含标准输入输出流文件
using namespace std;  // 使用标准命名空间

// 返回局部变量引用
int& test01() {
	int a = 10;  // 局部变量;存放在四区中的栈区;函数调用后将被释放
	return a;
}

// 返回静态变量引用
int& test02() {
	static int a = 20;  // 静态变量,存放在全局区,全局区上的数据在程序结束后系统释放
	return a;
}

int main() {

	// 不能返回局部变量的引用
	int& ref = test01();
	cout << "ref = " << ref << endl;
	cout << "ref = " << ref << endl;

	int& ref2 = test02();
	cout << "ref2 = " << ref2 << endl;  // 20
	cout << "ref2 = " << ref2 << endl;  // 20

	// 如果函数做左值,那么必须返回引用
	// 如果函数的返回值是引用,这个函数调用可以作为左值
	test02() = 1000;  // test02()调用后返回的是a的引用;相当于返回a这个变量;test02()返回的就是a的本身;相当于a=1000

	cout << "ref2 = " << ref2 << endl;  // 1000 ; ref2是a的别名;原名赋值为1000,使用别名ref2访问也是1000
	cout << "ref2 = " << ref2 << endl;  // 1000
	
	cout << endl;

	system("pause");  // 相当于在本地 Windows 调试器中的:请按任意键继续...;暂停,方便看清楚输出结果

	return 0;  // 程序正常退出
}

在这里插入图片描述

2.5 引用本质

本质: 引用的本质在c++内部实现是一个指针常量 (int * const p)

#include <iostream>  // 包含标准输入输出流文件
using namespace std;  // 使用标准命名空间

// 发现是引用,转换为 int* const ref = &a;
void func(int& ref) {
	ref = 100;  // ref是引用,转换为*ref = 100
}

int main() {

	int a = 10;

	// 自动转换为 int* const ref = &a; 指针常量是指针指向不可改,也说明为什么引用不可更改(指向a地址后,不可再指向b地址)
	int& ref = a;
	ref = 20;  // 内部发现ref是引用,自动帮我们转换为: *ref = 20;解引用

	cout << "a:" << a << endl;      // 20
	cout << "ref:" << ref << endl;  // 20
	
	cout << endl;

	system("pause");  // 相当于在本地 Windows 调试器中的:请按任意键继续...;暂停,方便看清楚输出结果

	return 0;  // 程序正常退出
}

在这里插入图片描述

在这里插入图片描述

结论:C++推荐用引用技术,因为语法方便,引用本质是指针常量,但是所有的指针操作编译器都帮我们做了

2.6 常量引用

作用: 常量引用主要用来修饰形参,防止误操作

在函数形参列表中,可以加 const修饰形参,防止形参改变实参

// 引用使用的场景,通常用来修饰形参

#include <iostream>  // 包含标准输入输出流文件
using namespace std;  // 使用标准命名空间

// 打印数据函数
void showValue(const int& v) {  // const修饰形参;形参引用
	//v += 10;  // 加const后,v的值不可修改会提示错误
	cout << v << endl;
}

int main() {

	//int& ref = 10;  // 引用本身需要一个合法的内存空间(栈区、堆区数据),因此这行错误;10是常量,在常量区

	// 加入const就可以了,编译器优化代码,int temp = 10; const int& ref = temp;
	const int& ref = 10;

	// ref = 100;  //加入const后不可以修改变量;变为只读
	cout << ref << endl;  // 10

	// 函数中利用常量引用防止误操作修改实参
	int a = 10;  // 如果函数showValue()未加const,在函数内修改v,会将a的值也进行修改
	showValue(a);  // 10
	
	cout << endl;

	system("pause");  // 相当于在本地 Windows 调试器中的:请按任意键继续...;暂停,方便看清楚输出结果

	return 0;  // 程序正常退出
}

在这里插入图片描述


总结

(1)、C++ 中在程序运行前分为全局区和代码区;

(2)、代码区特点是共享和只读;

(3)、全局区中存放全局变量、静态变量、常量;

(4)、常量区中存放 const 修饰的全局常量和字符串常量;

(5)、堆区数据由程序员管理开辟和释放;

(6)、堆区数据利用new关键字进行开辟内存;

(7)、通过引用参数产生的效果同按地址传递是一样的。引用的语法更清楚简单。

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

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

相关文章

记一次完整的rc.local中启动python脚本报psutil找不到问题解决

文章目录 1&#xff0c;问题1.1&#xff0c;rc.local1.2&#xff0c;watchdog.py 2&#xff0c;问题排查2.1&#xff0c;手动执行start.sh后功能正常2.2&#xff0c;开机启动后rc.local加载start.sh&#xff0c;然后start.sh启动python脚本报错2.3&#xff0c;怀疑是rc.local加…

SAP S/4HANA不是ERP了?

今天浏览了一下SAP官方帮助&#xff08;Help&#xff09;网站&#xff0c;有一个意外的发现&#xff0c;如上图&#xff1a;SAP S/4HANA和SAP ERP是分别显示的&#xff0c;这让我想起了前段时间一个朋友和我说&#xff1a;“S/4HANA现在都不叫ERP了&#xff0c;因为里面包括了超…

【C++初阶】C++入门(一):命名空间C++的输入输出缺省参数函数重载

​ ​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;数据结构 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 文章目录 1.什么是C1.1 C的发…

最新Tuxera NTFS2023最新版Mac读写NTFS磁盘工具 更新详情介绍

Tuxera NTFS for Mac是一款Mac系统NTFS磁盘读写软件。在系统默认状态下&#xff0c;MacOSX只能实现对NTFS的读取功能&#xff0c;Tuxera NTFS可以帮助MacOS 系统的电脑顺利实现对NTFS分区的读/写功能。Tuxera NTFS 2023完美兼容最新版本的MacOS 11 Big Sur&#xff0c;在M1芯片…

Python统计学:如何理解样本统计量?

本期介绍样本统计量是怎么算的&#xff0c;并用Python来模拟随机抽样。用一个在鱼塘捞鱼的简单例子来理解样本均值的概念。 如何理解重复试验&#xff1f; 指能够在完全相同条件下进行多次的试验&#xff1b; 比如我们抛10枚硬币&#xff0c;用来计算正面出现的概率&#xff…

4.7 贝塞尔曲线

学习目标&#xff1a; 学习贝塞尔曲线可以遵循以下步骤&#xff1a; 1.了解基本概念和定义&#xff1a;学习贝塞尔曲线前需要了解贝塞尔曲线的基本概念和定义&#xff0c;如何定义一条贝塞尔曲线、控制点的概念以及贝塞尔曲线的几何性质等。 2.学习贝塞尔曲线的构造方法&…

Django搭建一个简易GPT网站

文章目录 环境安装创建主项目和应用程序在 settings.py 文件中注册应用程序在 views.py 文件中为应用程序创建视图配置应用程序的 URL创建和渲染模板KEY实现发送提示功能注意事项完整源码 环境安装 pip install django openai创建主项目和应用程序 处理完项目的环境后&#x…

第二个机器学习应用:乳腺癌数据集在决策树模型上的挖掘

目录 决策树优化与可视化 1 决策树分类 2 决策树可视化 3 显示树的特征重要性 特征重要性可视化 决策树回归 1 决策树回归 决策树优化与可视化 1 决策树分类 from sklearn.datasets import load_breast_cancer from sklearn.tree import DecisionTreeClassifier from sk…

基于C++开发的医院医学影像PACS 可二次开发,三维重建

医学影像PACS系统源码&#xff0c;集成三维影像后处理功能&#xff0c;包括三维多平面重建、三维容积重建、三维表面重建、三维虚拟内窥镜、最大/小密度投影、心脏动脉钙化分析等功能。系统功能强大&#xff0c;代码完整。有演示。 本套PACS系统专门针对医院工作流程设计的&am…

分布式ID生成策略总结

1、UUID 2、数据库自增ID 2.1、主键表 2.2、ID自增步长设置 3、号段模式 4、Redis INCR 5、雪花算法 6、美团(Leaf) 7、百度(Uidgenerator) 8、滴滴(TinyID) 总结比较 背景 在复杂的分布式系统中&#xff0c;往往需要对大量的数据进行唯一标识&#xff0c;比如在对…

springboot中的日志

作者&#xff1a;~小明学编程 文章专栏&#xff1a;spring框架 格言&#xff1a;热爱编程的&#xff0c;终将被编程所厚爱。 目录 为什么需要日志 如何使用日志功能 日志的打印 获取日志对象 使用日志对象打印日志 日志级别 为什么我们需要把日志分为如此多的种类呢&am…

今天面试招了个25K的测试员,从腾讯出来的果然都有两把刷子···

公司前段时间缺人&#xff0c;也面了不少测试&#xff0c;前面一开始瞄准的就是中级的水准&#xff0c;也没指望来大牛&#xff0c;提供的薪资在15-25k&#xff0c;面试的人很多&#xff0c;但平均水平很让人失望。看简历很多都是4年工作经验&#xff0c;但面试中&#xff0c;不…

《系统架构设计》-07-面向领域的技术设计

文章目录 1 实体与值对象1.1 实体对象1.1.1 唯一标识&#xff08;Identity&#xff09;1.1.2 可变性贫血模型充血模型 1.2 值对象1.3 示例&#xff08;识别实体和值对象&#xff09;1&#xff09;识别实体对象2&#xff09;提取值对象3&#xff09;挖掘实体的关键行为4&#xf…

solidworks2022 - feature works 变灰的解决方法

文章目录 solidworks2022 - feature works 变灰的解决方法概述实验feature works 变灰问题的重现备注END solidworks2022 - feature works 变灰的解决方法 概述 feature works 用于step文件转零件. 一般是不同版本的solidworks交换文件的方法. 今天突然发现, 我自己转出的ste…

Spring框架使用总结

Spring框架使用 前言处理事务管理声明式事务&#xff1a;编程式事务&#xff1a; 框架核心常见注解 AOP&#xff08; 面向切面编程&#xff09;切面和通知有哪些类型&#xff1f;切面的类型通知类型AOP实现使用场景 IOC(管理所有的JavaBean)依赖注入&#xff08;DI&#xff09;…

像素比特行列置乱加密算法安全性分析

比特行列置乱加密 将MN大小的灰度图像每个像素值转换为8bit二进制&#xff0c;得到M8N大小的二值图像。 基于加密秘钥&#xff0c;生成随机序列TM和TN分别对二进制图像的行列进行置乱&#xff0c;生成置乱加密后的图像。 Logistic混沌序列加密&#xff1a; 选择明文攻击过程 …

Node内置模块 【path模块】

文章目录 &#x1f31f;前言&#x1f31f;path模块&#x1f31f;引用模块&#x1f31f;常用属性&#x1f31f;path.sep&#x1f31f;在MacOSX、 Unix、Linux操作系统上&#xff1a;&#x1f31f;在 Windows 上&#xff1a; &#x1f31f;常用方法&#x1f31f;将路径转换为对象…

【python视图1】networkx操作Graph图

一、说明 数据可视化需要显示种种数据&#xff0c;matplotlib负责曲线类画图&#xff0c;然而类似于图论的操作用什么方法。这里用networkx程序包完成。本文专门介绍这种程序包的用法。 二、生成图&#xff08;Creating a graph&#xff09; 2.1 创建一个没有节点和边的空图。…

Linux:centos 7:查看运行级别 控制init运行级别 已安装图形化以后设置开机进入图形化或命令行

0 target 关机状态&#xff0c;使用该级别时将关闭主机 1 rescue.target 单用户模式&#xff0c;不需要密码验证即可登录系统&#xff0c;多用于系统维护 …

HTTP 的工作原理

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、简单 HTTP二、HTTP 连接请求 I请求Ⅱ 持久 HTTP并执行 HTTP默认浏览器连接设置总结 前言 在处理 Web 性能监控或优化时&#xff0c;了解 HTTP 协议的基础知…