//类型别名,用于指向 sdshdr 的 buf 属性
typedef char *sds;
//保存字符串对象的结构
struct sdshdr {
// buf 中已占用空间的长度
int len;
// buf 中剩余可用空间的长度
int free;
// 数据空间
char buf[];
};
动态字符串是redis的基础数据结构之一,这里并没有简单地只用C中常用的char *类型来保存字符串,而是创建了一个sdshdr的结构体,该结构体包含三个组成元素,字段len保存字符串的实际长度,free记录申请的剩余空间,buf[]保存真正的字符串内容。
因为这样一个结构体的存在,当要获取字符串的长度时,就不必像传统的C语言那样遍历字符串然后通过计数最后得到组付出长度,这里直接通过len字段就可以获取,时间复杂度由o(n)变为o(1)。
另外,根据给定的字符串初始化该结构体时,最终调用的是sdsnewlen(const void *init, size_t initlen)方法。如下:
sds sdsnewlen(const void *init, size_t initlen) {
struct sdshdr *sh;
// 根据是否有初始化内容,选择适当的内存分配方式
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 中
if (initlen && init)
memcpy(sh->buf, init, initlen);
// 以 \0 结尾
sh->buf[initlen] = '\0';
// 返回 buf 部分,而不是整个 sdshdr
return (char*)sh->buf;
}
该方法有两个形参,第一个形参*init保存的是字符串本身,第二个initlen记录的是该字符串长度。初始化结构体首先是在内存中动态申请一段存储空间,长度是sizeof(struct sdshdr)+initlen+1,第一部分是结构体本身的长度,中间部分是该字符串本身的长度,最后的1存储的是空字符'\0',以作为字符串的结尾。若分配失败则返回NULL,反之则给结构体中各字段赋值。注意,该方法最终返回的是字符串本身部分,也就是sds类型,而不是整个sdshdr。
然而实际情况一般都是只有一个参数,也就是字符串本身,这时就先需要通过方法strlen()求出字符串长度,然后再调用上述方法初始化。