柔性数组
这个问题是阿里的一个面试题。当时没有很清楚,答得很差,特地实验看一下运行结果。
在结构体中定义了一个
char*
指针,与定义一个零元素的char
数组有什么区别?
作用
常用来构成缓冲区。比起指针,用空数组有这样的优势:
- 不需要初始化,数组名直接就是所在的偏移;
- 不占任何空间,指针需要占用int长度空间,空数组不占任何空间。
“这个数组不占用任何内存”,意味着这样的结构节省空间;
“该数组的内存地址就和它后面的元素地址相同”,意味着无需初始化,数组名就是后面元素的地址,直接就能当指针使用。
这样的写法最适合制作动态buffer,因为可以这样分配空间malloc(sizeof(structXXX) + buff_len)
; 直接就把buffer的结构体和缓冲区一块分配了。用起来也非常方便,因为现在空数组其实变成了buff_len长度的数组了。这样的好处是:
- 一次分配解决问题,省了不少麻烦。为了防止内存泄露,如果是分两次分配(结构体和缓冲区),那么要是第二次malloc失败了,必须回滚释放第一个分配的结构体。这样带来了编码麻烦。其次,分配了第二个缓冲区以后,如果结构里面用的是指针,还要为这个指针赋值。同样,在free这个buffer的时候,用指针也要两次free。如果用空数组,所有问题一次解决。
- 小内存的管理是非常困难的,如果用指针,这个buffer的struct部分就是小内存了,在系统内存在多了势必严重影响内存管理的性能。要是用空数组把struct和实际数据缓冲区一次分配大块问题,就没有这个问题。如此看来,用空数组既简化编码,又解决了小内存碎片问题提高了性能。
结构体最后使用0或1长度数组的原因:
为了方便的管理内存缓冲区(其实就是分配一段连续的内存,减少内存的碎片化),如果直接使用指针而不使用数组,那么,在分配内存缓冲区时,就必须分配结构体一次,然后再分配结构体内的指针一次,(而此时分配的内存已经与结构体的内存不连续了,所有要分别管理即申请和释放)而如果使用数组,那么只需要一次就可以全部分配出来,反过来,释放时也是一样,使用数组,一次释放。使用指针,得先释放结构体内的指针,再释放结构体,还不能颠倒顺序
结构体中最后一个成员为[1]长度数组的用法:与长度为[0]数组的用法相同,改写为[1]是出于可移植性的考虑。有些编译器不支持[0]数组,可将其改成[]或[1].
解释
1 | struct A{ |
为了说明这个问题,我们定义一下几个结构体作为比较:
1 | struct A |
1 | cout << sizeof(A) << endl; |
输出占用空间大小为
4 8 4 8 16
可以看到struct A
大小为int
大小 4字节,struct B
由于包含了一个指针,在32位系统中,大小为 4字节,总共8字节。strcut C
大小为4字节,明显char[0]
没有分配内存。struct D
大小由于内存对齐原因得到为8字节。struct E
大小同样由于内存对齐原因得到为16字节。
由此可以看到长度为0的数组没有分配内存。
为了更详细的说明内存分配情况,我们查看一下每个的地址.
1 | A a; B b; C c; D d; E e; |
输出为:
00AFFB4C
00AFFB3C 4 00AFFB40
00AFFB30 4 00AFFB34
00AFFB20 4 00AFFB24
00AFFB08 4 00AFFB0C
可以看到每个b
都指向了同一位置,int a
后面一位的地址.
为了更清楚的描述,在中间插入一个char c
可以看到有:
1 | struct A |
输出为:
012FF9E8
012FF9D4 8 012FF9DC
012FF9C4 5 012FF9C9
很明显可以得到结论,char b[0]
不分配内存,但是可以获得结构体的末尾地址.
本文标题:柔性数组
文章作者:王二
发布时间:2019-04-26
最后更新:2024-11-06
原始链接:https://wanger-sjtu.github.io/%E6%9F%94%E6%80%A7%E6%95%B0%E7%BB%84/
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!