柔性数组

1 定长数组

在日常编码中时常需要缓存接收到的数据,通常不知道数据具体的长度,此时需要预先分配一个足够大的数组。下面例子中,就是我们申请的1024个字节长度的数组。

1.1 定义结构

#define MAX_BUFF_LEN 1024
struct solid_array{
	unsigned int len;
	char  buff[MAX_BUFF_LEN];
};

1.2 申请内存

struct solid_array * pBuf = (struct solid_array *) malloc( sizeof(unsigned int) + sizeof(char) * MAX_BUFF_LEN);
 if(pBuf){
 	pBuf->len = CUR_BUFF_LEN;
 	memset(pBuf->buff, 0, MAX_BUFF_LEN);
 	memcpy(pBuf->buff, "solid array test!", CUR_BUFF_LEN);
}

1.3 释放内存

free(pBuf);
pBuf = NULL;

1.4 优缺点

缺点:

  1. 如果接收到大量的数据包,数据包的大小都不超过24字节,那么每一个数据包都将有1000个字节的内存空间浪费,而内存又是相当昂贵的资源。
  2. 如果是大量发送此数据结构的数据包,也会对流量造成极大浪费

优点:

  1. 内存地址连续内存碎片化少,只需要一次释放内存。

2 指针数据

为了解决定长数组内存浪费,通常声明一个指针,根据接收的数据包大小,动态申请内存。

2.1 定义结构

struct dynamic_array{
	unsigned int len;
	char*  buff;
};

2.2 申请内存

struct dynamic_array* pBuf = (struct dynamic_array*) malloc( sizeof(unsigned int) + sizeof(char *));
 if(pBuf){
 	pBuf->len = CUR_BUFF_LEN;
 	pBuf->buff = (char *) malloc(sizeof(char ) * CUR_BUFF_LEN);
 	if(pBuf->buff){
 	 	memset(pBuf->buff, 0, CUR_BUFF_LEN);
 		memcpy(pBuf->buff, "dynamic array test!", CUR_BUFF_LEN);
 	}
}

2.3 释放内存

free(pBuf->buff);
pBuf->buff = NULL;
free(pBuf);
pBuf = NULL;

2.4 优缺点

缺点:

  1. 2次申请内存,效率低下,内存碎片化
  2. 作为API参数对外提供数据,容易遗漏pBuf->buff释放步骤,导致内存泄露
  3. 数据拷贝时,必须拷贝它指向的内存,不能直接赋值。

优点:

  1. 节省内存空间

3 柔性数组

柔性数组成员(flexible array member)也叫伸缩性数组成员,这种代码结构产生于对动态结构体的需求。在日常的编程中,有时候需要在结构体中存放一个长度动态的字符串,鉴于这种代码结构所产生的重要作用,C99 甚至把它收入了标准中:

柔性数组是 C99 标准引入的特性,所以当你的编译器提示不支持的语法时,请检查你是否开启了 C99 选项或更高的版本支持。

3.1 定义结构

struct flexible_array{
	unsigned int len;
	char  buff[];
};

3.2 结构约束

1、柔性数组成员必须定义在结构体里面且为最后元素;
2、结构体中不能单独只有柔性数组成员;
3、柔性数组不占内存。

在一个结构体的最后,申明一个长度为空的数组,就可以使得这个结构体是可变长的。对于编译器来说,此时长度为 0 的数组并不占用空间,因为数组名本身不占空间,它只是一个偏移量,数组名这个符号本身代表了一个不可修改的地址常量,
但对于这个数组的大小,我们可以进行动态分配,对于编译器而言,数组名仅仅是一个符号,它不会占用任何空间,它在结构体中,只是代表了一个偏移量,代表一个不可修改的地址常量!

3.3 申请内存

struct flexible_array* pBuf = (struct flexible_array*) malloc( sizeof(unsigned int) + sizeof(char ) * CUR_BUFF_LEN);
 if(pBuf){
 	pBuf->len = CUR_BUFF_LEN;
 	memset(pBuf->buff, 0, CUR_BUFF_LEN);
 	memcpy(pBuf->buff, "flexible array test!", CUR_BUFF_LEN);
}

3.4 释放内存

free(pBuf);
pBuf = NULL;

3.5 优缺点

优点:

  1. 由于结构体使用指针地址不连续(两次 malloc),柔性数组地址连续,只需要一次 malloc,同样释放前者需要两次,后者可以一起释放。
  2. 在数据拷贝时,结构体使用指针时,必须拷贝它指向的内存,内存不连续会存在问题,柔性数组可以直接拷贝。
  3. 减少内存碎片,由于结构体的柔性数组和结构体成员的地址是连续的,即可一同申请内存,因此更大程度地避免了内存碎片。另外由于该成员本身不占结构体空间,因此,整体而言,比普通的数组成员占用空间要会稍微小点。

缺点:

  1. 对结构体格式有要求,必要放在最后,不是唯一成员。

3.6 应用场景

Redis的简单动态数组(SDS)结构

struct sdshdr {
    // buf 中已占用空间的长度
    int len;
    // buf 中剩余可用空间的长度
    int free;
    // 字节数组
    char buf[];
}

SDS优点:

  1. 常数复杂度获取字符串长度:O(1)
  2. 杜绝缓冲区溢出
  3. 减少修改字符串时带来的内存重分配次数
  4. 二进制安全
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kingforyang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值