也许你从未听说过这个柔性数组(flexible array)这个概念,但是它确实是存在的,C99中,结构中的最后一个元素允许是未知大小的数组,这就叫做【柔性数组】成员。
柔性数组举例
什么意思呢?举个例子:
struct P{
char n;
int a;
char crr[];//可变长度/未知大小
//char crr[0]//这个也可以
};
int main(){
struct P p;
return 0;
}
其中 crr[]就是柔性数组成员-因为它的数组大小是可以调整的。
柔性数组的特点
1.结构中的柔性数组成员前面必须至少有一个其他成员。
2.sizeof 返回的这种结构大小不包括柔性数组的内存。
3.包含柔性数组成员的结构用malloc函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。
柔性数组使用
柔性数组有什么用呢?或者它是怎么使用的?
在说它是怎么使用之前,我们先算一算结构体P的大小,之前结构体中我门专门讨论过结构体的所占空间的计算,如果不清楚的大家可以看一下我之前的博客C语言结构体。
struct P{
char n;
int a;
char crr[];//可变长度/未知大小
};
int main(){
struct P p;
printf("%d",sizeof (p));
return 0;
}
我们这里输出一下结构体p的大小,结果为:8个字节
8个字节这个很明显,仅仅实际算了char 类型的变量n以及int类型的变量a这二者的大小,并没有考虑这个柔性数组crr的大小,那么这又是为什么呢?
其实原因很简单,因为我们要让这个数组可变长度,所以就不能一开始就给它分配空间。
那么我们应该怎么使用呢?
struct P* ps= malloc(sizeof (struct P)+sizeof(20) );
我们来看这个代码,我们不再去创建普通的结构体变量了,我们创建一个指针,然后用malloc函数去分配一段空间,这段空间的大小怎么计算呢?
就是用结构体对象的大小(我们之前算得是8个字节)+你需要的柔性数组的大小(给20个字节)=分配的空间。
那么在内存中的分配如下:
其实到这里柔性数组的大小就确定了,就是20个字节。
struct P{
char n;
int a;
char crr[];//可变长度/未知大小
};
int main(){
// struct P p;
// printf("%d",sizeof (p));
struct P* ps= malloc(sizeof (struct P)+sizeof(20) );
ps->n='t';
ps->a=200;
for (int i = 0; i < 20; ++i) {
ps->crr[i]='a';
}
printf("%c\n",ps->n);
printf("%d\n",ps->a);
for (int i = 0; i < 20; ++i) {
printf("%c ",ps->crr[i]);
}
接下来我们对结构体中的成员变量依次赋值,然后输出看一下:(注意这里代码我少了个free大家自己要记得加上)
从使用的角度来看跟普通的结构体并没有太大的区别。
那么既然是动态开辟的空间,我们是否可以使用realloc函数去调整开辟的空间呢?答案是可以的
看接下来的代码
int main(){
// struct P p;
// printf("%d",sizeof (p));
struct P* ps= malloc(sizeof (struct P)+sizeof(20) );
ps->n='t';
ps->a=200;
struct P* ps2=realloc(ps,25);
if (ps2){
ps=ps2;
}
for (int i = 0; i < 25; ++i) {
ps->crr[i]='a';
}
printf("%c\n",ps->n);
printf("%d\n",ps->a);
for (int i = 0; i < 25; ++i) {
printf("%c ",ps->crr[i]);
if ( i%5==0){
printf("\n");
}
}
free(ps);
ps=NULL;
return 0;
}
输出结果为:
可以看到 这个数组已经能够输出25个char类型字母a了,那么以上就是柔性数组的简单使用。
柔性数组的替代方案
其实我们发现,无非就是想要一个结构体中有一个大小是我们可以自己定义的成员嘛,那有些同学可能就想到,可以这样定义啊:
struct P{
char n;
int a;
char *crr;//存储一个指针
};
int main(){
struct P* ps= (struct P* )malloc(sizeof (struct P));
//开辟了一块空间包括n a *crr
ps->n='t';
ps->a=200;
ps->crr= malloc(20);
for (int i = 0; i < 20; ++i) {
ps->crr[i]='a';
}
printf("%c\n",ps->n);
printf("%d\n",ps->a);
for (int i = 0; i < 20; ++i) {
printf("%c ",ps->crr[i]);
if ( i%5==0){
printf("\n");
}
}
free(ps->crr);
ps->crr=NULL;
free(ps);
ps=NULL;
return 0;
}
可以直接在结构体中定义一个char类型指针,然后在堆上先开辟一个空间存储结构体,然后再开辟一段空间给这个指针crr呀,并且我们按照相同的方式赋值,然后输出,结果还是一样的。
输出结果:
包括调整大小:
struct P{
char n;
int a;
char *crr;//存储一个指针
};
int main(){
struct P* ps= (struct P* )malloc(sizeof (struct P));
//开辟了一块空间包括n a *crr
ps->n='t';
ps->a=200;
ps->crr= malloc(20);
for (int i = 0; i < 20; ++i) {
ps->crr[i]='a';
}
printf("%c\n",ps->n);
printf("%d\n",ps->a);
for (int i = 0; i < 20; ++i) {
printf("%c ",ps->crr[i]);
if ( i%5==0){
printf("\n");
}
}
char * ps2= realloc(ps->crr,25);
if (ps2)
ps->crr=ps2;
printf("\nrealloc之后\n");
for (int i = 0; i < 20; ++i) {
ps->crr[i]='b';
}
for (int i = 0; i < 20; ++i) {
printf("%c ",ps->crr[i]);
if ( i%5==0){
printf("\n");
}
}
free(ps->crr);
ps->crr=NULL;
free(ps);
ps=NULL;
return 0;
}
输出结果:
依旧是能够实现同样的效果。
通过这个我们可以发现柔性数组是可以有其他的方案来替代,这种方案跟柔性数组的区别在于,结构体中的成员变量,在内存中是不连续的。
那既然有替代方案为什么还需要柔性数组呢?或者说柔性数组的优点是什么?
柔性数组的优势
1.从代码的形式上来看,替代方案要malloc申请两次空间,同样也要free两次,那么这就导致大大提高了出现忘记释放掉某个空间的风险。使用柔性数组是一次性开辟了一段空间,并且这段空间针对结构体的成员变量在空间上是连续的。申请也只申请一次,free也只free一次。 并且内存空间连续其访问速度也会比离散的空间块。(便于内存释放)
2.malloc的越多就会出现越多存储空间的浪费。红色的方框都很难被使用了,被称之为内存碎片。所以malloc用的越多内存中的内存碎片就越少,内存的利用率就更高(提高访问速度)
那么以上就是柔性数组的全部内容啦,仅供各位参考如有疑问可在下面评论区提出。祝各位生活愉快!C语言中