柔性数组(Flexible Array)是一种 C99 标准引入的特性,也称为变长数组(Variable Length Array),它允许在结构体中定义一个大小不确定的数组。在结构体定义中,柔性数组必须是最后一个成员,其大小在结构体实例化时被动态指定。
比如:
struct S
{
int n;
int arr[]; // 柔性数组成员
}; 或者:
struct S
{
int n;
int arr[0]; // 柔性数组成员
};Ⅱ. 柔性数组的特点
1、结构中柔性数组成员前面必须至少有一个其他成员
比如,当你创建含有柔性数组成员的结构体时,结构体成员不能单单只有一个柔性数组成员:
struct Er
{
int arr[];
}; // error 除了柔性数组成员之外,结构体成员中应该至少再包含一个其他非柔性数组成员。
2、sizeof返回的这种结构大小不包括柔性数组的内存
用 sizeof 来计算一个含有柔性数组成员的结构体大小时,计算出的结果不包括柔性数组成员在内。比如:
#include <stdio.h>
struct S
{
int n;
int arr[]; // 柔性数组成员
};
int main()
{
printf("%d\n", sizeof(struct S));
return 0;
}
// 运行结果:
43、包含柔性数组成员的结构用malloc函数进行内存的的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小
比如,对于该结构体:
struct S
{
int n;
int arr[]; // 柔性数组成员
}; 你想用他的柔性数组成员存放5个整型元素,那么你应该这样开辟空间:
#include <stdio.h>
#include <stdlib.h>
struct S
{
int n;
int arr[]; // 柔性数组成员
};
int main()
{
// 开辟空间
struct S* ps = (struct S*)malloc(sizeof(struct S) + 5 * sizeof(int));
return 0;
}
Ⅲ. 柔性数组的使用
我们可以利用柔性数组实现以下功能:
- 要求:用结构体将数字100和0~4五个数字进行封装。
- 要求改为:用结构体将数字100和0~9十个数字进行封装。
#include <stdio.h>
#include <stdlib.h>
struct S
{
int n;
int arr[]; // 柔性数组成员
};
int main()
{
// 开辟动态内存空间
struct S* ps = (struct S*)malloc(sizeof(struct S) + 5 * sizeof(int));
ps->n = 100; // 将结构体中第一个元素赋值为100
int i = 0;
for (i = 0; i < 5; i++)
{
ps->arr[i] = i; // 将柔性数组中下标为i的元素赋值为i
}
// 调整所开辟的动态内存空间的大小
struct S* ptr = realloc(ps, sizeof(struct S) + 10 * sizeof(int));
if (ptr != NULL) // 开辟成功
ps = ptr;
for (i = 5; i < 10; i++)
{
ps->arr[i] = i; // 将柔性数组中下标为i的元素赋值为i
}
// 释放开辟的动态内存空间
free(ps);
ps = NULL;
return 0;
}注意:柔性数组的使用与动态开辟内存的知识密不可分。
Ⅳ. 柔性数组的优势
1、方便内存释放
我们可以看到,用柔性数组解决这个问题的时候,我们只需要释放一次动态内存。而模拟实现柔性数组的时候,我们需要释放两次动态内存,最重要的是这两次释放内存的顺序还不能颠倒,如若颠倒了释放顺序就会导致有一块动态开辟的内存空间不能得到释放,最终导致内存泄漏。
2、有益于提高访问速度,也有益于减少内存碎片
其实第一种用柔性数组解决问题的时候,内存中开辟的空间是连续的:

而第二种模拟实现柔性数组的方法,在开辟内存的时候差不多是这样的:

看似在分配内存的时候连续还是不连续好像没什么影响,但是你是否知道有一个概念叫内存碎片。
越多的不连续内存分配会产生越多的内存碎片,内存碎片越多,我们对内存的利用率就越低下,所以我们应该尽量避免不连续的内存分配。
其次,CPU在向存储器中提取数据时,会遵循局部性原理。
局部性原理:CPU访问存储器时,无论是存取指令还是存取数据,所访问的存储单元都趋于聚集在一个较小的连续区域中
所以,将相关联的数据存储在一起(即连续存储),会提高CPU的访问速度。