【C语言】柔性数组

​​​​​​​文章目录

  1. ​​​​​​​柔性数组概念
  2. 使用柔性数组时的注意事项
  3. 柔性数组的使用
  4. 柔性数组的优势
  5. 结语

1.柔性数组概念

 柔性数组是C99中引入的新特性

C99 中,结构中的最后一个元素允许是未知大小的数组,这就叫做『柔性数组』成员

 柔性数组成员的大小可以在程序运行中跟据所需要的大小进行改变

定义包含柔性数组成员的结构体:

 例如:

struct S 
{
	int a;
	char str[0];//指名数组大小为0,其实就是未指名数组元素大小,柔性数组成员
};

 有些编译器会报错无法编译可以改成:

struct S 
{
	int a;
	char str[];//未指名数组元素大小,柔性数组成员
};

2.使用柔性数组时的注意事项

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

例如:

#include 
typedef struct S 
{
	int a;
	char str[];
} S;
int main()
{
	printf("%zd\n", sizeof(S));
	return 0;
}

程序运行结果为:
【C语言】柔性数组_第1张图片
输出结果为4,不难看出,sizeof计算这种结构体的大小不包含柔性数组成员的内存


3.柔性数组的使用

例如:

#include 
#include

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

int main()
{
	int i = 0;
	//申请内存用于存储我们定义的结构体,用一个结构体指针进行维护,为柔性数组成员申请了5个元素的空间
	S* p = (S*)malloc(sizeof(S) + 5 * sizeof(int));
	if (p == NULL)
	{
		return 0;
	}

	p->a = -1;
	//将结构体中的柔性数组成员初始化
	for (i = 0; i < 5; i++)
	{
		p->arr[i] = i;
	}
	//打印结构体a成员
	printf("%d\n", p->a);

	//打印结构体arr成员
	for (i = 0; i < 5; i++)
	{
		printf("%d ", p->arr[i]);
	}
	printf("\n");

	//调整malloc申请空间大小,增加5个整型元素空间
	S* ptr = (S*)realloc(p, sizeof(S) + 10 * sizeof(int));

	//如果调整失败,则结束程序,调整成功,将返回的新地址赋值给指针p
	if (ptr == NULL)
	{
		return 0;
	}
	p = ptr;
	ptr = NULL;

	//将arr后面增加的元素,进行赋值
	for (i = 5; i < 10; i++)
	{
		p->arr[i] = i;
	}

	//打印新增加的结构体arr成员
	for (i = 5; i < 10; i++)
	{
		printf("%d ", p->arr[i]);
	}
	printf("\n");

	//释放内存
	free(p);
	p = NULL;
	return 0;
}

运行结果为:
【C语言】柔性数组_第2张图片


内存图分析:
【C语言】柔性数组_第3张图片


4.柔性数组的优势

我们使用柔性数组就是为了结构体中的数组成员大小可以根据我们的需要变化
那么下面的结构体也可以实现类似的功能:

#include 
#include

typedef struct S
{
	int a;
	int* arr;
} S;

int main()
{
	int i = 0;
	S* p = (S*)malloc(sizeof(S));
	//判断空间是否申请成功
	if (p == NULL)
	{
		return 0;
	}

	//申请5个整形元素大小空间,将空间地址赋值给结构体对象的arr成员,让arr成员维护这空间的数据
	(int*)p->arr = (int*)malloc(5 * sizeof(int));
	//判断空间是否申请成功
	if (p->arr == NULL)
	{
		return 0;
	}

	p->a = -1;
	printf("%d\n", p->a);

	//将arr指针存储地址对应空间的5个整形元素,进行赋值
	for (i = 0; i < 5; i++)
	{
		p->arr[i] = i;
	}

	//根据arr中存储的地址,进行5个整型元素的打印,打印所指空间的内容
	for (i = 0; i < 5; i++)
	{
		printf("%d ", p->arr[i]);
	}
	printf("\n");

	//若arr所指数组长度不满足我们所需,我们可以进行调整
	int* ptr = (int*)realloc(p->arr, 10 * sizeof(int));
	//判断是否调整成功
	if (ptr == NULL)
	{
		return 0;
	}

	//调整成功,将ptr赋值给p指向的arr成员
	p->arr = ptr;
	ptr = NULL;

	//根据arr存储的地址,将新增加的空间进行赋值
	for (i = 5; i < 10; i++)
	{
		p->arr[i] = i;
	}

	//根据arr存储的地址,将新增加的空间中存储的内容进行打印
	for (i = 5; i < 10; i++)
	{
		printf("%d ", p->arr[i]);
	}
	printf("\n");

	//释放空间
	free(p->arr);
	p->arr = NULL;
	free(p);
	p = NULL;
	return 0;

}

运行结果如下图:
【C语言】柔性数组_第4张图片
我们发现,和上方柔性数组例子结果一样,我们也可以根据这种方式,实现在结构体中的某个成员控制长度变化的数组


内存图分析:
【C语言】柔性数组_第5张图片


虽然上述方法可以完成和柔性数组实现的类似的功能,但是用柔性数组有一些优势:

1.方便内存释放:

  • 我们在没有使用柔性数组时,实现类似功能,需要申请两次空间,那么在结束使用时,就要释放两次空间。而使用柔性数组,只需要一次释放空间即可,形式上减少了错误的产生。
  • 而且,若我们的代码是在一个给别人用的函数中,你在里面做了二次内存分配,并把整个结构体返回给用户。用户调用free可以释放结构体,但是用户并不知道这个结构体内的成员也需要free,所以你不能指望用户来发现这个事。所以,如果我们把结构体的内存以及其成员要的内存一次性分配好了,并返回给用户一个结构体指针,用户做一次free就可以把所有的内存也给释放掉。

2.有利于减少内存碎片:

  • 多次申请空间,会导致申请的空间间有些小的,不利于使用的内存碎片。而使用柔性数组实现类似功能减少了申请空间的次数,有利于减少内存碎片

3.连续的内存有益于提高访问速度

  • 计算机存储结构是金字塔式的存储结构,cpu处理数据速度十分快,而读写内存较慢,那么在读取内存时,我们会将这次读写数据附近的数据放到寄存器或高速缓存中,以便下次若访问附近的数据,可以增快读写速度(局部性原理,多数情况下,下次访问的数据是上次访问数据附近的数据)。若是柔性数组实现类似的功能,申请的空间是连续的,有利于提高访问速度,而第二种实现的方法,会申请两次空间,两端空间不连续,不利于提高访问速度。

5.结语

我们简单介绍了柔性数组的概念和使用上的一些优势

如果这篇文章对你有帮助可以点个赞,点个关注之类的!!!

若有问题或者发现文章有问题可以发在评论区!!!

拜拜!!!

你可能感兴趣的:(c语言,开发语言)