c语言快速入门|C语言柔性数组详解
文章目录
- 前言
- 一、柔性数组是什么?
- 二、柔性数组的特点
- 三、柔性数组的优点
- 总结
前言 可能大家第一眼看到这个标题会有点懵,到底什么是柔性数组,我怎么从来没听说过?但柔性数组确实是存在的,也经常会出现在一些公司的面试题中,今天就跟着笔者来学习一下柔性数组吧。
提示:以下是本篇文章正文内容,下面案例可供参考
文章图片
一、柔性数组是什么? C99中,结构体中的最后一个元素允许是未知大小的数组,这就叫作柔性数组,for example:
struct st_type
{ int i;
int a[0];
//柔性数组成员,也可以写int a[];
};
结构体成员a数组,它的数组大小是没有确定的,将来如果需要可以大也可以小。
有些编译器支持a[0]这种写法,有些编译器支持a[ ]这种写法,具体取决编译器。
二、柔性数组的特点 1.结构体中柔性数组成员前面必须至少有一个其他成员
示例如下:
struct st_type
{ int i;
int a[0];
//柔性数组成员,也可以写int a[];
};
比如上面这段代码,如果你要创建一个柔性数组a,前面必须创建一些别的成员
2.sizeof返回的这种结构大小不包括柔性数组的内存
示例如下:
struct st_type
{ int i;
//4字节
int a[0];
//柔性数组成员,也可以写int a[];
//因为是柔性数组,无法确认a占几个字节
};
int main()
{printf("%d\n", sizeof(struct st_type));
//打印4
return 0;
}
这里计算包含柔性数组的结构体大小,因为柔性数组本身是无法确定有几个字节的,所以计算整体结构体大小时,会省略柔性数组的计算。
3.包含柔性数组成员的结构用malloc()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小
ps:除了malloc函数,realloc、calloc等动态内存开辟的函数也需要类似的操作
比如说我现在要数组a里面有10个元素,现在进行malloc一下
示例如下:
#include
#include
struct st_type
{ int i;
//4字节
int a[0];
//柔性数组成员,也可以写int a[];
};
int main()
{//假设我现在需要a里有10个元素
struct st_type*ps=(struct st_type*)malloc(sizeof(struct st_type) + 10 * sizeof(int));
if (ps == NULL)//由于空间可能不够开辟导致malloc开辟失败,开辟失败会返回空指针
{printf("%s\n", strerror(errno));
return -1;
//程序出问题后,跳出程序
}
//开辟成功
int j = 0;
for (j = 0;
j < 10;
j++)
{ps->a[j] = j;
}
for (j = 0;
j < 10;
j++)
{printf("%d ", ps->a[j]);
//打印0-9
}
printf("\n");
//如果想继续用柔性数组a进行打印
//比如现在a里只有10个元素,我用完10个了,我还要继续来10个,用realloc追加
struct st_type*ptr=realloc(ps, sizeof(struct st_type) + 20 * sizeof(int));
//ps:realloc第二个参数是调整后的整体大小
if (ptr == NULL)
{printf("扩容失败\n");
return -1;
}
else
{ps = ptr;
}
//扩容成功
int k = 0;
for (k = 10;
k < 20;
k++)
{ps->a[k] = k;
}
for (j = 0;
j < 20;
j++)
{printf("%d ", ps->a[j]);
//打印0-19
}
//释放空间
free(ps);
ps = NULL;
return 0;
}
文章图片
文章图片
我们这里需要数组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的空间的
三、柔性数组的优点 柔性数组就是对一块空间实现动态开辟嘛,那我们之前也讲过指针来动态内存开辟,我们来看一段代码来对比一下这两种方法:
//用指针也可以做到a指向的空间动态变化
struct st_type
{ int i;
//4字节
int *a;
//4字节,这里计算结构体大小恰好是8字节
};
int main()
{ struct st_type*ps = (struct st_type*)malloc(sizeof(struct st_type));
ps->i = 100;
ps->a = (int*)malloc(10 * sizeof(int));
//a指向40个字节的空间,该空间由int*进行管理
int j = 0;
for (j = 0;
j < 10;
j++)
{ps->a[j] = j;
//a[j]=*(a+j)
}
for (j = 0;
j < 10;
j++)
{printf("%d", ps->a[j]);
}
//a指向的空间不够了,希望调整大小
int *ptr = (int*)realloc(ps->a, 20 * sizeof(int));
if (ptr == NULL)
{printf("扩容失败");
return -1;
}
else
{ps->a = ptr;
}
//使用...
//释放
free(ps->a);
ps->a = NULL;
free(ps);
ps = NULL;
}
这里需要注意的是,在释放空间时,你要先释放指针a指向的空间,然后释放结构体指针
文章图片
如上图,我们结构体指针ps开辟一块空间,空间里存放整形i和整形指针a,a又malloc(后续如果需要还可以realloc追加)一块空间,如果你先释放掉ps,a就没了,你就没法找到a指向的那块空间了。
还是那个生动的例子 就比如a是一个警察头子,malloc开辟的空间是卧底,只有a知道那个卧底,你现在警察头子死了,再也没法证明卧底是卧底了,也就是说a消失后,没办法再对开辟的空间进行释放,这时就会造成内存泄露,指针实现动态内存调整是需要对指针释放讲解一定的顺序性的
这里对比柔性数组,柔性数组和上述的指针都可以实现一块空间大小的调整,但是柔性数组有两个好处:
第一个好处是:方便内存释放
如果我们的代码是在一个给别人用的函数中,你在里面做了二次内存分配,并把整个结构体返回给用户。用户调用free可以释放结构体,但是用户并不知道这个结构体内的成员也需要free,所以你不能指望用户来发现这个事。以上,如果我把结构体的内存及其成员要的内存一次性分配好,并返回给用户一个结构体指针,用户做一次free就可以把所有内存都释放掉,并且不用考虑前面说的释放的顺序。
第二个好处是:加快访问速度
连续的内存有益于提高访问速度,也有益于减少内存碎片。
ps:内存碎片如下图
文章图片
操作系统给我们一块内存,我们在进行malloc时,不一定就是一块连着一块的,上图的空白部分就是内存碎片,有些类似我们裁剪布料,一些剩余的边角料这样。
总结 【c语言快速入门|C语言柔性数组详解】本文介绍了柔性数组的定义、其三个使用特点,及其对比指针实现动态内存的优势,并对其进行了具体举例,知识点并不复杂,希望读者学习完本文后能对柔性数组有更进一步的理解,祝读者生活愉快!
推荐阅读
- 【生信技能树】R语言练习题|【生信技能树】R语言练习题 - 中级
- 一起来学习C语言的字符串转换函数
- C语言字符函数中的isalnum()和iscntrl()你都知道吗
- C语言浮点函数中的modf和fmod详解
- C语言中的时间函数clock()和time()你都了解吗
- C语言学习|第十一届蓝桥杯省赛 大学B组 C/C++ 第一场
- C语言解方程的根和判断是否是闰年
- C语言的版本比较
- typeScript入门基础介绍
- 【C】题目|【C语言】题集 of ⑥