经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » C 语言 » 查看文章
C语言柔性数组详解
来源:jb51  时间:2021/10/11 9:22:18  对本文有异议

前言

可能大家第一眼看到这个标题会有点懵,到底什么是柔性数组,我怎么从来没听说过?但柔性数组确实是存在的,也经常会出现在一些公司的面试题中,今天就跟着笔者来学习一下柔性数组吧。

提示:以下是本篇文章正文内容,下面案例可供参考

在这里插入图片描述

一、柔性数组是什么?

C99中,结构体中的最后一个元素允许是未知大小的数组,这就叫作柔性数组,for example:

  1. struct st_type
  2. {
  3. int i;
  4. int a[0];//柔性数组成员,也可以写int a[];
  5. };

结构体成员a数组,它的数组大小是没有确定的,将来如果需要可以大也可以小。
有些编译器支持a[0]这种写法,有些编译器支持a[ ]这种写法,具体取决编译器。

二、柔性数组的特点

1.结构体中柔性数组成员前面必须至少有一个其他成员

示例如下:

  1. struct st_type
  2. {
  3. int i;
  4. int a[0];//柔性数组成员,也可以写int a[];
  5. };

比如上面这段代码,如果你要创建一个柔性数组a,前面必须创建一些别的成员

2.sizeof返回的这种结构大小不包括柔性数组的内存

示例如下:

  1. struct st_type
  2. {
  3. int i;//4字节
  4. int a[0];//柔性数组成员,也可以写int a[];
  5. //因为是柔性数组,无法确认a占几个字节
  6. };
  7. int main()
  8. {
  9. printf("%d\n", sizeof(struct st_type));//打印4
  10. return 0;
  11. }

这里计算包含柔性数组的结构体大小,因为柔性数组本身是无法确定有几个字节的,所以计算整体结构体大小时,会省略柔性数组的计算。

3.包含柔性数组成员的结构用malloc()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小

ps:除了malloc函数,realloc、calloc等动态内存开辟的函数也需要类似的操作

比如说我现在要数组a里面有10个元素,现在进行malloc一下

示例如下:

  1. #include<string.h>
  2. #include<errno.h>
  3. struct st_type
  4. {
  5. int i;//4字节
  6. int a[0];//柔性数组成员,也可以写int a[];
  7. };
  8. int main()
  9. {
  10. //假设我现在需要a里有10个元素
  11. struct st_type*ps=(struct st_type*)malloc(sizeof(struct st_type) + 10 * sizeof(int));
  12. if (ps == NULL)//由于空间可能不够开辟导致malloc开辟失败,开辟失败会返回空指针
  13. {
  14. printf("%s\n", strerror(errno));
  15. return -1;//程序出问题后,跳出程序
  16. }
  17. //开辟成功
  18. int j = 0;
  19. for (j = 0;j < 10;j++)
  20. {
  21. ps->a[j] = j;
  22. }
  23. for (j = 0;j < 10;j++)
  24. {
  25. printf("%d ", ps->a[j]);//打印0-9
  26. }
  27. printf("\n");
  28. //如果想继续用柔性数组a进行打印
  29. //比如现在a里只有10个元素,我用完10个了,我还要继续来10个,用realloc追加
  30. struct st_type*ptr=realloc(ps, sizeof(struct st_type) + 20 * sizeof(int));//ps:realloc第二个参数是调整后的整体大小
  31. if (ptr == NULL)
  32. {
  33. printf("扩容失败\n");
  34. return -1;
  35. }
  36. else
  37. {
  38. ps = ptr;
  39. }
  40. //扩容成功
  41. int k = 0;
  42. for (k = 10;k < 20;k++)
  43. {
  44. ps->a[k] = k;
  45. }
  46. for (j = 0;j < 20;j++)
  47. {
  48. printf("%d ", ps->a[j]);//打印0-19
  49. }
  50. //释放空间
  51. free(ps);
  52. ps = NULL;
  53. return 0;
  54. }

在这里插入图片描述

在这里插入图片描述

我们这里需要数组a里有10个元素,那我们malloc的时候要对结构体里的整形i先开辟4个字节,然后为整形数组a再开辟40个字节,然后malloc函数返回开辟空间的起始地址,赋给truct st_type * 类型的ps指针。

malloc(sizeof(struct st_type) + 10 * sizeof(int))这个操作等价于struct st_type类型创建一个变量所占空间,只不过是用malloc来开辟

你改变数组a大小,追加空间时,realloc(ps, sizeof(struct st_type) + 20 * sizeof(int)),realloc的第一个参数仍然是ps,因为你当时是用malloc一次开辟出的一块空间,你是不能单独调整数组a的空间的

三、柔性数组的优点

柔性数组就是对一块空间实现动态开辟嘛,那我们之前也讲过指针来动态内存开辟,我们来看一段代码来对比一下这两种方法:

  1. //用指针也可以做到a指向的空间动态变化
  2. struct st_type
  3. {
  4. int i;//4字节
  5. int *a;//4字节,这里计算结构体大小恰好是8字节
  6. };
  7. int main()
  8. {
  9. struct st_type*ps = (struct st_type*)malloc(sizeof(struct st_type));
  10. ps->i = 100;
  11. ps->a = (int*)malloc(10 * sizeof(int));//a指向40个字节的空间,该空间由int*进行管理
  12. int j = 0;
  13. for (j = 0;j < 10;j++)
  14. {
  15. ps->a[j] = j;//a[j]=*(a+j)
  16. }
  17. for (j = 0;j < 10;j++)
  18. {
  19. printf("%d", ps->a[j]);
  20. }
  21. //a指向的空间不够了,希望调整大小
  22. int *ptr = (int*)realloc(ps->a, 20 * sizeof(int));
  23. if (ptr == NULL)
  24. {
  25. printf("扩容失败");
  26. return -1;
  27. }
  28. else
  29. {
  30. ps->a = ptr;
  31. }
  32. //使用...
  33. //释放
  34. free(ps->a);
  35. ps->a = NULL;
  36. free(ps);
  37. ps = NULL;
  38. }

这里需要注意的是,在释放空间时,你要先释放指针a指向的空间,然后释放结构体指针

在这里插入图片描述

如上图,我们结构体指针ps开辟一块空间,空间里存放整形i和整形指针a,a又malloc(后续如果需要还可以realloc追加)一块空间,如果你先释放掉ps,a就没了,你就没法找到a指向的那块空间了。

还是那个生动的例子 就比如a是一个警察头子,malloc开辟的空间是卧底,只有a知道那个卧底,你现在警察头子死了,再也没法证明卧底是卧底了,也就是说a消失后,没办法再对开辟的空间进行释放,这时就会造成内存泄露,指针实现动态内存调整是需要对指针释放讲解一定的顺序性的

这里对比柔性数组,柔性数组和上述的指针都可以实现一块空间大小的调整,但是柔性数组有两个好处:
第一个好处是:方便内存释放

如果我们的代码是在一个给别人用的函数中,你在里面做了二次内存分配,并把整个结构体返回给用户。用户调用free可以释放结构体,但是用户并不知道这个结构体内的成员也需要free,所以你不能指望用户来发现这个事。以上,如果我把结构体的内存及其成员要的内存一次性分配好,并返回给用户一个结构体指针,用户做一次free就可以把所有内存都释放掉,并且不用考虑前面说的释放的顺序。

第二个好处是:加快访问速度

连续的内存有益于提高访问速度,也有益于减少内存碎片。

ps:内存碎片如下图

在这里插入图片描述

操作系统给我们一块内存,我们在进行malloc时,不一定就是一块连着一块的,上图的空白部分就是内存碎片,有些类似我们裁剪布料,一些剩余的边角料这样。

总结

本文介绍了柔性数组的定义、其三个使用特点,及其对比指针实现动态内存的优势,并对其进行了具体举例

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注w3xue的更多内容!

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号