在c99标准中,结构体类型中最后一个元素是未知大小的数组,这个成员就叫做柔性数组成员
exp:
#include <stdio.h>
typedef struct T
{
int a;
int arr[];
//int arr[0];//这样的定义方式和上面的效果是一样的,都是未知大小的结构体柔性数组,不同的编译器,
//会有不同的定义方式
}st;
int main()
{
st* ptr;//st就是经过改变定义的结构体类型名
printf();
return 0;
}
这里还要强调的是typedef这个,他是可以改变类型名称的关键字
还有就是在计算包含柔性数组的结构体大小时,不包含该数组进行计算
柔性数组:顾名思义很随和,可以对它的大小进行改变,下面是柔性数组的使用:
#include <stdio.h>
#include <stdlib.h>
struct T
{
int n;
int arr[];
};
int main()
{
struct T s;
struct T* ptr=(struct T*)malloc(sizeof(s)+5*sizeof(int));//我们要在这里建立五个连续的空间,共 //给柔性数组,这样我们就达到了将数组的空间开辟可控的效果,
int i = 0;
for(i;i<5;i++)
{
ptr->arr[i]=i;//此处我们可以将这快在堆区开辟的连续空间当作数组去访问,因为数组的内存空间和这个 //一样,是在栈区开辟的连续数组,,所以可以类比的去给这块内存空间,去初始化
}
struct T* ptr1=(struct T*)realloc(ptr,44);//这里也可以对已经开辟的连续动态空间进行敢调整大 //小,还是达到数组空间可控的的目的
for(i=0;i<5;i++)
{
printf("%d",ptr->arr[i]);//successful 0 1 2 3 4
}
if(ptr1!=NULL)//这里调整大小要检验,避免开辟失败后对空指针进行操作,或者非法访问
{
ptr=ptr1;
for(i=5;i<10;i++)
{
ptr->arr[i]=i;
}
for(i=5;i<10;i++)
{
printf("%d",ptr->arr[i]);// 5 6 7 8 9
}
}
free(ptr);//这里的free一定要记得释放动态内存空间,养成好的编程习惯
ptr=NULL;
return 0;
}
当然也有另一种方法来模拟开辟连续动态空间,(模拟可控大小数组):
#include <stdio.h>
#include <stdlib.h>
sturct T
{
int n;
int* arr;
};
int main()
{
struct T s;
struct T* ptr=(struct T*)malloc(sizeof(s));
ptr—>arr=(struct T*)malloc(5*sizeof(int));
int i = 0;
for(i;i<5;i++)
{
ptr->arr[i]=i;
}
for(i=0;i<5;i++)
{
printf("%d",ptr->arr[i]);
}
//free(ptr);
//ptr=NULL;
//free(ptr->arr);这样的逻辑是错误的
//ptr->arr=NULL;
free(ptr->arr);
ptr->arr=NULL;//这里一定要先释放结构体内部的指针指向的开辟的连续空间,要不然当我们释放了ptr之后 //就没法找到arr指向的连续开辟的空间,那快空间就没法找到并释放了,导致内存泄漏,和接着会有非法访问
free(ptr);
ptr = NULL;
return 0;
}
这两种开辟数组的方式共同点都可以实现,对动态内存的连续开辟,达到可控大小的数组
第一种的优势在于,我们是先在内存堆中开辟一个结构体的动态内存空间,然后在第一个结构体成员n的后面开辟连续的动态内存空间,提到这里我们要注意,带有柔性数组的结构体的成员必须在柔性数组之前至少有一个确定的其他成员。
第二种他要先开辟一块动态内存空间来存放n和arr,然后在开辟一块连续的动态内存空间,将这块空间的起始地址返回arr,让它来存储,通过arr来找到空间再去调整大小
首先在我们在开辟空间后要free中就产生了差异,第一种方案,free只用了一次,而第二种们用了两次,先不说先后顺序,一般用户在使用我们的代码的时候,一般不会知道我们释放的动态内存空间的地址是否也是来自动态内存空间,或者我们使用的动态内存空间内不是否有我们需要先释放动态内存空间,这样的代码交互性不高,还有就是,万一堆的动态内存空间的释放顺序错了,也会出错,就像上面提到的。所以柔性数组的优势就体现出来了,它是一次性开辟那么多的动态内存空间 都是连续开辟的,交给一个指针ptr来维护,释放也就释放这指针指向地址开辟的动态内存空间, 而且如果按照第二种方法用动态内存空间的指针去接收另外开辟的动态内存空间,时不时会造成一些内存碎片(因为在堆上开辟内存空间是哪有就在哪开辟,有可能指针变量arr所在的空间和那些连续的空间,离得很远,这样的杂乱的使用空间就会造成一些未被充分利用的空间叫内存碎片,内存碎片越多,空间利用率越低)
还有就像上面提到的指针变量arr所处的空间并不一定和那些连续空间在一起,内存访问数据时候采取一种局部性原理(就是指在内存中访问一个数据的时候,接下来有很大的几率会接着访问它附近的数据),而如果那些连续空间和指针变量arr所处的空间离得很远,就会花费更长的时间去找到这块连续空间的数据,而且我们在访问数据中,其实最先开始的是寄存器,如果寄存器中没有我们会向下一级cache(高速缓存器)中去访问,然后再没有再向内存,硬盘去访问,这些内存器的访问速度是由高到低的,所以如果开辟的时候就将它们放在一起,就很方便访问,速度相较快
所以我们总结一些,柔性数组的优势更大一些