柔性数组
也许你从来没有听说过柔性数组(flexible array)这个概念,但是它确实是存在的。C99中,结构体中的最后一个元素允许是未知大小的数组,这就叫做『柔性数组』成员。
//写法1
typedef struct st_type
{
int i;
int a[0];//柔性数组成员,大小未知
}type_a;
//写法2
typedef struct st_type
{
int i;
int a[];//柔性数组成员,大小未知
}type_a;
柔性数组的特点:
- 结构中的柔性数组成员前面必须至少有一个其他成员
- sizeof返回的这种结构大小不包括柔性数组的内存。
- 包含柔性数组成员的结构用malloc()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。
typedef struct st_type
{
int i; //4
int a[0];//柔性数组成员
}type_a;
int main()
{
printf("%d\n",sizeof(type_a)); //输出的是4
return 0;
}
柔性数组的动态申请内存和使用
typedef struct S
{
int i;
int arr[0]; //柔性数组
};
int main()
{
//用malloc动态分配内存,期望arr的大小是10个整型
struct S* p = (struct S*)malloc(sizeof(struct S)+10*sizeof(int)); //申请空间:4(i)+10*4(arr) = 44
//使用...
p->i = 10;
for(int i = 0;i<10;i++)
{
p->arr[i] = i; //对柔性数组赋值
}
//增容
struct S* ptr = (struct S*)realloc(p,sizeof(struct S)+20*sizeof(int));
if(ptr != NULL)
{
p = ptr;
}
//使用增容后的空间...
//释放
free(p);
p = NULL;
return 0;
}
用普通代码实现柔性数组的功能
//实现柔性数组相同功能
struct S
{
int n;
int* arr;
};
int main()
{
struct S* p = (struct S*)malloc(sizeof(struct S));
if (p == NULL)
{
return 1;
}
p->n = 10;
p->arr = (int*)malloc(10 * sizeof(int)); //对指针arr再进行动态空间分配
if (p->arr == NULL)
{
return 1;
}
//使用
for (int i = 0; i < 10; i++)
{
p->arr[i] = i;
}
//增容
int* ptr = (int*)realloc(p->arr, 20 * sizeof(int));
if (ptr != NULL)
{
p->arr = ptr;
}
free(p->arr); //先释放arr指针指向的动态申请空间
p->arr = NULL;
free(p); //再释放整个结构体的空间,如果先释放结构体的空间,指针arr被释放了,则arr指向的空间就找不到了
p = NULL;
return 0;
}
普通代码实现相同功能与柔性数组的比较
- 普通代码需要两次malloc与free,比较容易出错,而柔性数组只需要一次malloc与free,方便内存的申请与释放
- 多次malloc在堆上申请空间时,会产生内存碎片,内存碎片被再次利用的概率小,会造成内存利用率降低(这里引申出软件设计上的内存池概念:在内存池里已经统一申请了多块空间,程序需要时直接拿去用就可以,用完后还回来,再统一释放,做到对内存的统一管理)
- 空间局部性:一块内存被访问后,其周边的内存很大概率会被再次访问;所以柔性数组在一次性malloc时变量的内存是连续的,对内存的访问速度一定程度上比较快,而多次malloc的内存空间可能是不连续的,此时对内存的访问效率会有一定影响