当前位置: 首页 > 科技观察

哦!数组还是可以这样用的,学习了!

时间:2023-03-18 22:17:21 科技观察

这个问题源于读者在阅读redis源码时的一个疑问。先看下面的代码,对于动态字符串成员,Test0和Test1这两个结构占用了多少空间?//来源:公众号【编程珠玑】//作者:看守先生#includestructTest0{inta;intb;char*c;};structTest1{inta;intb;charc[];};intmain(void){printf("sizeof(structTest0)=%zd\n",sizeof(structTest0));printf("sizeof(structTest1)=%zd\n",sizeof(structTest1));return0;}可以是看到在64位系统上,编译成64位程序,输出结果为:168Test0的结果为16,通常不用怀疑,毕竟4(int)+4(int)+8(pointer)=16,但有些读者可能会对后一个结构占用8个字节产生疑惑。(关于字节对齐,参考《字节对齐,看这篇就懂了》)灵活数组(flexiblearray)其实是C99引入的灵活数组的一个特性。也就是说,结构的最后一个成员可以是一个不完整类型的数组(一种缺乏足够信息来描述一个完整对象的类型),但是它使得整个结构的大小就像没有这个成员一样。但是,当使用结构体通过这个名字来访问这个成员时,就像访问一个普通的数组成员一样。如果数组最终没有元素,那么访问数组将是未定义的行为。正如我们之前看到的:structTest1{inta;intb;charc[];};成员c是一个数组,但是没有指定大小,使用sizeof计算Test1,它占用的空间只有8个字节。有什么好处?那么使用灵活数组有什么好处呢?内存申请和释放假设使用两种类型的结构来存储16字节的字符数据,需要申请内存。forstructTest0:strcutTest0*t0=malloc(sizeof(structTest0));//为结构体t0申请内存->c=malloc(sizeof(char)*16);//为指向的数据申请内存成员和结构Test1:strcutTest1*t1=malloc(sizeof(structTest1)+sizeof(char)*16);看到不同?前者需要两次内存申请,而后者只需要一次。前者的地址是不连续的(两个malloc),而后者的地址是连续的。当访问成员c时,只需要做如下操作:t1->c,与普通成员没有区别。判断他们的地址是否连续也很简单,只需要分别打印b和c的地址即可。与内存释放类似,成员c申请的内存需要单独释放,而后者可以一起释放。由于前面的差异,数据复制在复制数据时更加不同。forstructTest0://memcpy(t0copy,t0,sizeof(structTest0));//不是,所以t0copy的c和t0的c指向同一个内存区。t0t0copy.a=t0.a;t0t0copy.b=t0.b;memcpy(t0copy.c,t0.c,sizeof(char)*16);这里不能一次性复制,因为它的成员c是指针类型,我们需要的是全量复制,所以它指向的内存必须复制。(参考《结构体成员赋值到底是深拷贝还是浅拷贝?》)但是对于structTest1:memcpy(t0copy,t0,sizeof(strcutTest1)+sizeof(char)*16);这里由于灵活数组的内存,它的数据内容和结构体数据成员的地址是连续的,所以可以直接复制。减少内存碎片由于结构体的灵活数组和结构体的成员的地址是连续的,可以一起申请内存,从而更大程度的避免了内存碎片。另外,由于成员本身不占用结构体的空间,所以总体来说,比普通数组成员占用的空间要少一些。零长数组的功能和灵活数组类似,也有零长数组,只是没有在标准中,但是可以实现类似的功能。用法如下:structTest1{inta;intb;charc[0];};区别是让数组长度为0。但是由于不是C标准的一部分,所以不建议移植,除非你还不能使用C99。灵活数组的使用总结:位于结构体的最后位置不完整的数组类型不是唯一的成员最后放一张图看看区别:普通数组和灵活数组