C语言学习-24-柔性数组
目录
一、自由抒发
二、offsetof宏函数
1、作用
2、头文件
3、定义
4、参数介绍
三、实验模拟
1、测试代码
2、实验效果
四、优点与缺点
1、柔性数组的优点
2、 柔性数组的缺点
一、自由抒发
最近在摸索PG数据库源码时,发现一种之前没有用到过的C语言特性-柔性数组,它允许在结构体的末尾定义一个长度未知的数组。此特性比较适合需要动态管理内存的场景。
二、offsetof宏函数
再讲柔性数组数组之前,我们先了解一下offsetof宏函数,对于后面的实验有帮助。
1、作用
计算结构体中某个成员相对于结构体起始地址的偏移量(以字节为单位)。
2、头文件
#include <stddef.h>
3、定义
size_t offsetof(type, member);
4、参数介绍
参数 | 描述 |
type | 结构体类型。 |
member | 结构体中的成员名称。 |
三、实验模拟
1、测试代码
#define TEST_STUDENT_NAME_LEN 8
#define TEST_STUDENT_DESC_LEN 16typedef struct MyStudentSt
{char Name[TEST_STUDENT_NAME_LEN];int Sex;int Age;char Desc[];
}MyStudentSt;Status main()
{LOG_LEVEL = Debug;MyStudentSt *Student = (MyStudentSt *)MyMalloc(sizeof(MyStudentSt) + sizeof(char) * TEST_STUDENT_DESC_LEN);char *Desc = NULL;LOG_FORMAT(Debug,"MyStudentSt = %lu\n",sizeof(MyStudentSt));LOG_FORMAT(Debug,"MyStudentSt.Name = %lu\n",offsetof(MyStudentSt,Name));LOG_FORMAT(Debug,"MyStudentSt.Sex = %lu\n",offsetof(MyStudentSt,Sex));LOG_FORMAT(Debug,"MyStudentSt.Age = %lu\n",offsetof(MyStudentSt,Age));LOG_FORMAT(Debug,"MyStudentSt.Desc = %lu\n",offsetof(MyStudentSt,Desc));strcpy(Student->Desc,"Sun Moon");Desc = (char *)Student + offsetof(MyStudentSt,Desc);LOG_FORMAT(Debug,"MyStudentSt.Desc = '%s'\n",Student->Desc);LOG_FORMAT(Debug,"Desc = '%s'\n",Desc);free(Student);Student = NULL;return SUCCESS_FLAG;
}
2、实验效果
2025-08-27 15:11:33.102338-P[112005]-T[112005]-[Debug]-main : MyStudentSt = 16
2025-08-27 15:11:33.102623-P[112005]-T[112005]-[Debug]-main : MyStudentSt.Name = 0
2025-08-27 15:11:33.102631-P[112005]-T[112005]-[Debug]-main : MyStudentSt.Sex = 8
2025-08-27 15:11:33.102635-P[112005]-T[112005]-[Debug]-main : MyStudentSt.Age = 12
2025-08-27 15:11:33.102638-P[112005]-T[112005]-[Debug]-main : MyStudentSt.Desc = 16
2025-08-27 15:11:33.102641-P[112005]-T[112005]-[Debug]-main : MyStudentSt.Desc = 'Sun Moon'
2025-08-27 15:11:33.102645-P[112005]-T[112005]-[Debug]-main : Desc = 'Sun Moon'
MyStudentSt结构体计算字节数为16,也就是没有计算柔性数组Desc,需考虑字节对齐,我这边是4的倍数,所以正好,没有自动补齐。
打印出每个参数的起始地址的偏移量。
获取到Desc的起始地址的偏移量,Student + 得到的偏移量 = Student->Desc地址,从打印结果一致来看,我们的代码逻辑也是正确的。
Desc = (char *)Student + offsetof(MyStudentSt,Desc);
这里 (char *)Student类型转换很关键,不转化的话加的就不是offsetof算出的字节数了,而是offsetof(MyStudentSt,Desc) * sizeof(MyStudentSt)个字节了。
四、优点与缺点
1、柔性数组的优点
优点 | 详细说明 |
---|---|
内存连续性 | 结构体和数组成员在内存中连续存储,提高缓存局部性和访问效率 |
单次内存分配 | 只需要一次malloc/free操作,简化内存管理 |
减少内存碎片 | 避免多次分配造成的内存碎片问题 |
无指针开销 | 节省存储指针所需的内存(通常4-8字节) |
访问简便 | 直接使用struct->array[index] 语法,无需多级解引用 |
更好的缓存性能 | 连续内存布局有利于CPU缓存预取,减少缓存未命中 |
代码更简洁 | 所有相关数据在一个结构体中,代码更易理解和维护 |
精确内存控制 | 可以精确计算所需内存大小,避免过度分配 |
2、 柔性数组的缺点
缺点 | 详细说明 |
---|---|
标准要求高 | 需要C99或更新标准,不支持C89/C90 |
编译器兼容性 | 某些老旧编译器可能不支持此特性 |
C++兼容性问题 | C++标准不完全支持C风格的柔性数组 |
使用限制严格 | 必须是结构体的最后一个成员,不能有多个柔性数组 |
不能栈上分配 | 必须使用动态内存分配,不能创建栈上的实例 |
需要手动计算大小 | 必须正确计算分配内存的大小,容易出错 |
缺乏边界检查 | C语言不提供数组边界检查,需要开发者自行维护 |
调试工具支持有限 | 某些调试器可能无法正确显示柔性数组内容 |
静态分析挑战 | 一些静态分析工具可能难以处理柔性数组的模式 |
调整大小困难 | 需要重新分配整个结构体来调整数组大小 |