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

【柔性数组与局部性原理】

柔性数组概念

  • 柔性数组特点

  • 局部性原理

柔性数组概念

对于柔性数组,也许你之前从未听说过,柔性数组,顾名思义,就是数组,它的柔性柔在元素个数可以动态变化,
即柔性数组是未知大小的,数组大小可以动态变化的。
在结构体中,最后一个元素允许是未知大小的数组,这个数组就叫柔性数组成员。

下面直接举例子来说明柔性数组。

struct S
{
	int n;
	int arr[]; 
};

这就是一个柔性数组,柔性数组是在结构体中是未知大小的。

struct S
{
	int n;
	int arr[0]; 
};

上面这种写法也可以。

在这里插入图片描述
当我们进行编译时,编译通过。

柔性数组特点:

下面举例说明柔性数组的特点:

假设需要动态开辟一块内存空间,我们可以这样操作:

typedef struct S
{
	int n;
	int arr[];
};

当我们计算这个柔性数组的大小时,结果如下:
在这里插入图片描述
看到结果是4,可能你会疑惑,别着急,这就是柔性数组的第一个特点:

特点1:sizeof计算结构体大小时,不包括柔性数组的大小

所以计算上面的结构体的大小时,仅计算n大小,在进行结构体内存对齐后,结果是4 。

特点2:柔性数组成员之前必须至少有一个其他成员。

特点3:包含柔性数组成员的结构用malloc ()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。

什么意思呢?举个例子:

typedef struct S
{
	int n;
	int arr[0]; 
}S;

int main()
{
	struct S s = { 0 };
	struct S* ps = (S*)malloc(sizeof(S) + 5 * sizeof(int));

	return 0;
}

看上面的代码:当我们动态申请一块内存时,申请的空间需要大于结构体本身的大小。
在后面增加适当的空间以适应柔性数组的大小。

下面举实际例子来说明柔性数组的一些好处:

typedef struct S
{
	int n;
	int arr[0]; 
}S;

int main()
{
	struct S s = { 0 };
	struct S* ps = (S*)malloc(sizeof(S) + 5 * sizeof(int));
	if (ps == NULL)
	{
		printf("%s\n", strerror(errno));
		return;
	}
	ps->n = 100;
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		ps->arr[i] = i;
	}

}

在内存中申请一块空间,这块空间在内存中是连续的。

在这里插入图片描述
然后将n 和arr分别赋值
在这里插入图片描述
如果想使用的空间不够,那就重新申请空间

struct S*ptr=realloc(ps, 44);

44 = 24(原结构体大小) + 20(5*int)

也就是再开辟五个int类型的空间

int main()
{
	struct S s = { 0 };
	struct S* ps = (struct S*)malloc(sizeof(struct S) + 5 * sizeof(int));
	if (ps == NULL)
	{
		printf("%s\n", strerror(errno));
		return;
	}
	ps->n = 100;
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		ps->arr[i] = i;
	}

	struct S*ptr=realloc(ps, 44);

	if (ptr != NULL)
	{
		ps = ptr;
	}

	for (i = 5; i < 10; i++)
	{
		ps->arr[i] = i;
	}
	for (i = 0; i < 10; i++)
	{
		printf("%d ", ps->arr[i]);
	}
	//释放
	free(ps);
	ps = NULL;
	return 0;
}

然后进行赋值,再打印出来,整个完整代码如上:
结果如下:

在这里插入图片描述

使用柔性数组动态开辟内存,这块空间是连续的。

下面来看另一种开辟内存的方法:

struct S
{
	int n;
	int* arr;
};

int main()
{
	struct S* ps = (struct S*)malloc(sizeof(struct S));
	ps->arr = malloc(sizeof(int) * 5);
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		ps->arr[i] = i;
	}

	//加大内存,调整大小
	int *ptr=  realloc(ps->arr, 10 * sizeof(int));
	if (ptr != NULL)
	{
		ps->arr = ptr;
	}
	for (i = 5; i < 10; i++)
	{
		ps->arr[i] = i;
	}

	for (i = 0; i < 10; i++)
	{
		printf("%d ", ps->arr[i]);
	}
	free(ps->arr);//free要有先后顺序
	free(ps);
	ps = NULL;
	return 0;
}

看不懂代码?请看下图:

在这里插入图片描述
首先申请了第一块空间结构体指针ps,该指针指向了上面的S结构体,第二次申请的空间是 ps所指向的结构体成员指针arr,为这个arr成员申请了一块空间,然后增加空间。

在两次申请的过程中,两块空间ps 和 arr所指向的空间 都是在堆区上,结构体是在栈区上,所以释放的时候,只释放申请出来的两块空间,不需要释放结构体空间。

对比两段代码,可以看出不同点:

在第一段代码中,申请的空间是连续的,第二段代码中,申请的空间是不连续的。

第一段代码申请的空间只需free一次,第二段代码申请的空间需要free两次,并且free的先后顺序有要求,需要先释放arr指向的空间,再释放ps。如果释放顺序相反,会导致arr指向的空间就失踪了, 也就是没人能知道arr指向的空间在哪。

所以,在这里使用柔性数组的好处有:

1.方便释放空间。

2.利于内存访问

第二点如何理解?

下面引出局部性原理的概念:

局部性原理

局部性原理是指CPU访问存储器时,无论是存取指令还是存取数据,所访问的存储单元都趋于聚集在一个较小的连续区域中。

意思是:当我们访问一块空间时,接下来有 80%的可能性会访问它周边的数据。

来看下面:

在这里插入图片描述

一个存储器层次结构图如上:

直接画出主要的布局:
在这里插入图片描述
cpu在访问空间时,首先会在寄存器中读取数据,如果寄存器中没有那块需要读取的空间,就会去高速缓存中寻找,如果高速缓存中也没有这块内存区域,它就会去硬盘中寻找。

这样层层往下寻找。
如上图:
假如需要读取的空间是连续的,那么这块空间放在寄存器中时,cpu先读取一小部分空间,根据局部性原理:cpu有80%的几率读取周围的空间,这样就能够提高访问效率。
这就是使用柔性数组的好处

假如读取的空间是不连续的,而是碎片式的访问。这样读取效率就会降低。
所以,柔性数组的好处在于:
1.方便释放空间。

2.利于内存访问

相关文章:

  • SCI论文解读复现【NO.3】MSFT-YOLO:基于变压器的改进YOLOv5钢表面缺陷检测(代码已复现)
  • 2022年度总结|我的CSDN成长历程
  • CSS基础总结(五)定位
  • 一文学会基础的TypeScript
  • js函数之call和apply
  • stm32平衡小车(1)---蓝牙模块及其bug处理
  • [JavaEE]线程的状态与安全
  • 【Qt】事件处理——按键事件处理
  • opencv-python常用函数解析及参数介绍(八)——轮廓与轮廓特征
  • flutter项目编译问题汇总
  • C++关联容器(复习题篇)
  • 02SpringCloudAlibaba服务注册中心—Eureka
  • opencv-python常用函数解析及参数介绍(七)——边缘检测
  • 14---实现文件上传和下载(头像上传功能)
  • Vue2学习笔记(四):计算属性(computed)和监事属性(watch)
  • “大数据应用场景”之隔壁老王(连载四)
  • android高仿小视频、应用锁、3种存储库、QQ小红点动画、仿支付宝图表等源码...
  • electron原来这么简单----打包你的react、VUE桌面应用程序
  • gf框架之分页模块(五) - 自定义分页
  • Median of Two Sorted Arrays
  • Storybook 5.0正式发布:有史以来变化最大的版本\n
  • UEditor初始化失败(实例已存在,但视图未渲染出来,单页化)
  • -- 查询加强-- 使用如何where子句进行筛选,% _ like的使用
  • 大主子表关联的性能优化方法
  • 对象引论
  • 解决iview多表头动态更改列元素发生的错误
  • 如何正确配置 Ubuntu 14.04 服务器?
  • 实战|智能家居行业移动应用性能分析
  • 使用 QuickBI 搭建酷炫可视化分析
  • #单片机(TB6600驱动42步进电机)
  • #我与Java虚拟机的故事#连载03:面试过的百度,滴滴,快手都问了这些问题
  • (14)Hive调优——合并小文件
  • (27)4.8 习题课
  • (4)事件处理——(6)给.ready()回调函数传递一个参数(Passing an argument to the .ready() callback)...
  • (Java岗)秋招打卡!一本学历拿下美团、阿里、快手、米哈游offer
  • (动手学习深度学习)第13章 计算机视觉---图像增广与微调
  • (附源码)ssm航空客运订票系统 毕业设计 141612
  • (附源码)计算机毕业设计SSM疫情下的学生出入管理系统
  • (三)Hyperledger Fabric 1.1安装部署-chaincode测试
  • (一)appium-desktop定位元素原理
  • (一)ClickHouse 中的 `MaterializedMySQL` 数据库引擎的使用方法、设置、特性和限制。
  • (译)2019年前端性能优化清单 — 下篇
  • (转)视频码率,帧率和分辨率的联系与区别
  • (转)真正的中国天气api接口xml,json(求加精) ...
  • . NET自动找可写目录
  • .libPaths()设置包加载目录
  • .NET 8 中引入新的 IHostedLifecycleService 接口 实现定时任务
  • .Net Attribute详解(上)-Attribute本质以及一个简单示例
  • .net core webapi 部署iis_一键部署VS插件:让.NET开发者更幸福
  • .NET CORE使用Redis分布式锁续命(续期)问题
  • .NET Core中的去虚
  • .NET 解决重复提交问题
  • .NET国产化改造探索(一)、VMware安装银河麒麟
  • .NET面试题(二)
  • .NET学习全景图