Redis中String字符串和sdshdr结构体超详细讲解
作者:程序员李哈
前言
在redis中对字符串的处理,个人觉得特别的优雅,所以特意写这篇帖子来分享一下对其的理解。
struct sdshdr { int len; int free; char buf[]; };
len是长度
free是目前空闲的长度
buf是实际存储的字符数组
很多读者,第一次见到char buf[],会感到到,这不是数组么,怎么还能不传入初始大小???
为了用官方的论证这个char buf[],笔者特意找到GCC手册中6.17 Arrays of Length Zero章节,以下截图是对其的描述。
GCC手册的论证
一言以蔽之:就是GCC编译器在ISO C99的C语言规范中支持动态数组。并且初始长度为0。并且只能出现在结构体中最后一个成员。
既然redis定义了自己的字符串形式,那么对字符串的操作那比不可少,所以下面挑选几个api对其讲解。实际上,细心的读者可以发现,其实每个api的操作大同小异,都是通过指针的骚操作得到sdshdr结构体,然后取值。
sdslen讲解
static inline size_t sdslen(const sds s) { struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr))); return sh->len; }
首先需要明白:
sizeof(struct sdshdr) = 8
。
因为由GCC文档得知可变数组在初始化的过程中大小为0,所以sizeof(struct sdshdr) 仅仅是2个int的大小2*4 = 8;
其次方法参数const sds s是传入的char*地址。也就是sdshdr结构体中char buf[];的地址。
所以使用char buf[];的地址 - 8 就是sdshdr结构体的地址。
文字描述可能存在理解偏差,所以笔者借用图形化来方便读者的理解,如下图所示。
sdsnewlen讲解
// void *init是初始化的字符串 // size_t initlen是长度 sds sdsnewlen(const void *init, size_t initlen) { struct sdshdr *sh; if (init) { // 因为sizeof(struct sdshdr)长度不计算char buf[], // 所以开辟大小需要加上initlen,为什么要+1,因为字符串'\0'结尾,所以要+1的大小。 sh = zmalloc(sizeof(struct sdshdr)+initlen+1); } else { sh = zcalloc(sizeof(struct sdshdr)+initlen+1); } if (sh == NULL) return NULL; sh->len = initlen; sh->free = 0; // 把传入的字符串拷贝到sdshdr结构体中的char buf[]中。 if (initlen && init) memcpy(sh->buf, init, initlen); // 把字符串的最后一位赋值为'\0' sh->buf[initlen] = '\0'; // 返回char buf[]的地址 return (char*)sh->buf; }
仔细的讲解都在代码的注释中。
这里需要注意一点,就是sizeof(struct sdshdr)长度不计算char buf[],所以开辟大小需要加上initlen,为什么要+1,因为字符串'\0'结尾,所以要+1的大小。
其他的api大同小异,就不一一的做讲解了。
总结
到此这篇关于Redis中String字符串和sdshdr结构体超详细讲解的文章就介绍到这了,更多相关Redis String字符串内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!