【c语言】柔性数组/变长数组_austerlitzl的博客-爱代码爱编程
文章目录
1.柔性数组的概念
C99标准下,结构中最后一个元素允许是未知大小的数组 ,这个数组就是柔性数组,也被称作变长数组。
这里必须注意三点:第一,在结构体中定义的未知大小数组。 第二,该位置大小数组必须是结构体最后一个元素,该数组前面至少有一个元素。 举个例子,比如下方图片,arr就是柔性数组。 第三,数组名永远不会是指针,但是对于这个数组的大小,我们可以动态分配(下面会体现出来)。
2.柔性数组的使用和特点
1.柔性数组不占用内存空间
对于编译器来说,柔性数组并不占用内存空间,可以用sizeof来验证。如下代码,运行后结果如图,是8,说明struct s这个结构体大小为8,而int类型和float类型的数据都是4个字节,根据结构体内存对齐规则,如果不计算arr的大小,结构体大小确实为8。
#include<stdio.h>
#include<stdlib.h>
struct S
{
int a;
float b;
int arr[];
};
int main()
{
printf("%d ", sizeof(struct S));
}
2.有柔性数组的结构要用malloc分配空间
包含柔性数组成员的结构要用malloc分配空间,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。如下,malloc分配给了s原本结构体的大小和4个int类型数据的大小,这4个int类型数据的大小就是分配给arr的,此时arr可用的是arr[0]到arr[3]。
与此同时,由于是malloc分配,所以最后不用的时候要free掉。
#include<stdio.h>
#include<stdlib.h>
struct S
{
int a;
float b;
int arr[];//结构体最后一个元素允许是大小未知的,但是其前面至少有一个成员
};
int main()
{
//开辟
struct S* s = (struct S*)malloc(sizeof(struct S) + sizeof(int) * 4);
if (!s)
{
printf("malloc fail!");
exit(-1);
}
//使用
s->a = 10;
s->b = 5.5;
for (int i = 0;i < 4;i++)
s->arr[i] = i+1;
printf("%d %.2f\n", s->a,s->b);
for (int i = 0;i < 4;i++)
{
printf("%d ", s->arr[i]);
}
//释放
free(s);
return 0;
}
既然是“柔性数组”,那么其“柔”体现在哪里呢?比如上面的代码,arr只可以存放4个数据,但是现在需要存放10个数据,那么就要arr数组可以扩大容量,而柔性数组可以实现这个功能,其“柔”体现在这里。比如下面代码,调整的代码块,用realloc分配了s原本结构体的大小和10个int类型数据的大小,此时arr数组就可以存储10个数据。对新开辟的空间赋值,其运行结果如下图。
#include<stdio.h>
#include<stdlib.h>
struct S
{
int a;
float b;
int arr[];//结构体最后一个元素允许是大小未知的,但是其前面至少有一个成员
};
int main()
{
//开辟
struct S* s = (struct S*)malloc(sizeof(struct S) + sizeof(int) * 4);
if (!s)
{
printf("malloc fail!");
exit(-1);
}
//使用
s->a = 10;
s->b = 5.5;
for (int i = 0;i < 4;i++)
s->arr[i] = i+1;
printf("%d %.2f\n", s->a,s->b);
for (int i = 0;i < 4;i++)
{
printf("%d ", s->arr[i]);
}
//调整
struct S* ptr = (struct S*)realloc(s, sizeof(struct S) + 10 * sizeof(int));
if (!ptr)
{
printf("realloc fail!");
exit(-1);
}
else
{
s = ptr;
}
//使用
for (int i = 4;i < 10;i++)
{
s->arr[i] = i+1;
}
for (int i = 4;i < 10;i++)
{
printf("%d ", s->arr[i]);
}
//释放
free(s);
return 0;
}
3.柔性数组的优势
到这里,不免会有疑问,既然柔性数组是为了让数组容量可以变化,那么为什么不直接定义一个指针,然后动态分配内存呢?实际上,柔性数组比使用指针动态分配内存要好,一方面方便内存的释放,另一方面可以提高访问速度、减少内存碎片。
1.方便内存释放
如下代码,在开辟内存的时候,一方面要开辟结构体的空间(这里是为了模仿柔性数组,所以结构体也动态开辟),另一方面要开辟arr指向的数组的空间。释放的时候,既要释放结构体空间,又要释放arr指向数组的空间,比较麻烦。而上文中,使用柔性数组,可以直接一次性开辟、一次性释放,相较之下很方便。
#include<stdio.h>
#include<stdlib.h>
struct S
{
int a;
float b;
int* arr;
};
int main()
{
//开辟
struct S* s = (struct S*)malloc(sizeof(struct S));
if (!s)
{
printf("malloc fail!");
exit(-1);
}
int* ptr = (int*)malloc(sizeof(int) * 10);
if (!ptr)
{
printf("malloc fail!");
exit(-1);
}
s->arr = ptr;
//使用
s->a = 10;
s->b = 5.5;
for (int i = 0;i < 10;i++)
{
s->arr[i] = i + 1;
}
printf("%d %.2f\n", s->a, s->b);
for (int i = 0;i < 10;i++)
{
printf("%d ", s->arr[i]);
}
//释放
free(s);
s = NULL;
free(ptr);
ptr = NULL;
return 0;
}
2.提高访问速度、减少内存碎片
如下图,左边是结构体的内存示意,右边是堆区内存示意图。结合上面代码,在堆区内存中,开辟了一块空间存放结构体s,同时还要开辟另一块空间存放数组arr,但是这块空间位置随机的,可能是蓝色方框1、2、3、4中任意一个,也可能是其他地方,它和结构体并不连续,就导致结构体和数组arr中间的那一块空间,很难被利用,这块空间就被称为内存碎片。
但是用柔性数组就不胡有这样的问题,柔性数组 开辟的arr就是一个数组,而不是指针,如下图,这样不会存在内存碎片的问题。同时,由于数组就在结构体里面,所以a、b、arr数组在内存里是连续的,访问起来要快一些。而像上面的,arr是指针,多了一个找到arr物理地址的过程,相较之下要慢一点点。
关于柔性数组,就介绍这么多啦!!!