☰
柔性数组的解析
2026/10/20 10:50:44 网站建设 项目流程

目录

摘要:

一:语法规则

1:书写规则

2:柔性数组特点

二:优点

1:柔性数组的使用

2:指针替换柔性数组

3:柔性数组的优点


摘要:

本博客旨在介绍何为柔性数组,以及其的书写语法规则,最后对比指针实现的方式来体现柔性数组的优点.....


一:语法规则

1:书写规则

也许你从来没有听说过柔性数组(flexible array)这个概念,但是它确实是存在的,在C99 中,结构中的最后⼀个元素允许是未知大小的数组,这就叫做『柔性数组』成员。所以柔性数组是结构体的一个成员变量!

书写规则:结构中的柔性数组成员前面必须至少还存在一个其他成员变量

struct st_ty1pe { int i; int a[0];//柔性数组成员 };

若上述写法编译器报错⽆法编译,则可以改成下面写法(去掉[ ]中数字):

struct st_type { int i; int a[];//柔性数组成员 };

2:柔性数组特点

柔性数组的特点:

•sizeof 返回的这种结构大小不包括柔性数组的内存。
•用 malloc() 为含柔性数组成员的结构体动态分配内存时,分配的大小应大于结构体本身的大小,以容纳柔性数组的预期长度。
typedef struct st_type { int i; int a[0];//柔性数组成员 }type_a; int main() { printf("%d\n", sizeof(type_a));//输出的是4 return 0; }

解释:柔性数组之所以叫柔性,就是因为其的大小是可变的,所以sizeof当然无法精确计算出柔性数组的大小,只能计算其他结构体成员变量的大小!

此外!:柔性数组要想发挥出自己的作用,是必须和内存函数互相结合的!如果不用malloc、calloc、free等内存函数,柔性数组就毫无用处,甚至会导致程序崩溃!!

⚠️:错误的用法(完全没有用内存函数配合):

struct FlexArray { int len; int data[]; // 柔性数组 }; int main() { struct FlexArray arr; // 错误!如何为 data 分配内存? arr.data[0] = 10; // 危险!data 没有实际内存空间 return 0; }

解释:sizeof(struct FlexArray)只包含int len的大小。data没有空间。访问arr.data[0]会写入未知内存,导致段错误!

总结:

没有内存函数,柔性数组就没有栖身的内存空间,只是一个无法使用的幽灵字段;只有通过malloc/realloc一次性分配头部+数据区的连续内存,柔性数组才能真正成为一个长度可变、管理方便、缓存友好的动态数组。所以要想给柔性数组开辟空间,必须结合内存函数


二:优点

1:柔性数组的使用

代码逻辑:利用结构体中的柔性数组成员,结合malloc和realloc实现一个可动态扩容的数组,最开始柔性数组存放1 2 3 4 5,然后扩容再存放6,7,8,9,10,最后打印观看~

#include <stdio.h> #include <stdlib.h> // 定义结构体 S struct S { int n; // 普通成员变量 int arr[]; // 柔性数组成员:不占结构体大小,用于后续动态扩展 }; int main() { // 1. 动态分配内存 // 这里分配了结构体本身的大小 + 20字节(足够存5个int) struct S* ps = (struct S*)malloc(sizeof(struct S) + 20); if (ps == NULL) return 1; // 检查分配是否成功 ps->n = 100; // 初始化普通成员 int i = 0; // 2. 使用初始分配的空间(前5个元素) for (i = 0; i < 5; i++) { ps->arr[i] = i + 1; // 赋值 1, 2, 3, 4, 5 } // 3. 扩容内存 // 原空间不够用了,需要再加40字节(总共能存10个int) // 注意:这里总大小 = 结构体大小 + 20(旧) + 40(新加) struct S* tmp = (struct S*)realloc(ps, sizeof(struct S) + 40); // 检查 realloc 是否成功 if (tmp == NULL) { perror("realloc"); return 1; } // 更新指针指向新内存块 ps = tmp; // 4. 使用扩容后的空间(后5个元素) for (i = 5; i < 10; i++) { ps->arr[i] = i + 1; // 赋值 6, 7, 8, 9, 10 } // 5. 输出结果 // 验证数据是否连续且正确 for (i = 0; i < 10; i++) { printf("%d ", ps->arr[i]); } // 6. 释放内存 free(ps); ps = NULL; return 0; }

解释:malloc申请空间的时候,多申请一点,结构体内被常规的成员变量占据空间之后,剩余的空间都属于柔性数组,可以直接以下标的形式进行使用空间,当柔性数组需要更大空间时,则增容即可,最后free释放结构体指针,则会将整个结构体变量,包括其中的柔性数组一起释放~

2:指针替换柔性数组

我们也可以用指针来替换柔性数组,虽然实现方式差不多,但是相比柔性数组而言,指针的方式更繁琐,也更容易出问题,没有柔性数组省事!

指针实现如下:

#include <stdio.h> #include <stdlib.h> // 修改后的结构体:使用指针代替柔性数组 struct S_Ptr { int n; int* arr; // 这是一个指针,通常占 4 或 8 字节 }; int main() { // 1. 分配结构体本身的内存 struct S_Ptr* ps = (struct S_Ptr*)malloc(sizeof(struct S_Ptr)); if (ps == NULL) return 1; ps->n = 100; // 2. 单独为 arr 分配内存 (模拟原来的 +20 字节) ps->arr = (int*)malloc(20); if (ps->arr == NULL) return 1; int i = 0; for (i = 0; i < 5; i++) { ps->arr[i] = i + 1; } // 3. 扩容 arr // 必须单独对 ps->arr 进行 realloc,而不是对 ps 进行 realloc int* tmp = (int*)realloc(ps->arr, 40); // 注意:这里逻辑变成了总大小40,还是增量40取决于需求,这里假设扩容到40 if (tmp == NULL) { perror("realloc"); return 1; } ps->arr = tmp; for (i = 5; i < 10; i++) { ps->arr[i] = i + 1; } // 4. 输出 for (i = 0; i < 10; i++) { printf("%d ", ps->arr[i]); } // 5. 释放内存 (必须释放两次!) free(ps->arr); // 先释放内部数组 free(ps); // 再释放结构体 return 0; }

3:柔性数组的优点

通过上面的代码实现,我们可以清晰地看到柔性数组(int arr[])相对于指针(int *arr)的有三方面的核心优势:

①:内存分配次数与效率(主要优势)

  • 指针方法:需要2 次malloc。

    • 一次分配结构体。

    • 一次分配数组内存。

    • 频繁的内存分配会导致内存碎片。

  • 柔性数组:只需要1 次malloc。

    • 结构体和数组数据在内存中是连续存储的。

    • 减少了系统调用次数,效率更高。

②:内存释放的便捷性

  • 指针方法:需要2 次free。

    • 必须先free(ps->arr),再free(ps)。(顺序颠倒会出错,导致找不到arr)

    • 如果忘记释放其中任何一个,都会造成内存泄漏。

  • 柔性数组:只需要1 次free。

    • free(ps)会一次性释放整块内存,管理更简单,不易出错。

③:访问速度与局部性

  • 指针方法:ps指向的内存和ps->arr指向的内存,在物理地址上可能相隔很远(不连续)。这会导致 CPU 缓存命中率降低。

  • 柔性数组:数据紧跟在结构体后面,具有极好的局部性原理优势,访问速度通常更快。

总结:

虽然使用int *arr也能实现类似的功能,但柔性数组提供了一种更优雅、更高效的方式来管理“头结构 + 变长数据”的场景(例如网络数据包、动态字符串等)。它通过一次性分配连续内存,解决了内存碎片问题,并简化了内存管理逻辑。


📌 [ 作者 ] shylyly
📃 [ 首次发布 ] 2026.5.17
❌ [ 最新修改 ] 2026.10.19
📜 [ 声明 ] 由于笔者水平有限,文中难免有疏漏或不妥之处,还望读者不吝赐教。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询