柔性数组的概念
柔性数组(flexible array member)也叫伸缩性数组成员,这种结构产生与对动态结构体的去求。在平常编程中,有时须要在结构体中存放一个长度是动态的字符串(也多是其余数据类型),通常的作法,实在结构体中定义一个指针成员,这个指针成员指向该字符串所在的动态内存空间。编程
在一般状况下,若是想要高效的利用内存,那么在结构体内部定义静态的数组是很是浪费的行为。其实柔性数组的想法和动态数组的想法是同样的。数组
柔性数组用来在结构体中存放一个长度动态的字符串。网络
普通的方式
其实不用柔性数组咱们同样能够作到:在结构体中定义一个方法,在方法中动态地将指针指向动态数组数据结构
#include <string.h> #include <stdlib.h> typedef struct Test { int a; char *p; } Test; int main() { char *str = (char *)malloc(sizeof(char) * 10); strcpy(str, "hello"); Test *t = (Test *)malloc(sizeof(Test)); t->p = str; printf("%s\n", (t->p)); printf("Address:\n"); printf("t\t %p\n", t); printf("t.a\t %p\n", &(t->a)); printf("t.p\t %p\n", (t->p)); free(t); free(t->p); //还须要显式的释放p所指的内存 return 0; }
运行结果:flex
上面的代码的确是能够完成咱们想要的结果。咱们看了一下指针p和数组的起始地址。咱们能够看到动态数组的内存块和字符串的内存是两块不同的内存。spa
可是,咱们在释放空间时,须要显式地释放指针p引用的内存空间,否则会出现内存泄露的状况。操作系统
使用柔性数组的方式
从C99开始便支持了不完整类型实现柔性数组成员。为何使用不完整类型呢?指针
int a[] = {10};
看到这个声明语句,咱们发现a[]其实就是个数组记号,不完整类型,因为赋值语句,因此在编译时便肯定了数组的大小,是一个完整的数组类型。
在结构体中便利用不完整类型在运行对动态的数组进行指明。code
C99标准的定义以下blog
struct Test{ int a; char p[]; // 不仅是char类型,其余类型一样也是能够 }
因为声明内存连续性的关系,柔性数组成员必须定义在结构体的最后一个,而且不能是惟一的成员。
咱们再来看一看整个结构体(包含数组内存的分布状况)
#include <string.h> #include <stdlib.h> typedef struct Test { int a; char p[]; } Test; int main() { Test *t=(Test*)malloc(sizeof(Test)+sizeof(char)*(10+1)); strcpy(t->p,"hello"); printf("%s\n", (t->p)); printf("Address:\n"); printf("t\t %p\n", t); printf("t.a\t %p\n", &(t->a)); printf("t.p\t %p\n", (t->p)); free(t); //只须要释放一次内存 return 0; }
再运行结果:
由运行结果就能够看出,整个结构体是连续的,而且释放结构体的方式也很是简单直接对结构体指针进行释放。
【注】因为这个是C99的标准,在ISO C和C++的规格说明书中是不容许的。在vs下使用0长度的数组可能会获得一个警告。
然而gcc, clang++预先支持了C99的玩法,因此在Linux下编译无警告
进一步认识柔性数组
现有一个程序
#include<stdio.h> typedef struct _SoftArray{ int len; int array[]; }SoftArray; int main() { int len = 10; printf("The struct's size is %d\n",sizeof(SoftArray)); }
运行结果:
咱们能够看出,_SoftArray结构体的大小是4,显然,在32位操做系统下一个int型变量大小恰好为4,也就说结构体中的数组没有占用内存。为何会没有占用内存,咱们平时用数组时不时都要明确指明数组大小的吗?但这里却能够编译经过呢?这就是咱们常说的动态数组,也就是柔性数组。
先不要乱,让咱们再看一段代码
#include <stdio.h> #include <malloc.h> typedef struct _SoftArray { int len; int array[]; } SoftArray; int main() { int len = 10; SoftArray *p = (SoftArray *)malloc(sizeof(SoftArray) + sizeof(int) * len); printf("After the malloc function the struct's size is %d\n",sizeof(SoftArray)); return 0; }
运行结果:
是否是有点奇怪,为何申请了内存后结构体大小仍是4呢?
缘由是:动态申请的内存只是申请给数组拓展所用,从上个程序咱们能够看出结构体的大小在建立时已经肯定了,array明确来讲不算是结构体成员,只是挂羊头卖狗肉而已。
下面咱们来看看关于柔性数组的资料:
一、什么是柔性数组?
柔性数组既数组大小待定的数组, C语言中结构体的最后一个元素能够是大小未知的数组,也就是所谓的0长度,因此咱们能够用结构体来建立柔性数组。
二、柔性数组有什么用途 ?
它的主要用途是为了知足须要变长度的结构体,为了解决使用数组时内存的冗余和数组的越界问题。
三、用法:在一个结构体的最后 ,申明一个长度为空的数组,就可使得这个结构体是可变长的。对于编译器来讲,此时长度为0的数组并不占用空间,由于数组名
自己不占空间,它只是一个偏移量, 数组名这个符号自己代 表了一个不可修改的地址常量 (注意:数组名永远都不会是指针! ),但对于这个数组的大小,咱们能够进行动态分配,对于编译器而言,数组名仅仅是一个符号,它不会占用任何空间,它在结构体中,只是表明了一个偏移量,表明一个不可修改的地址常量!
对于柔性数组的这个特色,很容易构造出变成结构体,如缓冲区,数据包等等:
typedef struct _SoftArray { Int len; int array[]; }SoftArray;
柔性数组用途
这样的变长数组经常使用于网络通讯中构造不定长数据包,不会浪费空间浪费网络流量,好比我要发送1024字节的数据,若是用定长包,假设定长包的长度为2048,就会浪费1024个字节的空间,也会形成没必要要的流量浪费。
其实柔性数组成员在实现跳跃表时有它特别的用法,在Redis的SDS数据结构中和跳跃表的实现上,也使用柔性数组成员。