C99中,结构中的最后一个元素允许是未知大小的数组,称为柔性数组成员。
struct S
{
int n;
int arr[]; //未知大小
};
//struct S
//{
// int n;
// int arr[0]; //未知大小
//};
int main()
{
struct S s;
return 0;
}
上述代码中两种写法都是正确的,未知大小的数组被称为柔性数组成员,数组的大小是可以调整的。
创建包含柔性数组的结构体
计算包含柔性数组成员的结构体的大小时,不包含柔性数组成员,如下:
struct S
{
int n;
int arr[]; //未知大小
};
int main()
{
struct S s;
printf("%d\n", sizeof(s)); //4
return 0;
}
柔性数组的应用:
#include <stdlib.h>
struct S
{
int n;
int arr[];
};
int main()
{
struct S* ps = (struct S*)malloc(sizeof(struct S) + 5 * sizeof(int));
if (ps != NULL)
{
int i = 0;
for (i = 0; i < 5; i++)
{
ps->arr[i] = i;
}
struct S* ptr = (struct S*)realloc(ps, 44);
if (ptr != NULL)
{
ps = ptr;
}
for (i = 5; i < 10; i++)
{
ps->arr[i] = i;
}
for(i=0;i<10;i++)
{
printf("%d ", ps->arr[i]);
}
}
free(ps);
ps = NULL;
return 0;
}
上述代码中,结构体S中的arr数组的大小是可以调整的,称为柔性数组。
使用动态开辟空间代替柔性数组
#include <stdlib.h>
struct S
{
int n;
int* arr;
//不能写成数组形式,一旦写成数组形式,除了柔性数组外其他数组都不可以改变
//需要改变大小的话可以写成指针形式,指向一个动态开辟的空间
};
int main()
{
//开辟一个结构体空间,包含n和指针arr
struct S* ps = (struct S*)malloc(sizeof(struct S));
ps->arr = malloc(5 * sizeof(int));
int i = 0;
for (i = 0; i < 5; i++)
{
ps->arr[i] = i;
printf("%d ", ps->arr[i]);
}
printf("\n");
//调整arr指针指向空间的大小
int* ptr = realloc(ps->arr, 10 * sizeof(int));
if (ptr != NULL)
{
ps->arr = ptr;
}
for (i = 5; i < 10; i++)
{
ps->arr[i] = i;
}
for (i = 0; i < 10; i++)
{
printf("%d ", ps->arr[i]);
}
free(ps->arr);
ps->arr = NULL;
free(ps);
ps = NULL;
return 0;
}
0 1 2 3 4
0 1 2 3 4 5 6 7 8 9
上述代码中,两次malloc开辟了两块内存空间,在使用完毕后需要释放空间并置为空指针,顺序是从内向外释放,即先释放arr开辟的空间,再释放struct S开辟的空间。 如下: 先释放ps,ptr指向的空间就不能通过ps->arr找到,ptr指向的空间就无法正常释放。 ::: tip 柔性数组所在的结构体的大小应该使用malloc、calloc创建,后期使用realloc调整空间大小。上图中结构体是由malloc一次开辟的,开辟空间是连续的,即arr所在的空间是紧跟在n的后面,所以ps->n可以访问n,ps->arr可以访问arr。 :::
两种方案的区别
使用柔性数组开辟空间时,是由malloc一次开辟成功,由结构体指针ps维护开辟的连续空间。动态内存开辟的方式是先使用malloc位结构体S开辟一个空间,空间内包含n和arr,arr的大小需要改变时,指针arr再通过malloc指向新开辟的空间。 两种方式的内存布局是有区别的,但都可以实现结构体S中n后面的空间大小动态变化的需要。
::: tip 柔性数组的优势: 一次开辟连续的空间在释放时只需要释放一次,无需考虑多次释放空间时的顺序。 内存空间连续存放时访问效率会更高。 柔性数组的内存利用率更高。 (malloc的内存空间开辟方式是有空间空余就会去开辟空间,容易造成更多小的空间,即更多的内存碎片。malloc的频繁使用,会造成更多的内存碎片) :::
柔性数组特点:
- 结构体中柔性数组前必须至少包含一个其他成员。
- sizeof返回的结构大小不包括柔性数组的。
- 包含柔性数组成员的结构使用malloc进行动态内存分配,分配的内存大小应大于结构大小,以适应柔性数组的预期大小。 (柔性数组开辟空间时除了其他成员大小外加上一块新的空间分配给柔性数组。)