谈柔性数组在结构体中的作用

简介: 可能你从来没有听过柔性数组这个概念,但是它确实是存在的。在C99中,结构体中最后一个成员允许是大小未知的数组,这就叫做【柔性数组】成员。

该文章将介绍柔性数组是什么,有什么作用以及如何使用柔性数组,帮助你更好的理解结构体中的细节。


1.柔性数组


可能你从来没有听过柔性数组这个概念,但是它确实是存在的。


在C99中,结构体中最后一个成员允许是大小未知的数组,这就叫做【柔性数组】成员。


比如:


struct Stu
{
  int n;
  char c;
  int arr[];这个就是柔性数组成员
};


有些编译器会报错无法编译可以改成


struct Stu
{
  int n;
  char c;
  int arr[0];
};


2.柔性数组的特点:


  • 结构体中的柔性数组成员前面必须至少要有一个其他成员。
  • sizeof返回的这个结构体大小不包含柔性数组的内存
  • 包含柔性数组的结构体要用malloc()函数进行内存的动态分配,并且分配的大小应该大于结构体的大小,以适应柔性数组的预期大小


例如:


struct Stu
{
  int n;
  int arr[];柔性数组
};
int main()
{
  printf("%d", sizeof(struct Stu));
  //这个结构体大小不包含柔性数组的内存,相当于这个给结构体中只有int n;成员,大小也就是4
  return 0;
}


3.柔性数组的使用


struct S
{
  int a;
  char arr[];//不设置大小--柔性数组,结构体中最后一个,前面至少要求一个成员
  //这个结构体大小是除柔性数组以外的成员计算得到大小
  //柔性数组的空间使用需要malloc开辟
};
int main()
{//使用malloc函数给结构体变量开辟内存,sizeof(struct S)是为结构体中除柔性数组外成员分配空间,而后面的则是为柔性数组开辟空间
  //最后都把开辟的空间转换为结构体类型,用结构体指针来接收
  struct S*p=(struct S*)malloc(sizeof(struct S) + 10 * sizeof(char));
  if (p == NULL)//判断一下
  {
    perror("malloc");
    return 1;
  }
  p->a = 100;//访问变量
  int i = 0;
  for (i = 0; i < 10; i++)//给数组分配了10个整形的空间
  {
    p->arr[i] = '6';//使用这块空间
  }
  for (i = 0; i < 10; i++)
  {
    printf("%c ", p->arr[i]);//打印出来
  }
  //想要给柔性数组增容 -----p指向的是原先的空间大小的指针,要想开辟更大的空间,在原基础上加就可以了
  struct S*ptr=(struct S*)realloc(p,sizeof(struct S) + 20 * sizeof(char));
  if (ptr != NULL)//还是要判断一下
  {
    p = ptr;
  }
  else
  {
    perror("realloc");
    return 1;
  }
  //释放
  free(p);
  p = NULL;
  return 0;
}//


这样的柔性数组arr相当于先获得10个整形大小的连续空间,后来又增容到20个整形的连续空间。


4.柔性数组的优势


上面的代码就是将结构体中一个数组的大小可以随意变化。也可以这样写:


struct S
{
  int a;
  int* arr;//成员是指针不是柔性数组
  //用malloc使这个指针指向一块大小可以是自己改变的
};
int main()
{
  struct S *p = (struct S*)malloc(sizeof(struct S));//给结构体变量申请一块空间,用结构体指针接收
  p->a = 100;//访问结构体成员
  p->arr = (int*)malloc(10 * sizeof(int));//给指针arr指向的地方申请10个整形空间
  int i = 0;
  for (i = 0; i < 10; i++)
  {
    p->arr[i] = '6';//使用malloc为arr开辟的空间
  }
  for (i = 0; i < 10; i++)
  {
    printf("%c ", p->arr[i]);
  }
  //增容
  int *ptr=(int*)realloc(p->arr, 20 * sizeof(int));
  if (ptr == NULL)//判断一下
  {
    perror("realloc");
    return 1;
  }
  free(p->arr);//必须先释放arr开辟的空间再释放p开辟的空间,从里到外释放
  p->arr = NULL;
  free(p);
  p = NULL;
  return 0;
}


上面代码1和代码2都可以完成相同的功能,但代码1的实现有两个好处:


第一个好处是:方便内存释放


可以明显的看出来代码2比代码1,多开辟空间也多释放空间。


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


第二个好处是:有利于提高访问速度


频繁的开辟空间会形成大量的内存碎片而连续的内存有益于提高访问速度,减少内存碎片。


如果想了解更多的知识这里有陈皓大佬的一篇文章里面也是讲的柔性数组,供参考

C语言结构体里的成员数组和指针

相关文章
|
1月前
|
编译器 Linux C语言
详解结构体内存对齐及结构体如何实现位段~
详解结构体内存对齐及结构体如何实现位段~
|
7月前
|
编译器 C语言 C++
C/C++内存对齐规则(结构体、联合体、类)
C/C++内存对齐规则(结构体、联合体、类)
|
1月前
|
存储 编译器 Linux
【C语言】自定义类型:结构体深入解析(二)结构体内存对齐&&宏offsetof计算偏移量&&结构体传参
【C语言】自定义类型:结构体深入解析(二)结构体内存对齐&&宏offsetof计算偏移量&&结构体传参
|
1月前
|
存储 编译器 Linux
匿名结构体类型、结构体的自引用、结构体的内存对齐以及结构体传参
匿名结构体类型、结构体的自引用、结构体的内存对齐以及结构体传参
|
8天前
结构体\结构体指针
结构体\结构体指针
8 3
|
1月前
|
存储
结构体和结构体指针的区别
结构体和结构体指针的区别
|
1月前
|
存储 网络协议 编译器
【C语言】自定义类型:结构体深入解析(三)结构体实现位段最终篇
【C语言】自定义类型:结构体深入解析(三)结构体实现位段最终篇
|
10月前
|
C语言
C语言知识点:结构体,枚举,联合体
C语言知识点:结构体,枚举,联合体
54 0
|
11月前
|
存储
结构体基础
结构体基础
|
存储 编译器 C语言
【C语言】玩转结构体(声明、引用、初始化、内存对齐、传参、位段)
前言 大家好,本文主要深度讲解关于结构体的使用及细节,收录到C—语法专栏,此专栏定期更新C语言语法方面的知识,都是比较详细的,自己复习的同时也希望能帮助到大家,如果有不对或者不足的地方欢迎评论区补充。 🏡个人主页:悲伤的猪大肠9的博客-C语言领域博主 ✨如果文章对你有帮助记得点赞收藏关注哦!!✨ 🌈重要的不是成功,而是奋斗的过程 一、结构体 之前我们学过整形(short,int),浮点型(float,double),字符型,还有数组(存储相同类型的数据),但在实际问题中,这些类型显然不够,如果我们想表示一个学生的信息,就会有姓名、年龄、分数。。。这时候就需要一个可以存储不同类型数据