柔性数组概念:
这个概念你可能没听说过,但是这个概念确实存在,在C99中,结构体中最后一个成员允许是未知大小的数组,这就叫做【柔性数组】成员。
struct S { char c; int i; int arr[0];//未知大小的数组 - 柔性数组成员 };
有些编译器可能不支持上面的写法,可以改成:
struct S { char c;//1 //3 int i;//4 int arr[];//未知大小的数组 - 柔性数组成员 };
柔性数组的特点:
结构中的柔性数组成员前面必须至少有一个其他成员
sizeof返回这种结构体大小不包括柔性数组成员的内存
包含柔性数组成员的结构用malloc进行内存分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。
我们还是以刚刚上面的结构体为例:
根据内存对齐规则,这个结构如果不算柔性数组大小的话,的确是8,而我们可以看到,这里打印出来的也确实是8,所以我们可以理解第二条特点sizeof返回结构大小的时候不包括柔性数组的大小。
那么第一条特点也就可以解释了,倘若结构里面只有柔性数组的话,那么sizeof计算结构体大小,就没办法算了,所以结构里柔性数组的前面必须至少有一个其他类型的成员。
那么第三条特点的意思是,我们在为其开辟内存的时候是这样的:
我们定义一个结构变量指针用来接收,然后malloc出来的空间首先sizeof(struct S)代表先给char和int两个成员变量开辟空间,然后剩下的+20就是给柔性数组来开辟的,内存申请的不够了,就可以用realloc重新调整大小。
具体的代码实现是这样的:
struct S { char c;//1 //3 int i;//4 int arr[];//未知大小的数组 - 柔性数组成员 }; int main(){ struct S* ps = (struct S*)malloc(sizeof(struct S) + 20); if (ps == NULL) { perror("malloc"); return 1; } ps->c = 'w'; ps->i = 100; int i = 0; for (i = 0; i < 5; i++) { ps->arr[i] = i; } //打印 for (i = 0; i < 5; i++) { printf("%d ", ps->arr[i]); } //空间不够了 struct S* ptr = (struct S*)realloc(ps, sizeof(struct S)+40); if (ptr != NULL) { ps = ptr; } else { perror("realloc"); return 1; } //增容成功后,继续使用 //释放 free(ps); ps = NULL; return 0; }
上面的例子就是对结构里有柔性数组的该如何进行调整,malloc以及realloc,还有即使打印使用的时候。
但是如果我们在柔性数组那里换成指针是不是也一样可以达到空间的可大可小的效果呢?
例如这样的代码:
struct s { char c; int i; int* data; }; int main() { struct s* ps = (struct s*)malloc(sizeof(struct s)); if (ps == NULL) { perror("malloc1"); return 1; } ps->c = 'w'; ps->i = 100; ps->data = (int*)malloc(20); if (ps->data == NULL) { perror("malloc2"); return 1; } int i = 0; for (i = 0; i < 5; i++) { ps->data[i] = i; } for (i = 0; i < 5; i++) { printf("%d ", ps->data[i]); } //空间不够了,增容 int* ptr = (int*)realloc(ps->data, 40); if (ptr == NULL) { perror("realloc"); return 1; } else { ps->data = ptr; } /*增容成功就使用 ... 释放*/ free(ps->data); ps->data = NULL; free(ps); ps = NULL; return 0; }
我们这里结构里放了一个指针变量成员int* data,而我们再用malloc函数申请空间并让data指向这个空间也就达到了和柔性数组一样的效果。只不过我们这里需要malloc两次:
当然也需要释放两次:
那么既然这两个代码实现的效果一样,那么哪个更好一点呢?
答案是:第一个会更好一点。(带有柔性数组)
好处1:方便内存释放
第一个只用申请一次,而第二个则需要申请两次。
好处2:有利于访问速度
一次申请空间,空间内存一定是连续的,但是两次申请空间的话,就不一定是连续的了,内存之间就有可能存在缝隙,那么这些缝隙,也就有可能影响访问速度。(但我个人觉得其实也没多高)。