C编程-柔性数组

Posted by 周思进 on September 6, 2019

偶尔在结构体中看到0长度的数组(柔性数组),比如

typedef struct {
    int len;
    char data[0];
}TEST;

TEST test;

一直没仔细去研究过,这次好好了解记录下。

网上搜索时,看到耗子叔写的《C语言结构体里的成员数组和指针》文章里非常详细的解说了柔性数组的特性和作用,非常推荐看看。(这文章我看完消化还是挺花了一些时间的… 多gdb调试下,对理解还是很有用的)

里面涉及的gdb调试命令也都相对简单,具体可以help查看

命令 作用
l 显示代码
b 设置断点
r 执行代码
p 命令打印对象值
dissassemble 查看汇编
x 显示地址对应的数据

文章里一开始并没有讲柔性数组的事,而是先讲了会示例代码中执行到哪个语句会崩溃的问题,例子中只是刚好数组使用了柔性数组,本身其实和柔性数组并没有什么关系。

该问题的核心在于访问数组名得到的是数组的地址,而访问指针则是访问指针所指向地址的内容。

所以即使数组array和指针p所指向的地址都是0x04,但语句 if (array) 是不会造成段错误问题,而语句 if (p) 则会造成崩溃问题,原因在于需要访问p所指向的地址0x04的内容,而在用户空间,0x04地址是不可访问的,所以导致段错误。(这也好理解,平常应用层代码判断 p != NULL 不会段错误的原因就是p本身所在的地址是用户空间可访问的)


文章后面讲了柔性数组,一开始我也是没明白为啥要柔性数组,我直接结构体里声明一个指针,再动态申请内存不就行了。文中说了两个好处:

1、柔性数组所指向的内存空间和结构体连续,读写相对高效
2、因为内存连续,方便一起释放。

个人觉得这个具体还是看使用场景了,如果只是自己内部使用的,可能更多的还是声明一个指针的方式,再动态申请内存。一点原因是,柔性数组可能并不是所有人都那么清楚了解,这对于维护其实是增加一点复杂度了。

针对柔性数组好处的第二点,我也觉得是内部接口自己使用的情况下有体现,如果是提供外部接口给别人调用的,除非结构体内存也是由内部动态申请的(接口声明得是个二级指针,感觉也提升了一点复杂度),那么的确是做成柔性数组好。这样外部调用完之后,只需要做一次释放操作。

而如果是使用的指针形式,则需要先释放指针申请的内存空间,再释放结构体声明的内存空间,这的确很容易忘记释放指针所申请的内存空间。

但一般场景可能结构体声明在接口外面定义,接口调用传递结构体指针进去,内部再动态申请下内存空间,外部调用完,只需要释放下内部申请的这部分空间就行,可能是更常使用的方式。(当然这里无论哪种方式,接口说明中都得写详细了)


涉及问题
1、柔性数组 是不占内存空间的,可以理解为只是一个占位符,即含有柔性数组的结构体大小不用考虑柔性数组所占的内存空间

2、lvalue required as left operand of assignment 左值不能是表达式