C语言入门到精通 2020-12-23程序员
如下文章来源于C语言与CPP编程 ,做者自成一派123算法
分享C语言/C++,数据结构与算法,计算机基础,操做系统等数组
在平时的开发中,缓冲区数据收发时,若是采用缓冲区定长包,假定大小是 1k,MAX_LENGTH
为 1024。结构体以下:网络
// 定长缓冲区
struct max_buffer
{
int len;
char data[MAX_LENGTH];
};
数据结构的大小 >= sizeof(int)
+ sizeof(char) * MAX_LENGTH
为了防止数据溢出的状况,data 的长度通常会设置得足够大,但也正是由于这样,才会致使数组的冗余。数据结构
假如发送 512 字节的数据, 就会浪费 512 个字节的空间, 平时通讯时,大多数是心跳包,大小远远小于 1024,除了浪费空间还消耗不少流量。app
内存申请:ide
if ((m_buffer = (struct max_buffer *)malloc(sizeof(struct max_buffer))) != NULL)
{
m_buffer->len = CUR_LENGTH;
memcpy(m_buffer->data, "max_buffer test", CUR_LENGTH);
printf("%d, %s\n", m_buffer->len, m_buffer->data);
}
内存释放:学习
free(m_buffer);
m_buffer = NULL;
为了不空间上的浪费,咱们能够将上面的长度为 MAX_LENGTH
的定长数组换为指针, 每次使用时动态的开辟 CUR_LENGTH
大小的空间。数据包结构体定义:flex
struct point_buffer
{
int len;
char *data;
};
数据结构大小 >= sizeof(int)
+ sizeof(char *)
但在内存分配时,须要两步进行:
内存申请:
if ((p_buffer = (struct point_buffer *)malloc(sizeof(struct point_buffer))) != NULL)
{
p_buffer->len = CUR_LENGTH;
if ((p_buffer->data = (char *)malloc(sizeof(char) * CUR_LENGTH)) != NULL)
{
memcpy(p_buffer->data, "point_buffer test", CUR_LENGTH);
printf("%d, %s\n", p_buffer->len, p_buffer->data);
}
}
内存释放:
free(p_buffer->data);
free(p_buffer);
p_buffer = NULL;
虽然这样可以节约内存,可是两次分配的内存是不连续的, 须要分别对其进行管理,致使的问题就是须要对结构体和数据分别申请和释放内存,这样对于程序员来讲无疑是一个灾难,由于这样很容易致使遗忘释放内存形成内存泄露。
有没有更好的方法呢?那就是今天的主题柔性数组。
柔性数组成员(flexible array member)也叫伸缩性数组成员,这种代码结构产生于对动态结构体的需求。在平常的编程中,有时候须要在结构体中存放一个长度动态的字符串,鉴于这种代码结构所产生的重要做用,C99 甚至把它收入了标准中:
As a special case, the last element of a structure with more than one named member may have an incomplete array type; this is called a flexible array member.
柔性数组是 C99 标准引入的特性,因此当你的编译器提示不支持的语法时,请检查你是否开启了 C99 选项或更高的版本支持。
C99 标准的定义以下:
struct test {
short len; // 必须至少有一个其它成员
char arr[]; // 柔性数组必须是结构体最后一个成员(也但是其它类型,如:int、double、...)
};
在一个结构体的最后,申明一个长度为空的数组,就可使得这个结构体是可变长的。对于编译器来讲,此时长度为 0 的数组并不占用空间,由于数组名自己不占空间,它只是一个偏移量,数组名这个符号自己表明了一个不可修改的地址常量,
但对于这个数组的大小,咱们能够进行动态分配,对于编译器而言,数组名仅仅是一个符号,它不会占用任何空间,它在结构体中,只是表明了一个偏移量,表明一个不可修改的地址常量!
对于柔性数组的这个特色,很容易构造出变成结构体,如缓冲区,数据包等等, 其实柔性数组成员在实现跳跃表时有它特别的用法,在Redis的SDS数据结构中和跳跃表的实现上,也使用柔性数组成员。它的主要用途是为了知足须要变长度的结构体,为了解决使用数组时内存的冗余和数组的越界问题
。
//柔性数组
struct soft_buffer
{
int len;
char data[0];
};
数据结构大小 = sizeof(struct soft_buffer)
= sizeof(int)
,这样的变长数组经常使用于网络通讯中构造不定长数据包, 不会浪费空间浪费网络流量。
申请内存:
if ((softbuffer = (struct soft_buffer *)malloc(sizeof(struct soft_buffer) + sizeof(char) * CUR_LENGTH)) != NULL)
{
softbuffer->len = CUR_LENGTH;
memcpy(softbuffer->data, "softbuffer test", CUR_LENGTH);
printf("%d, %s\n", softbuffer->len, softbuffer->data);
}
释放内存:
free(softbuffer);
softbuffer = NULL;
对比使用指针和柔性数组会发现,使用柔性数组的优势:
缺点:对结构体格式有要求,必要放在最后,不是惟一成员。
在平常编程中,有时须要在结构体中存放一个长度是动态的字符串(也多是其余数据类型),可使用柔性数组,柔性数组是一种可以巧妙地解决数组内存的冗余和数组的越界问题一种方法。很是值得你们学习和借鉴。