redis源码之动态字符串(sds.h和sds.c)(篇一)

简介

redis 源码版本:3.05.04

如有理解不对的地方,欢迎各位指出,大家共同交流和学习。

源码分析

为什么文件叫sds那?因为,sds是simple dynamic string的英文缩写,也就是简单的动态字符串。

sds的底层还是char *数据类型

typedef char *sds;

sds 结构:

struct sdshdr {
  unsigned int len;  //当前字符串已占空间
  unsigned int free; //剩余可用空间
  char buf[];         //字符数组,存放实际数据

sds对象示意图如下:
在这里插入图片描述
如上图所示,buf作为一个字符数组,与c/c++语言的字符串类似,都是以‘\0’来结束字符串。其中,len表示当前buf中的字符数,不包括后面的结束符’\0’,free表示当前buf中剩余的空间数,整个buf的大小就是len+free+1。

那么一个sdshdr对象占多大空间那?

即:

sizeof(sdshdr) = 8

因为,结构体中的char buf[]只作为一个符号地址存在,不占内存空间。且它还有一个名字叫柔性数组成员,必须是结构体的最后一个成员,柔性数组成员不仅可以用于字符数组,还可以是元素为其他类型的数组,如int i[];且结构体中只能出现一个柔性数组成员,否则,最后一个柔性数组成员前面的柔性数组成员就会报错,提示"不允许使用不完整的数据类型"。
补充一下,柔性数组和一个指针的区别。

首先,看一个Demo代码:

#include<iostream>
#include<string>
using namespace std;

struct sdshdr_1 {

	unsigned int len;  
	unsigned int free;
	char buf[];       //柔性数组
};

struct sdshdr_2 {

	unsigned int len;  
	unsigned int free;
	char *buf;       //指针
};


int main()
{

	cout << sizeof(sdshdr_1) << endl;   //结果为:8
	cout << sizeof(sdshdr_2) << endl;   //结果为:12

	system("pause");
	return 0;
}

可以看出,指针会占用4字节(32位)的内存空间,而柔性数组不占用内存空间,所以,采用这种方式可以节省内存空间。在sdsnewlen核心函数中,就利用到了柔性数组来保存实际数据,根据initlen参数(数据长度)动态分配内存,有效利用内存空间。具体代码如下
创建一个指定长度的sds字符串
这个函数是后面操作sds字符串的核心。

sdsnewlen函数

//创建一个指定长度的sds字符串     这个也是sds字符串的核心函数,后面操作sds字符串的函数底层调用的就是这个函数
sds sdsnewlen(const void *init, size_t initlen) {
  struct sdshdr *sh;
  if (init)
  {
    //zmalloc 用来分配内存,不初始化内存(详细后面会讲到,这里暂且当成malloc理解即可)
​    sh = zmalloc(sizeof(struct sdshdr)+initlen+1);
  } else 
  {
    //zcalloc 也是用来分配内存,同时进行初始化(详细后面会讲到,这里暂且当成malloc和memset初始化操作理解即可)
​    sh = zcalloc(sizeof(struct sdshdr)+initlen+1);
  }
  if (sh == NULL) return NULL;
  sh->len = (int)initlen;                           WIN_PORT_FIX /* cast (int) */
   //因为是新的字符串,所以,不预留内存空间,即分配的内存就是字符串实际所占用的大小,不预留空间
  sh->free = 0;
  if (initlen && init)
   //如果initlen和init初始化内容同时非空,则将初始化的init字符串复制到buf中memcpy(sh->buf, init, initlen);
   //最后以'\0'结尾
  sh->buf[initlen] = '\0';
   //返回对应字符串首地址,进行打印时,输出的就是字符串
  return (char*)sh->buf;
}

为什么分配空间时加1?

sizeof(struct sdshdr)+initlen+1

因为,buf中的字符是以’\0’结尾,表示字符至此结束。

上面的核心函数结合如下Demo理解:

#include<iostream>
#include<string>
using namespace std;
struct sdshdr {

	unsigned int len;  //已占空间   sizeof()
	unsigned int free;//剩余可用空间
	char buf[];       //存放实际数据
};
typedef char *sds;

sds newlen(const void * init, size_t initlen)
{
	sdshdr * sh;
	if (init)
	{
		sh = (sdshdr *)malloc(sizeof(struct sdshdr) + initlen + 1);
	}
	else
	{
		sh = (sdshdr *)malloc(sizeof(struct sdshdr) + initlen + 1);
		memset(sh, 0, sizeof(struct sdshdr) + initlen + 1);
	}
	if (sh == NULL) return NULL;
	sh->len = (int)initlen;                                                  
	sh->free = 0;
	if (initlen && init)   
		memcpy(sh->buf, init, initlen);
	sh->buf[initlen] = '\0';
	return (char*)sh->buf;
}
int main()
{

	cout << newlen("abc", 3) << endl;
	system("pause");
	return 0;
}

结果:abc

sdsempty函数

创建一个空sds字符串

sds sdsempty(void) {
    return sdsnewlen("",0);
}

sdsnew函数

创建一个sds字符串

sds sdsnew(const char *init) {
    size_t initlen = (init == NULL) ? 0 : strlen(init);
    return sdsnewlen(init, initlen);
}

sdsdup函数

复制一个sds字符串

sds sdsdup(const sds s) {
//init非空时,用strlen计算字符串的长度,然后调用sdsnewlen函数创建sds字符串
    return sdsnewlen(s, sdslen(s));
}

sdsfree函数

释放sds字符串

void sdsfree(sds s) {
    if (s == NULL) return;
    zfree(s-sizeof(struct sdshdr));
}

sdsupdatelen函数

更新sds字符串长度

void sdsupdatelen(sds s) {
    struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));  //获取sds对应结构体的起始位置,然后获取free和len
    int reallen = (int)strlen(s);   //获取字符串真实的长度                                      
    sh->free += (sh->len-reallen);  //更新剩余空间
    sh->len = reallen;
}

sdsclear函数

将sds字符串修改为长度为0的空字符串

void sdsclear(sds s) {
    struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
    sh->free += sh->len;
    sh->len = 0;
    sh->buf[0] = '\0';
}

sdslen函数

返回sds已使用空间字节数(在sds.h中)

static inline size_t sdslen(const sds s) {
  struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));
  return sh->len;
}

sdsavail函数

返回sds未使用空间字节数(在sds.h中)

static inline size_t sdsavail(const sds s) {
  struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));
  return sh->free;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值