在讲述柔性数组成员之前,首先要介绍一下不完整类型(incomplete type)。不完整类型是这样一种类型,它缺乏足够的信息例如长度去描述一个完整的对象,它的出现反映了C程序员对精炼代码的极致追求,这种代码结构产生于对动态结构体的需求。
鉴于这种代码结构所产生的重要作用,C99甚至把它收入了标准中。C99使用不完整类型实现柔性数组成员,在C99 中,结构中的最后一个元素允许是未知大小的数组,这就叫做柔性数组(flexible array)成员(也叫伸缩性数组成员),但结构中的柔性数组成员前面必须至少一个其他成员。柔性数组成员允许结构中包含一个大小可变的数组。柔性数组成员只作为一个符号地址存在,而且必须是结构体的最后一个成员,sizeof 返回的这种结构大小不包括柔性数组的内存(即:sizeof(SoftArray)=4)。柔性数组成员不仅可以用于字符数组,还可以是元素为其它类型的数组。包含柔性数组成员的结构用malloc ()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。柔性数组的使用请看下面柔性数组的定义:
typedef struct _soft_array
{
int len;
int array[0];
}SoftArray;
有些编译器会报错无法编译可以改成:
typedef struct _soft_array
{
int len;
int array[];
}SoftArray;
下面是柔性数组的完整示例:
#include <stdio.h>
#include <malloc.h>
typedef struct _soft_array
{
int len;
int array[];
}SoftArray;
SoftArray* create_soft_array(int size)
{
SoftArray* ret = NULL;
if( size > 0 )
{
ret = (SoftArray*)malloc(sizeof(*ret) + sizeof(*(ret->array)) * size);
ret->len = size;
}
return ret;
}
void fac(SoftArray* sa)
{
int i = 0;
if( NULL != sa )
{
if( 1 == sa->len )
{
sa->array[0] = 1;
}
else
{
sa->array[0] = 1;
sa->array[1] = 1;
for(i=2; i<sa->len; i++)
{
sa->array[i] = sa->array[i-1] + sa->array[i-2];
}
}
}
}
void delete_soft_array(SoftArray* sa)
{
free(sa);
}
int main()
{
int i = 0;
SoftArray* sa = create_soft_array(10);
fac(sa);
for(i=0; i<sa->len; i++)
{
printf("%d\n", sa->array[i]);
}
delete_soft_array(sa);
return 0;
}
如下为关于柔性数组的一道面试题:
对以下数据结构中data的处理方式描述正确的是()
struct Node
{
int size;
char data[0];
};
A data将会被编译成一个char *类型指针
B 全部描述都不正确
C 编译器会认为这就是一个长度为0的数组,而且会支持对于数组data的越界访问
D 编译器会默认将数组data的长度设置为1
正确答案: C
很多人其实会有这种疑惑,就是为什么不用指针去代替变长结构体,比如:
structNode
{
int size;
char* data;
};
就这个问题,我总结了一下用指针和用变长结构体的区别:
1.在位置方面:指针可以放在任何地方,但是变长结构体的变长部分一定要放在结构体的最后。
2.在内存占用方面:指针会占一个指针的大小的内存空间,但是变长数组是不占内存的,它只是一个占位符。
3.在内存布局方面:指针指向的内存和结构体的内存可以是不连续的,但是变长部分和结构体的内存必须是连续。
4.在内存释放方面:使用指针,就要先释放指针所指的内存在释放整个结构体的内存,否则会照成内存泄露。但是使用变长结构体直接释放整个结构体的空间就可以了。
5.一个限制:指针可以用在C++的类中,但是变长结构体就不可以了。因为有些编译器会将一些额外的信息放在类的最后,比如vptr或者虚基类的内容,使用了变长的类,就会把这部分的值改变,这种行为是未定义的,谁也不知道会发生什么。