动态内存分配(3)——柔性数组

简介: 动态内存分配(3)——柔性数组

前言:

       以前我们所学数组(包括变长数组),在数组声明的时候,就必须指定数组的大小,它所需要的内存在编译时分配。但是有时候需要的数组大小在程序运行的时候才能知道,该怎么办呢?这就是我们今天要来学习的新内容——柔性数组。


一、柔性数组的介绍

       柔性数组(flexible array):在C99中,结构的最后一个元素允许是未知大小的数组,这就叫做柔性数组成员。

示例:

typedef struct st_type
{
  int i;
  int a[0];//表示数组的大小是未知的——柔性数组成员
}type_a;

有些编译器会报错无法编译可以改成将a[0]改为a[]:

typedef struct st_type
{
  int i;
  int a[];//表示数组的大小是未知的——柔性数组成员
}type_a;

注意:

       1、柔性数组是C99中引入的,支持C99柔性数组的编译器才可使用柔性数组(VS集成开发支持)。

       2、柔性数组成员必须是结构体的成员,还必须是结构中的最后一个成员。

       3、柔性数组成员在声明的时候数组大小不写和数组大小为0是一样的(arr[]和arr[0]),表示数组的大小是未知的。

二、柔性数组的特点

特点:

       1、结构中的柔性数组成员前面必须至少有一个其他成员(我们想分配一个不定长的数组,所以定义一个结构体,最少有两个成员。一个代表数组的长度,一个是柔性数组成员)。

       2、sizeof返回的这种结构大小不包括柔性数组的内存(零长度的数组存在于结构体中,但是不占结构体的大小,可理解为一个没有内容的占位标识,直到我们给结构体分配了内存,这个占位标识才变成一个有长度的数组)。

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

讲解:

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

       代码演示:

typedef struct st_type
{
  int i;//必须至少有一个其他成员——数组长度
  int a[];//表示数组的大小是未知的——柔性数组成员
}type_a;

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

       代码演示:

#include<stdio.h>
typedef struct S
{
  int i;//必须至少有一个其他成员
  int a[];//表示数组的大小是未知的——柔性数组成员
}S;
int main()
{
  //计算结构体的大小并将其打印
  printf("%d\n", sizeof(S));
  return 0;
}

运算结果:

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

代码演示:

#include<stdio.h>
#include<stdlib.h>
typedef struct S
{
  int i;//必须至少有一个其他成员
  int a[];//表示数组的大小是未知的——柔性数组成员
}S;
int main()
{
  //使用柔性数组成员的结构,使用malloc进行内存分配,
  //并且分配的内存应该大于结构的大小。
  //给柔性数组开辟10个整形的空间
  struct S* ps = (struct S*)malloc(sizeof(struct S) + 10 * sizeof(int));
  return 0;
}

   图示:


三、柔性数组的使用

       代码演示1:使用柔性数组——结构体中数据是连续的

#include<stdio.h>
#include<stdlib.h>
typedef struct S
{
  int i;//必须至少有一个其他成员
  int a[];//表示数组的大小是未知的——柔性数组成员
}S;
int main()
{
  //给柔性数组成员开辟10个整形的空间
  struct S* ps = (struct S*)malloc(sizeof(struct S) + 10 * sizeof(int));
  //判断是否开辟成功
  if (NULL == ps)
  {
    //打印错误信息
    perror("malloc");
    return 1;
  }
  //使用
  ps->i = 10;
  int i = 0;
  for (i = 0; i < 10; i++)
  {
    ps->a[i] = i;
    printf("%d ", ps->a[i]);
  }
  //增容,在添加10个整形
  struct S* ptr = (struct S*)realloc(ps, sizeof(struct S) + 20 * sizeof(int));
  if (ptr != NULL)
  {
    ps = ptr;
        ps-> i = 20;
  }
  else
  {
    perror("realloc");
    //增容失败,释放之前开辟的
    free(ps);
    ps = NULL;
    return 1;
  }
  //再次使用(略)
  //释放增容成功
  free(ps);
  ps = NULL;
  return 0;
}

四、柔性数组的优势

      代码演示2:不使用柔性数组——结构体中的数据不连续

#include<stdio.h>
#include<stdlib.h>
typedef struct S
{
  int i;
  int* a;//不使用int a[]柔性数组,使用指针
}S;
int main()
{
  //给结构S开辟空间
  struct S* ps = (struct S*)malloc(sizeof(struct S));
  //判断是否开辟成功
  if (NULL == ps)
  {
    //打印错误信息
    perror("malloc");
    return 1;
  }
  //使用
  ps->i = 10;
  ps->a = (int*)malloc(10 * sizeof(int));
  if (NULL == ps->a)
  {
    //打印错误信息
    perror("malloc->a");
    return 1;
  }
  //使用
  int i = 0;
  for (i = 0; i < 10; i++)
  {
    ps->a[i] = i;
    printf("%d ", ps->a[i]);
  }
  //增容,在添加10个整形
  int* ptr = (int*)realloc(ps->a, 20 * sizeof(int));
  if (ptr != NULL)
  {
    ps->a = ptr;
        ps->i = 20;
  }
  else
  {
    perror("realloc->ptr");
    //增容失败,先释放结构体指针a指向的之前开辟的空间
    //(因为先释放结构体的空间,就找不着a指向的空间了)
    free(ps->a);
    ps->a = NULL;
    //再释放结构体的空间
    free(ps);
    ps = NULL;
    return 1;
  }
  //再次使用(略)
  //释放增容成功
  //先释放结构体指针a指向的之前开辟的空间
  //(因为先释放结构体的空间,就找不着a指向的空间了)
  free(ps->a);
  ps->a = NULL;
  //再释放结构体的空间
  free(ps);
  ps = NULL;
  return 0;
}

       上面代码演示1和代码演示2都可以完成同样的功能,谁更好呢?

       答案是:代码演示1,他有两个好处。

好处1:方便内存释放

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

图示:

      好处2:有利于访问速度

连续的内存有益于提高访问速度,也有益于减少内存碎片。(其实,也没多高,因为都要用偏移量的加法来寻址)。

      图示:

相关文章
|
编译器
动态内存管理与柔性数组 1
动态内存管理与柔性数组
50 0
|
程序员 C语言 C++
动态内存管理函数的使用与优化技巧(内存函数、柔性数组)(下)
动态内存管理函数的使用与优化技巧(内存函数、柔性数组)(下)
52 0
|
程序员 编译器 C语言
动态内存管理函数的使用与优化技巧(内存函数、柔性数组)(上)
动态内存管理函数的使用与优化技巧(内存函数、柔性数组)(上)
75 0
|
程序员 编译器 C语言
动态内存函数,内存开辟,柔性数组(超详细)
动态内存函数,内存开辟,柔性数组(超详细)
71 0
|
编译器 程序员 测试技术
详解动态内存管理【malloc/calloc/realloc/free函数/柔性数组】【C语言/进阶/数据结构基础】
详解动态内存管理【malloc/calloc/realloc/free函数/柔性数组】【C语言/进阶/数据结构基础】
200 0
|
编译器 程序员 C语言
【C语言】动态内存管理(malloc,free,calloc,realloc,柔性数组)
【C语言】动态内存管理(malloc,free,calloc,realloc,柔性数组)
|
5月前
|
程序员 C语言 C++
【C语言】:柔性数组和C/C++中程序内存区域划分
【C语言】:柔性数组和C/C++中程序内存区域划分
40 0
|
6月前
|
存储 安全 编译器
【C语言】动态内存管理 -- -- 深入了解malloc、calloc、realloc、free、柔性数组(万字深入了解)
【C语言】动态内存管理 -- -- 深入了解malloc、calloc、realloc、free、柔性数组(万字深入了解)
62 0
【C语言】动态内存管理 -- -- 深入了解malloc、calloc、realloc、free、柔性数组(万字深入了解)
|
6月前
|
程序员 编译器 C语言
C语言进阶⑰(动态内存管理)四个动态内存函数+动态通讯录+柔性数组_malloc+free(下)
C语言进阶⑰(动态内存管理)四个动态内存函数+动态通讯录+柔性数组_malloc+free
42 0
C语言进阶⑰(动态内存管理)四个动态内存函数+动态通讯录+柔性数组_malloc+free(下)
|
6月前
|
编译器 数据库 C语言
C语言进阶⑰(动态内存管理)四个动态内存函数+动态通讯录+柔性数组_malloc+free(上)
C语言进阶⑰(动态内存管理)四个动态内存函数+动态通讯录+柔性数组_malloc+free
45 0
C语言进阶⑰(动态内存管理)四个动态内存函数+动态通讯录+柔性数组_malloc+free(上)