为结构体分配额外存储空间的方法

本人是菜鸟一枚,在网络上多位技术大牛的指导下,尤其是读了黄建健宏老师的《Redis设计与实现》和黄老师注释的Redis源码,对Redis有了一些理解,在此深表感谢。当然更应感谢Redis开源项目的作者。
言归正传,在此介绍自己的一份收获。在Redis的sds.h和sds.c文件下,有sdshdr结构体的定义和相关的函数实现,使sdshdr成为一种具备多种优良特性的符串型数据结构,相关源码如下:
struct sdshdr {
    
    // buf 中已占用空间的长度
    int len;




    // buf 中剩余可用空间的长度
    int free;




    // 数据空间
    char buf[];
};
//创建一个由init和initlen初始化的sdshdr型变量
sds sdsnewlen(const void *init, size_t initlen) {
    struct sdshdr *sh;
    // 根据是否有初始化内容,选择适当的内存分配方式
    // T = O(N)
    if (init) {
        // zmalloc 不初始化所分配的内存
        sh = zmalloc(sizeof(struct sdshdr)+initlen+1);
    } else {
        // zcalloc 将分配的内存全部初始化为 0
        sh = zcalloc(sizeof(struct sdshdr)+initlen+1);
    }
    // 内存分配失败,返回
    if (sh == NULL) return NULL;
    // 设置初始化长度
    sh->len = initlen;
    // 新 sds 不预留任何空间
    sh->free = 0;
    // 如果有指定初始化内容,将它们复制到 sdshdr 的 buf 中
    // T = O(N)
    if (initlen && init)
        memcpy(sh->buf, init, initlen);
    // 以 \0 结尾
    sh->buf[initlen] = '\0';
    // 返回 buf 部分,而不是整个 sdshdr
    return (char*)sh->buf;
}
从以上源码可以看出,sdshdr存储字符串的存储区并不在结构体内,是在创建时根据实际需要申请的,实际存储initlen个字符,为新建的sdshdr变量分配的存储空间大小是(sizeof(struct sdshdr)+initlen+1)字节,这样刚好满足要求。没有浪费内存。在其他情况下,怎样处理呢?请看下面的结构体:
typedef struct xRecord
{
double m_v0;
short m_v1;
char m_buff[];
}Record,*PRecord;
如果要采用类似创建sdshdr变量的方法为Record变量动态分配存储空间,应该分配多少空间呢?
PRecord CreateRecord(const double v0,const short v1,const char buff[])
{
size_t buff_size=strlen(buff)+1;
size_t extra_size=?;//extra_size,在Record结构体外分配的存储空间大小;
size_t sum_size=sizeof(Record)+extra_size;//sum_size代表需要的存储空间总量;
PRecord pr=(PRecord)malloc(sum_size);//为了不转移主题,在此假设能正确分配动态存储空间,暂不检查malloc()函数的结果;
pr->m_v0=v0;
pr->m_v1=v1;
memcpy(pr->m_buff,buff,buff_size);
return pr;
}
其中的extra_size应该怎样确定呢?如果照搬sdshdr的分配方法,即:
extra_size=buff_size;
这样,由于结构体内存圆整,在结构Record内会有一部分填充空间(在32位环境下是2字节)未被充分利用。为了充分利用这部分填充空间,需要确定
extra_size。也就是在利用Record的填充空间的前提下,还需要多少存储空间。
size_t ExtraSize(const size_t need_size)
{//need_size,理论上结构体外需要的存储空间大小;
Record rc;//定义这个变量rc而不是人为计算Record结构体的内存分布,是为了充分利用系统功能,保证可移植性;
size_t spare_size=(size_t)((char*)(&rc+1)-rc.m_buff);//spare_size,Record结构内m_buff之后填充空间的大小;
size_t extra_size=(spare_size>=need_size?0:(need_size-spare_size));//extra_size,实际上需要在结构体外分配的存储空间大小;
return extra_size; 
}
完整的CreateRecord()函数实现如下:
PRecord CreateRecord(const double v0,const short v1,const char buff[])
{
size_t buff_size=strlen(buff)+1;
size_t extra_size=ExtraSize(buff_size);//extra_size,在Record结构体外分配的存储空间;
size_t sum_size=sizeof(Record)+extra_size;//sum_size代表需要的存储空间总量;
PRecord pr=(PRecord)malloc(sum_size);
if(pr!=NULL)
{
pr->m_v0=v0;
pr->m_v1=v1;
memcpy(pr->m_buff,buff,buff_size);
}
return pr;

}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
结构体嵌套会对性能产生一定的影响,但具体影响的大小取决于嵌套的深度和结构体的大小。以下是一些可能的影响: 1. 内存占用:结构体嵌套会增加内存的占用,因为每个嵌套的结构体都需要分配内存空间。如果结构体嵌套很深或者结构体本身很大,那么占用的内存也会更多。 2. 访问效率:访问嵌套的结构体成员需要多次解引用,这会增加访问成员的开销。尤其是在多层嵌套的情况下,访问成员可能需要多次指针解引用操作,导致额外的性能开销。 3. 缓存效果:CPU有多级缓存,可以提高数据的读写效率。但是,如果结构体嵌套导致数据在内存不是连续存储的,可能会降低缓存的命率,从而影响性能。 为了减少结构体嵌套对性能的影响,可以考虑以下策略: 1. 减少嵌套深度:尽量避免过深的结构体嵌套,可以将部分成员提取到外层结构体,减少解引用操作的次数。 2. 优化结构体大小:尽量避免结构体过大,可以考虑使用数据对齐、紧凑的成员排列等技术减小结构体内存占用。 3. 优化数据访问方式:在访问嵌套结构体成员时,可以使用指针或者引用来减少解引用操作的开销。 总之,结构体嵌套会对性能产生一定的影响,但具体的影响因情况而异。在设计和使用结构体时,需要根据具体的应用场景和性能需求来权衡结构体的嵌套深度和大小。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值