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_96288572几秒前
如何用 Python计算 CR能量指标(中间意愿指标)?
开发语言·python
slimer3 分钟前
11.C 语言 printf 打印函数详解:格式说明符、宽度精度与安全输出
linux·c语言
临床数据科学和人工智能兴趣组32 分钟前
在R语言的生态系统中,字符串处理一直是一个重要且复杂的任务
开发语言·正则表达式·r语言·r语言-4.2.1
蜗牛互联网32 分钟前
Python调用Gemini Structured Outputs实现工单路由门禁
java·开发语言·人工智能·python
SEO_juper1 小时前
给网页做可引用性评分:判断内容够不够让 AI 引用(GEO 工具,附代码)
开发语言·人工智能·爬虫·python·seo·独立站
Wang's Blog1 小时前
Java 项目实战: 交友项目 - 产品定位、功能模块与客户端原型解读
java·开发语言·交友
盛世宏博北京1 小时前
十二防档案库房总线架构设计:恒湿消毒净化一体机 RS485 组网实战笔记
开发语言·笔记·php·恒湿机·档案八防温湿度
雪雪爱冲浪1 小时前
JavaScript 网络爬虫教程(2026)
开发语言·javascript·爬虫
要吃这碗饭2 小时前
JavaScript 网络爬虫教程(2026)
开发语言·javascript·爬虫
czq_26867194872 小时前
Python打卡第40天
开发语言·python