1:背景
在阅读别人的代码的时候发现一种特别的结构体成员,一个长度为0的数组,类似于arr[0]。当时并不知为什么这样写。查阅一些资料才知道这个的妙用。
2:使用场所
长度为0的数据,目前好像只有GNU的C才支持。标准c和c++中是不允许。如果也想用的话,要使用长度为1的数组,来代替。
GNU的C不对越界的数组报错,会直接让你使用越界后的地址。数组越界后,会自动接着前面那块内存往后写,这样带来的将会是一系列安全问题。因为界外的内存不确定是否已经存放了东西,如果不凑巧存放着比较重要的数据,那么数组越界后将会把这块内存上的重要数据替换掉,后果可想而知。所以你要确保越界的地址空间是你可以控制的。例如使用malloc多分配一些空间,这段多分配的空间就是可以控制的。
3:出现的原因
通常我们使用结构体时,也需要在结构体中定义一些指针来指向别的空间。这样的话,你需要先为外层的结构体分配空间然后再为内部的指针分配空间。释放的时候先释放内部,再释放外部。
如果长度为0的数组的放在结构体的尾部,然后申请大于该结构体的空间
像这样
struct s{
char c;
float f;
char arr[0];
};
struct s *t = (struct s*)malloc(sizeof(struct s) + 10);
那么就产生一种很有趣的情况,就是在结构体成员arr处向后的10个字节,也可以被使用了。起始地址就是arr。
这样的话,只需要分配一次就可以有多余的空间可以被使用,如果释放的只需要释放t就可以把所有的申请空间全部释放。
4:示例
/*************************************************************************
> File Name: null_arr.c
> Author: kayshi
> Mail: kayshi2019@qq.com
> Created Time: Fri 27 Nov 2020 08:28:48 AM CST
************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct s{
char c;
float f;
char arr[0];
};
int main()
{
struct s *t = (struct s*)malloc(sizeof(struct s) + 10);
if(!t)
exit(1);
t->c = 5;
t->f = 3.14;
strcpy(t->arr, "hello");
printf("%d, %f, %s \n", t->c, t->f, t->arr);
}
结果
kayshi@ubuntu:~/code/Test$ ./a.out
5, 3.140000, hello
kayshi@ubuntu:~/code/Test$
结构体最后使用0或1的长度数组的原因,主要是为了方便的管理内存缓冲区,如果你直接使用指针而不使用数组,那么,你在分配内存缓冲区时,就必须分配结构体一次,然后再分配结构体内的指针一次,(而此时分配的内存已经与结构体的内存不连续了,所以要分别管理即申请和释放)而如果使用数组,那么只需要一次就可以全部分配出来,反过来,释放时也是一样,使用数组,一次释放,使用指针,得先释放结构体内的指针,再释放结构体。还不能颠倒次序。
其实就是分配一段连续的的内存,减少内存的碎片化
5:优点
- 不需要初始化,数组名直接就是所在的偏移 。
- 不占任何空间,指针需要占用int长度空间,空数组不占任何空间。
- 分配的内存连续,管理方便。
- 小内存的管理是非常困难的,如果用指针,这个buffer的struct部分就是小内存了, 在系统内存在多了势必严重影响内存管理的性能。要是用空数组把struct和实际数据缓冲区一次分配大块问题,就没有这个问题,解决了小内存碎片问题提高了性能。