前言:
上篇文章我们通过几个函数使用,实现了对动态内存空间的创建、修改、释放,而今天我们将通过柔性数组的介绍和使用,真正让我们的动态内存空间得以应用。
柔性数组:
也许你重来没有听说过柔性数组(flexible array)这个名词,但它确确实实是存在的。在C99中,结构中的最后一个元素允许是未知大小的数组,这就叫做【柔性数组】成员。例如:
struct name
{
int a;
int arr[0];//柔性数组成员
};
//有些编译器可能会发生错误可以改为下面这种形式:
struct name
{
int a;
int arr[];//柔性数组成员
};
1.柔性数组特点:
根据上面我们说到的柔性数组定义,我们可以得知,柔性主要有以下3个特点:
①.结构中柔性数组成员前面必须至少有一个成员;
②.sizeof返回的这种结构大小不包过柔性数组的内存;
③.包含柔性数组成员的结构用malloc()函数进行内存的动态分配时,分配的内存应该大于结构的大小,以适应柔性数组的预期大小。
定义中我们可以得知,柔性数组只能作为结构的最后一个元素,并且柔性数组的大小是不确定的,因此在结构中,柔性数组之前至少要有一个其他成员存在。
因为柔性数组的大小是未知的,所以在用sizeof函数计算结构体大小时,将不会计入柔性数组:
#include<stdio.h>
struct name
{
int a;
int arr[];//柔性数组成员
};
int main()
{
struct name a;
int ret = sizeof(a);
printf("%d", ret);
return 0;
}
所以上述代码编译运行的结果,只有计算一个整形a的大小也就是4个字节:
![](https://img-blog.csdnimg.cn/img_convert/7844e22282a2146b0f3bb92522e2c0b4.png)
因为柔性数组的大小是未知的,因此其大小是动态,可变的,所以在分配空间时,应当使用malloc函数进行动态内存分配。又因为计算结构大小时没有计入柔性数组的大小,所以在分配的内存应当大于结构大小,才能容纳柔性数组的预期大小:
#include<stdio.h>
typedef struct name
{
int a;
int arr[];//柔性数组成员
}name;
int main()
{
name a;
//定义name类型的结构体a;
name* p = (name*)malloc(sizeof(name) * 2);
free(p);
p = NULL;
return 0;
}
2.柔性数组的使用:
上面我们介绍柔性数组的3个特点,现在我们来看看柔性数组应该如何使用:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
typedef struct name
{
int a;
int arr[];//柔性数组成员
}name;
int main()
{
name a;
//定义name类型的结构体a;
name* p = (name*)malloc(sizeof(name) +40);
if (p == NULL)
{
perror("malloc fail");
return 1;
}
//业务处理1
p->a = 10;
for (int i = 0; i <p->a; i++)
{
p->arr[i] = i;
}
for (int i = 0; i < p->a;i++)
{
printf("%d ", p->arr[i]);
}
printf("\n");
name* pp = (name*)realloc(p,sizeof(name) + 80);
if (pp == NULL)
{
perror("realloc fail");
return 2;
}
//业务处理2
pp->a = 20;
for (int i = 10; i < pp->a; i++)
{
pp->arr[i] = i;
}
for (int i = 0; i < pp->a; i++)
{
printf("%d ", pp->arr[i]);
}
printf("\n");
free(pp);
pp = NULL;
return 0;
}
在这个示例中,我们首先定义name类型的结构体a,接着使用malloc函数为结构体整形a和柔性数组分配了动态存储空间,接着判断是否开辟成空也就是判断是否为空,然后给整形a赋值和柔性数组arr赋值,然后再打印arr数组里的数据。接着我们又使用malloc函数对结构体a进行扩容,本质上就是为柔性数组arr扩容,然后我们又进行了与上面同样的步骤进行赋值和打印。最后进行空间释放并将指针置空:
![](https://img-blog.csdnimg.cn/img_convert/64816e9678094a0910b9aeccd6ee35df.png)
3.柔性数组的优势:
但是我们可以发现,上面用柔性数组的实现可大可小的动态内存空间,其实用指针再配合函数也可以实现,那为什么还有出现这个柔性数组呢?我们先来看看这段代码:
typedef struct name
{
int a;
int* p;
}name;
int main()
{
name* a = (name*)malloc(sizeof(name));
a->a = 4;
a->p = (int*)malloc(sizeof(int) * 4);
for (int i = 0; i < a->a; i++)
{
a->p[i] = i;
}
for (int i = 0; i < a->a; i++)
{
printf("%d ", a->p[i]);
}
printf("\n");
free(a->p);
a->p = NULL;
free(a);
a = NULL;
return 0;
}
这两种方法确实都可以实现我们的要求,但是使用柔性数组有两个好处:
①.方便内存释放:
如果我们的代码是在一个给别人用的函数中,你在里面做了二次内存分配,并把整个结构体返回给 用户。用户调用free可以释放结构体,但是用户并不知道这个结构体内的成员也需要free,所以你不能指望用户来发现这个事。所以,如果我们把结构体的内存以及其成员要的内存一次性分配好 了,并返回给用户一个结构体指针,用户做一次free就可以把所有的内存也给释放掉。
②.有利于访问速度:
连续的内存有益于提高访问速度,也有益于减少内存碎片。(其实,我个人觉得也没多高了,反正 你跑不了要用做偏移量的加法来寻址)
总结:
通过今天的学习,我们就把动态内存空间讲解完了,并且也运用在我们的程序代码中了。使用动态内存可以让我们的代码更加灵活,有助于我们写出优秀的程序。