C语言中的柔性数组你了解吗

 更新时间:2023年04月14日 09:34:43   作者:努力学习游泳的鱼  
这篇文章主要为大家详细介绍了C99中的新语法——柔性数组的使用以及优缺点,文中的示例代码讲解详细,具有一定的学习价值,感兴趣的可以了解一下

1.什么是柔性数组

柔性数组就是大小可以变化的数组。

注意跟C99中的变长数组区分开来,变长数组指的是可以使用变量来指定大小,并且不能初始化的数组,比如:

int n = 0;
scanf("%d", &n);
int arr[n]; // 变长数组

柔性数组是在结构体中声明的。它满足:

是结构体的最后一个成员变量。

大小不确定,一般空出来或者用0来填充。

比如:

struct S
{
    char ch;
    double d;
    int arr[];
};

或者:

struct S
{
    char ch;
    double d;
    int arr[0];
};

上面的2中写法是等价的,但是有些编译器只支持其中的一种写法。

在进行如何使用的讲解之前,先来思考一个问题:结构体S的大小是多少?也就是说,sizeof(struct S)是多大?

不同的编译器结果不一定一样。事实上,sizeof(struct S)计算的是结构体中除了变长数组之外的大小,在VS2022,X64环境下,考虑内存对齐,计算出来的结果是16。

2.柔性数组应该如何使用

为了给柔性数组分配空间,应该使用动态内存管理。假设使用malloc来申请空间,我们应该考虑柔性数组的大小,算出总的大小。

以上面的struct S为例。假设我想要数组arr的大小是10个int,那么要开辟的总大小就是除了柔性数组之外的大小+柔性数组的大小。前面提到了,除了柔性数组之外的大小就是sizeof(struct S),而柔性数组的大小是10*sizeof(int),就能如下开辟空间:

struct S* ps = (struct S*)malloc(sizeof(struct S) + 10 * sizeof(int));
if (ps == NULL)
{
    // ...
}
// ...

这样,结构体中的成员数组arr就有了10个int的空间了,我们可以正常的使用这个结构体,只需把成员数组arr当成int arr[10];这样的数组即可。

当然,之所以叫“柔性数组”,这个数组不仅可以指定初始化的大小,也可以改变大小,毕竟是动态内存开辟出来的。只需要使用realloc即可,新的大小的计算方式和前面一样,也是sizeof(struct S)+柔性数组新的大小。比如,如果我想把数组的大小扩大成原来的2倍,可以这么写:

struct S* tmp = (struct S*)realloc(ps, sizeof(struct S) + 20 * sizeof(int));
if (tmp == NULL)
{
    // ...
}
else
{
    ps = tmp;
}
// ...

这样,ps指向的结构体的最后一个成员数组的容量就变成了20个int。

记住,所有动态申请的内存,在使用结束后,都需要调用free释放。比如以上的结构体s使用完后,需要free(ps);。

3.柔性数组的替代方案及内存分布对比

其实,我们可以不使用柔性数组实现类似的效果。分析一下,以上的结构体struct S的特点是:

使用动态内存管理,在堆区上申请空间。

有一个成员数组,可以动态的改变大小。

如果不使用柔性数组,而是直接使用动态内存管理的思路,也是可以的。比如:

struct S
{
    char ch;
    double d;
    int* arr;
};

先malloc出一个结构体出来。

struct S* ps = (struct S*)malloc(sizeof(struct S));
if (ps == NULL)
{
    // ...
}
// ...

接着再malloc出一块空间,交给arr管理。假设开辟出10个int的空间。

ps->arr = (int*)malloc(10 * sizeof(int));
if (ps->arr == NULL)
{
    // ...
}
// ...

如果想要扩容,直接对ps->arr进行realloc即可。比如,把arr指向的空间扩容成原来的2倍。

int* tmp = (int*)realloc(ps->arr, 20 * sizeof(int));
if (tmp == NULL)
{
    // ...
}
else
{
    ps->arr = tmp;
}

这块空间使用结束后,记得释放空间。注意一定要先释放arr,再释放ps,因为如果先释放ps,结构体内的arr就成了野指针,就找不到原来arr指向的空间了,形成了内存泄漏。

free(ps->arr);
ps->arr = NULL;
free(ps);
pf = NULL;

使用和不使用柔性数组的内存分布对比:

对于柔性数组,动态申请的内存只存放了一个完整的结构体,所有的成员变量(数组)整体上是连续的空间。

不使用柔性数组实现类似的效果,需要先动态开辟出一个结构体,再开辟出另外一块空间,用结构体的一个成员变量(一个指针)来管理。相当于malloc了2次,有2块独立的空间。

4.柔性数组有哪些优点

既然不使用柔性数组也能实现类似的效果,为什么还要使用柔性数组呢?因为柔性数组有以下的2个优点:

  • 使用柔性数组只用malloc一次,free一次。如果不使用柔性数组,要malloc两次,free两次。在使用上,柔性数组更方便、更简单。
  • 由于柔性数组在整体上只开辟了一块连续的空间,根据局部性原理,缓存的命中率更高,增加了效率(虽然其实也没增加多少)。

总结

柔性数组指的是结构体的最后一个成员变量是一个大小不确定的数组,数组的大小会空出来或者给0。

柔性数组的使用需要进行动态内存管理,具体的大小计算是sizeof(结构体类型)+柔性数组大小。

不使用柔性数组也可以实现类似的效果,但是柔性数组是有它独特的优势的。如果使用柔性数组,只需要malloc和free一次;如果不使用柔性数组,对于结构体需要malloc一次,结构体内的指针又要malloc一次,总共需要malloc两次,free两次。比较起来,使用柔性数组更简单、方便,而且整体是一块连续的空间,根据局部性原理,效率更高。

到此这篇关于C语言中的柔性数组你了解吗的文章就介绍到这了,更多相关C语言柔性数组内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C语言控制台打砖块小游戏

    C语言控制台打砖块小游戏

    这篇文章主要为大家详细介绍了C语言控制台打砖块小游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-05-05
  • C语言实现动态版通讯录的示例代码

    C语言实现动态版通讯录的示例代码

    这篇文章主要为大家详细介绍了如何利用C语言实现一个简单的动态版通讯录,文中的示例代码讲解详细,对我们学习C语言有一定帮助,需要的可以参考一下
    2022-08-08
  • c语言中的局部跳转及全局跳转功能

    c语言中的局部跳转及全局跳转功能

    本文介绍了C语言中的goto语句,以及如何使用setjmp和longjmp实现跨函数的跳转,详细讲解了setjmp和longjmp的使用方法和注意事项,以及使用这种全局跳转后变量状态的不确定性,感兴趣的朋友一起看看吧
    2024-09-09
  • C++中getline()、gets()等函数的用法详解

    C++中getline()、gets()等函数的用法详解

    这篇文章主要介绍了C++中getline()、gets()等函数的用法,本文给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-02-02
  • C++与Lua交互原理实例详解

    C++与Lua交互原理实例详解

    这篇文章主要介绍了C++与Lua交互原理实例详解,有感兴趣的同学可以研究下
    2021-02-02
  • C++直接初始化与复制初始化的区别深入解析

    C++直接初始化与复制初始化的区别深入解析

    这篇文章主要介绍了C++直接初始化与复制初始化的区别深入解析,是很多C++初学者需要深入了解的重要概念,需要的朋友可以参考下
    2014-09-09
  • C语言每日练习之选择排序

    C语言每日练习之选择排序

    本篇文章主要介绍了 C语言的选择排序,这里提供代码实例以便大家理解,通过本文,更好的理解排序算法
    2021-11-11
  • C++选择文件夹代码的封装

    C++选择文件夹代码的封装

    这篇文章主要介绍了C++选择文件夹代码的封装,实例展示了将选择文件夹功能代码封装为一个类并对其进行实例化调用的过程,对于学习C++程序设计有不错的参考价值,需要的朋友可以参考下
    2014-10-10
  • C++中的可移植性和跨平台开发教程详解

    C++中的可移植性和跨平台开发教程详解

    这篇文章主要为大家介绍了C++中的可移植性和跨平台开发教程详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-05-05
  • 简介C/C++预处理器的一些工作

    简介C/C++预处理器的一些工作

    这篇文章主要介绍了C/C++预处理器的一些工作,有助于理解编译器底层的工作流程,需要的朋友可以参考下
    2015-07-07

最新评论