你 还 不知道什么是“柔性数组”吗?详细深入讲解以及扩展,快进来学习以便所需之时使用

在这里插入图片描述
giao哥都已经翻开书了,你还有什么理由不学习?

1.柔性数组

1.1.柔性数组的特点

1.2.柔性数组的定义和使用

2.扩展

2.1.结构体创建指针直接指向

2.2.两种方法的对比

2.3.柔性数组的好处

3.推荐阅读

4.完结撒❀

–❀—❀—❀—❀—❀—❀—❀—❀—❀—❀—❀—❀—❀—❀—❀—❀—❀–

1.柔性数组

也许你从来没有听过柔性数组的概念,但它的确存在,在C99中结构体最后一个元素允许是位置大小的数组,这就叫做柔性数组成员,比如:

struct S
{
	int a;
	char b;
	int c[];//柔性数组成员
};

这里强调一下,柔性数组的定义格式为type arr[]或者type arr[0],只能这两种进行定义

1.1.柔性数组的特点

大家先阅读一下柔性数组的特点,方便下面进行讲解

1.结构体中的柔性数组成员前面必须至少有一个其他成员
2.sizeof返回的这种结构大小不包括柔性数组的内存
3.包含柔性数组成员的结构用malloc()函数进行内存的动态分配,并且分配的内存应该大于结构体的大小,以适应柔性数组的预期大小。

上面的特点1和3其实讲的就是柔性数组的定义和使用,对于sizeof返回结构体的大小是不包含柔性数组的大小的,我们见下图:
在这里插入图片描述计算刚才定义的结构体S的大小我们可以看出来,通过结构体的对其规则(结构体大小计算的规则,不会的同学可以学习我之前所写的博客:结构体大小计算),算出来结构为8bit表示确实是没有包含计算柔性数组int c的大小的。

1.2.柔性数组的定义和使用

通过上面的学习我们应该都知道了柔性数组是如何定义的,下面我们通过一串代码,来讲解柔性数组的使用。

struct S
{
	int a;
	char b;
	int arr[];//柔性数组成员
};

int main()
{
	struct S* ps = (struct S*)malloc(sizeof(struct S) + 10 * sizeof(int));//这里对于柔性数组开辟了40个字节进行使用
	//检验是否开辟成功
	if (ps == NULL)
	{
		perror("malloc:");
		return 1;
	}
	ps->a = 100;
	ps->b = 'r';
	//使用柔性数组
	for (int i = 0; i < 10; i++)
	{
		ps->arr[i] = i;
	}

	//假如40字节不够,我们可以使用realloc继续进行开辟
	struct S* ptr = (struct S*)realloc(ps, sizeof(struct S) + 15 * sizeof(int));
	if (ptr != NULL)
	{
		ps = ptr;
	}
	else
	{
		perror("realloc:");
		return 1;
	}

	//继续使用
	for (int i = 10; i < 15; i++)
	{
		ps->arr[i] = i;
	}

	//打印
	printf("%d %c", ps->a, ps->b);
	printf("\n");
	for (int i = 0; i < 15; i++)
	{
		printf("%d ", ps->arr[i]);
	}

	//销毁
	free(ps);
	ps = NULL;
	return 0;
}

按照上面代码,我们创建了结构体S,在结构体S中使用了柔性数组,使用malloc函数开辟出整个结构体的内存空间再加上所需要的整型数组arr的内存空间,开辟后malloc将所开辟空间的起始位置的指针传给ps,然后对指针ps指向结构体内存的空间地址进行使用,之后如果发现所创建的柔性数组空间不能满足需求,就可以使用realloc函数进行择增容,增容后便可以对新开辟的空间进行使用,最后记得将开辟的动态内存空间进行销毁。
上面代码执行结果是:
在这里插入图片描述
所以我们在要使用数组但又不确定需要开辟多大的内存的时候,我们就可以使用柔性数组,随时开辟动态空间大小,满足需求。

2.扩展

我们学习了柔性数组,柔性数组的作用就是对于数组可以开辟任意满足需求的内存空间大小,但是我们通过下面代码的方法,也是可以实现与柔性数组同样的功能。

2.1.结构体创建指针直接指向

struct S
{
	int i;
	char c;
	int* arr;
};

int main()
{
	struct S* ps = (struct S*)malloc(sizeof(struct S));
	if (ps == NULL)
	{
		perror("malloc1:");
		return 1;
	}
	ps->i = 100;
	ps->c = 'w';
	ps->arr = (int*)malloc(10 * sizeof(int));
	if (ps->arr == NULL)
	{
		perror("malloc2:");
			return 1;
	}
	//使用
	for (int i = 0; i < 10; i++)
	{
		ps->arr[i] = i;
	}
	//假如不够用,开辟
	int* ptr = (int*)realloc(ps->arr, 15 * sizeof(int));
	if (ptr != NULL)
	{
		ps->arr = ptr;
	}
	else
	{
		perror("realloc:");
	}
	//使用
	for (int i = 10; i < 15; i++)
	{
		ps->arr[i] = i;
	}
	//打印
	printf("%d %c\n", ps->i, ps->c);
	for (int i = 0; i < 15; i++)
	{
		printf("%d ", ps->arr[i]);
	}
	//销毁
	//这里要按照顺序先销毁arr指向的动态内存空间,最后在销毁所开辟的结构体空间
	free(ps->arr);
	ps->arr = NULL;
	free(ps);
	ps = NULL;
	return 0;
}

可以看到,我们并没用使用柔性数组的方法,但也是实现了与柔性数组同样的功能。
这里我们创建结构体S的时候是将柔性数组换成了一个整形指针arr,而在下面的动态内存开辟中我们将整形指针arr指向了malloc函数所开辟的40字节的动态内存中,接着通过对整形指针arr找到所创建的40个动态内存中,并解引用对所开辟的空间进行使用,不够的话就可以使用realloc函数对空间进行再次开辟增容,以满足所用需求。在最后可能有人会不理解为什么先对整形指针arr所创建的空间进行销毁,我从内存先后开辟的顺序给大家进行分析一下,因为先开辟的一块空间是为了存放整个结构体的,然后再进行开辟40字节的动态内存空间并将起始地址存到了整形指针arr当中,所以在下面释放中也应该是先释放arr指向的动态内存空间,然后才再释放存放结构体的动态内存空间。
打印结果如下:
在这里插入图片描述

这里也有可能会有人问:这样也可以实现柔性数组的功能,那么要柔性数组又有什么用呢?
之所以有柔性数组的概念,那么肯定是要优与上面直接使用指针arr指向的形式的,下面我们就来讲讲两者的区别和柔性数组的优点。

2.2.两种方法的对比

对于柔性数组和结构体创建指针直接指向这两种方法的区别,其核心就是在内存中的存储不一样
根据上面实现的代码,柔性数组的空间开辟形式如下图:
在这里插入图片描述可以看到柔性数组在内存中是连续存放的,因为我们只用了一个malloc函数来进行动态内存的开辟,下面对空间增容是用realloc函数来进行的,而realloc函数是在原有的空间上进行增容,所开辟的动态内存空间并不会断开。
结构体创建指针直接指向的方法在内存中的开辟如下图所示:
在这里插入图片描述因为使用了两次malloc函数,在创建空间的时候并不是连续进行创建的所以这个方法创建的内存空间是不连续的。
由于创建空间的连续和不连续造成了这两种方法的优略。对于结构体创建指针直接指向来说,因为它在创建空间的时候在内存存储之间会有内存碎片,而这些内存碎片又用不上,我们如果产生过多的内存碎片如下图:
在这里插入图片描述那么就会造成空间浪费,空间利用率下降,而结构体创建指针直接指向可能会使用两次或者多次的malloc函数,就会产生越来越多的内存碎片。
并且在最后的销毁中,结构体创建指针直接指向是需要有两次销毁,使用起来也没有柔性数组方便。

2.3.柔性数组的好处

其实我们在上面都已经讲过了,这里是对其的一个总结:
1.方便内存的释放
如果我们的代码是在一个给别人用的函数中,你在里面做了二次内存分配,并把结构体返回给用户。用户调用free可以释放结构体,但是用户并不知道这个结构体内的成员也需要free,所以你不能指望用户来发现这个事情。所以我们把结构体要的内存以及其成员要的内存一次性分配好了,并返回给用户一个结构体指针,用户做一次free就可以把所有的内存都给释放掉。
2.有利于访问速度
连续的内存有利于提高访问的速度,也有利于减少内存碎片。

3.推荐阅读

我们在进行了这些讲解之后我推荐大家看一片文章:
C语言结构里的数组和指针
这篇文章出自酷壳的一位大佬写的博客,认真阅读之后我想你一定会有所收获的。
并且在酷壳网上面有许多高质量的博客,也有很多业内大佬在,大家没事都可以阅读学习。

4.完结撒❀

如果以上内容对你有帮助不妨点赞支持一下,以后还会分享更多编程知识,我们一起进步。
最后我想讲的是,据说点赞的都能找到漂亮女朋友
在这里插入图片描述

  • 41
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值