C语言中的柔性数组

C语言中,为了实现变长结构体,引入了柔性数组,既省内存又比指针方案更高效更安全。本文将主要来认识柔性数组。

一、柔性数组的定义

C99中,结构中的最后一个元素可以是一个大小为0数组,这个数组就是柔性数组。

定义方式如下:

c 复制代码
struct struct_arr{
    int a;
    int b[];//柔性数组成员,有的编译器要求写成 int b[0]
};

柔性数组"int b\[\]"还是"int b0"主要看编译器,有的编译器两种都支持。

上面的数组名b,代表一个地址标签 ,(类似struct这个标签),是整个结构体的延伸部分的一个别名,不占据存储空间。

如果把b\[\]处换为"int *b",对于指针b需要为它开辟一个8字节的空间(前提64位的操作系统)。两者的差距可参考下面实际运行结果:

c 复制代码
#include<stdio.h>
#include<windows.h>
#include<math.h>

struct struct_arr0{
    int a;
    int b[];
};
struct struct_arr1{
    int c;
    int *d;
};
int main(){
    SetConsoleOutputCP(65001);
    SetConsoleCP(65001);
    struct struct_arr0 sa0;  
    struct struct_arr1 sa1;
    printf("struct_arr0结构体大小:%zu字节\n",sizeof(sa0)); 
    printf("struct_arr1结构体大小:%zu字节\n",sizeof(sa1));  
    return 0;
} 

运行结果如下:

对比项 柔性数组 指针
申请/释放次数 一次malloc,一次free 要两次malloc/free
内存情况 内存连续,访问更快(CPU缓存友好),序列化/网络传输方便 数据和结构体不连续,缓存不友好
潜在风险 不会泄漏 容易内存泄漏、野指针
缺点 数组长度分配以后不能动态改变 可以后期重新realloc改变数据长度

二、柔性数组的使用

如上面的b\[\],在a后面就是b,后面我们可以使用malloc申请一段任意大小的空间,用b去访问那段空间(即:操作灵活,同一个类型的结构体指针,可以操作的空间大小可变)

c 复制代码
 #include<stdio.h>
#include<windows.h>
#include<math.h>

struct struct_arr0{
    int a;
    int b[];
};
struct struct_arr1{
    int c;
    int *d;
};
int main(){
    SetConsoleOutputCP(65001);
    SetConsoleCP(65001);
    struct struct_arr0 *sa1=malloc(sizeof(struct struct_arr0)+2*sizeof(int));//此时b的空间大小就是8字节
    sa1->a=1000;
    *(sa1->b)=5;
    *(sa1->b+1)=100;
    printf("=====柔性数组====\n");
    printf("结构体-柔性数组中a:%d\n",sa1->a);
    printf("结构体-柔性数组中b[0]里存储的是:%d\n",*(sa1->b)); 
    printf("结构体-柔性数组中b[1]里存储的是:%d\n",*(sa1->b+1));  
    printf("sa1结构体的首地址是:%p\n",(void *)sa1);
    printf("结构体-柔性数组中a地址的是:%p\n",(void *)&(sa1->a)); 
    printf("结构体-柔性数组中b地址的是:%p\n",(void *)&sa1->b[0]); 
    printf("结构体-柔性数组中b[1]里地址的是:%p\n",(void *)&sa1->b[1]);

    struct struct_arr1 *sa2=malloc(sizeof(struct struct_arr1));//第一次malloc,结构体本身
    sa2->d=malloc(2*sizeof(int));//第二次malloc,d指向独立内存
    sa2->c=2000;
    sa2->d[0]=8;
    sa2->d[1]=300;
    printf("=====指针版本====\n");
    printf("结构体-指针中c:%d\n",sa2->c);
    printf("结构体-指针中d[0]里存储的是:%d\n",sa2->d[0]); 
    printf("结构体-指针中d[1]里存储的是:%d\n",sa2->d[1]);  

    printf("sa2结构体的首地址是:%p\n",(void *)sa2);
    printf("结构体-指针中c地址的是:%p\n",(void *)&(sa2->c)); 
    printf("结构体-指针中d的地址的是:%p\n",(void*)sa2->d); 
    printf("结构体-指针中d[0]内容的地址的是:%p\n",(void *)&sa2->d[0]);
    printf("结构体-指针中d[1]内容的地址的是:%p\n",(void *)&sa2->d[1]);
    free(sa1);
    free(sa2->d);
    free(sa2);
    return 0;
}  

运行结果:

sa1->b:b0、b1全部来自一次malloc,释放free(sa1)即可,只free一次

sa2->d:仅存一个地址,d指向另外一个malloc出来的独立堆块,需要malloc2次,释放要先free(sa2->d),再free(sa2),忘记释放d直接free(sa2)直接内存泄漏

二、柔性数组的注意事项

1、柔性数组成员必须放在结构体最后

三、对比其他语言

柔性数组只有C、C++有,在嵌入式、底层开发场景使用

C++:不推荐柔性数组,用std:vector

Java:没有柔性数组,用List、动态数组

python/go:自带动态数组,不需要这种语法

总之:柔性数组是C语言为了高效处理"变长结构体"而设计的特性

相关推荐
2601_962071573 小时前
【Java报错已解决】org.springframework.beans.factory.BeanCreationException
java·开发语言
淡海水4 小时前
07-04-并发-ConcurrentBag-T-工作窃取WorkStealing算法
开发语言·算法·c#·bag·concurrent·workstealing
CS_Zero5 小时前
C语言sizeof是函数吗?
c语言·开发语言
-今昭-5 小时前
《V2V 迁移实战:将 VMware 虚拟机转为 OpenStack 可用 Glance qcow2 镜像》
开发语言·python
黑马程序员毕设7 小时前
基于Java的医院药品管理系统的优化设计与实现
java·开发语言·spring boot·小程序·架构·课程设计·毕设
_Twink1e8 小时前
openJiuwen 实训营 Day 1 · WorkSwarm 入门教程
开发语言·c++·openjiuwen
问天_观心8 小时前
大模型微调学习(一)
开发语言·人工智能·学习·语言模型·github
阿里嘎多学长8 小时前
2026-09-03 GitHub 热点项目精选
开发语言·程序员·github·代码托管
Navigator_Z8 小时前
LeetCode //C - 1224. Maximum Equal Frequency
c语言·算法·leetcode
Navigator_Z9 小时前
LeetCode //C++ - 1226. The Dining Philosophers
c语言·算法·leetcode