前一段在看百度阿拉丁底层存储的时候,看到这样一段代码:
struct dlist_t { dlist_t *next; u_int key; int size[0]; };
印象中int size[0]这种在C/C++是不允许的,但是在结构中确实可以使用的(C99标准)。经过查看资料,知道这种可变数组被称为柔性数组。
C99的标准形式如下
struct sample { int a; double b; char c[]; //或者 char c[0] };
在结构体的最后,可以加入一个长度为0的数组c,这个数组c就是所谓的柔性数组。c只是一个偏移,通过动态申请c的大小可以达到动态结构的效果。
#include<cstring> #include<iostream> using namespace std; #define uint32 unsigned int typedef struct _normal_array_t { char a; uint32 b; int *c; }__attribute ((packed)) normal_array_t; typedef struct _dynamic_array_t { char a; uint32 b; int c[]; }__attribute ((packed)) dynamic_array_t; int main() { normal_array_t* n1 = (normal_array_t*)malloc(sizeof(normal_array_t) ); cout << "n1: before malloc size is " << sizeof(*n1) << endl; n1->c = (int*) malloc(100); n1->c[50] = 100; cout << "n1: after malloc c, n1->c[50] is " << n1->c[50] << endl; cout << "n1: after malloc c, size is " << sizeof(*n1) << endl; free(n1->c); free(n1); dynamic_array_t* d1 = (dynamic_array_t*)malloc(sizeof(dynamic_array_t) + 100 * sizeof(int) ); cout << "d1: size is " << sizeof(*d1) << endl; d1->c[50] = 200; cout << "d1: d1->c[50] is " << d1->c[50] << endl; free(d1); }
结果如下:
[email protected]:~/test$ ./dynamic n1: before malloc size is 13 n1: after malloc c, n1->c[50] is 100 n1: after malloc c, size is 13 d1: size is 5 d1: d1->c[50] is 200
如上图,我们如果想在struct里面声明一个动态的数组,可以有2种方式(里面的__attribute ((packed)) 是禁止编译器做字节对齐,效果明显).
第一种如下所示,这种方法可以先申请normal_array_t自身,然后在申请normal_array_t->c,然后通过normal_array_t->c[index]来访问动态数组,使用之后,需要先free(normal_array_t->c),然后再free(normal_array_t);
我的机器是64位,所以指针为8个字节,进而normal_array_t大小为13.
typedef struct _normal_array_t { char a; uint32 b; int *c; }__attribute ((packed)) normal_array_t;
第二种方法如下所示,这种方法一次性申请normal_array_t加上需要动态数组的大小来申请一整块内存,然后通过dynamic_array_t->c[index]来访问动态数组,使用之后,需要先free(dynamic_array_t)就可以释放整个内存。
可以看到dynamic_array_t->c仅仅是一个符号,dynamic_array_t的大小为5(char1, uint32 4)。
typedef struct _normal_array_t { char a; uint32 b; int *c; }__attribute ((packed)) normal_array_t;
可以看到使用柔性数组可以大大简化内容的管理,只需要一次申请,然后通过数组的指针偏移就可以直接获得相应的数据缓冲区,非常简单,释放的时候也仅仅只需要一次释放。