1,sds(simple dynamic string)作为redis作者自己实现的字符串类型,是redis的基本数据类型。
typedef char *sds;
struct sdshdr {
int len;
int free;
char buf[];
};
可以看到 sds本质上是一个char指针,内部存储结构为一个header+char*. len表示sds实际占用的空间大小, free表示sds尚未使用的空间。buf指向实际的字符串内容。
sizeof(struct sdshsr)在32位操作系统下面是8,redis作者没有用char *buf,是不是觉得这样一个头部就可以节约4字节内存?char buf[]被gcc编译器理解为动态数组了,而且buf变量只能放在结构体最后位置。否则报错。
2, 关于sds的操作
1)创建sds
sds.c有三个函数用于创建sds
sds sdsnewlen(const void *init, size_t initlen);//主要的创建函数
sds sdsnew(const char *init);//这个函数实际上调用的sdsnewlen,initlen问 字符串init的大小(不包含最后的‘\0’)
sds sdsempty();//同调用sdsnewlen,只不过initlen为0
sdsnewlen的具体代码
sds sdsnewlen(const void *init, size_t initlen) {
struct sdshdr *sh;
if (init) {
sh = zmalloc(sizeof(struct sdshdr)+initlen+1); //一个sds真正的占用空间为头部大小+字符串长度
} else {
sh = zcalloc(sizeof(struct sdshdr)+initlen+1);
}
if (sh == NULL) return NULL;
sh->len = initlen; //此处的len,没有把'\0‘计算在内
sh->free = 0;
if (initlen && init)
memcpy(sh->buf, init, initlen);
sh->buf[initlen] = '\0';
return (char*)sh->buf; //返回的指针指向真正字符串内容,而不是返回头部指针,这样用户之需要关心真正的内容就行,不需要管理头部
}
如果这样调用
sds mysds = sdsnewlen("redis", 5);
那么内存结构图为
那么如何获取头部信息呢,比如说我想获取sds的长度(buf长度)
static inline size_t sdslen(const sds s) {
struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));//往前偏移
return sh->len;
}
用sds指针向前移动sizeof(struct sdshdr) 字节(就是减去)就可以指向头部了。从而获取sds的头部相关信息。
2)sds释放
void sdsfree(sds s) {
if (s == NULL) return;
zfree(s-sizeof(struct sdshdr));
}
利用redis作者封装的free函数释放掉所有的内存,当然包括头部。
3)sds其他操作这里就不再叙述,基本上常见的字符串的操作都可以找到。