龙空技术网

Redis系列 | 简单动态字符串

Linux码农 66

前言:

目前大家对“c语言判断字符串是否包含子字符串”可能比较关注,同学们都想要分析一些“c语言判断字符串是否包含子字符串”的相关资讯。那么小编同时在网摘上汇集了一些关于“c语言判断字符串是否包含子字符串””的相关知识,希望你们能喜欢,姐妹们快快来了解一下吧!

今天分析一下redis中使用的字符串。

SDS 的定义

在C语言中字符串是以'\0'结尾的字符串数组。但在redis中并没有使用C语言中的传统字符串,而是自己构建一套简单动态字符串(simple dynamic string 简称SDS),同时也将SDS作为redis的默认字符串表示方式。

SDS 定义

struct sdshdr {//buf中已使用的字符串长度unsigned int len; //buf中未使用的长度unsigned int free;//保存字符串的数组char buf[];};

len 表示在buf中已填充的字符串的长度;

free 表示buf中可用的空间长度;

len+free表示buf空间的总长度。

为了兼容c语言传统字符串,在申请buff空间时会多申请一个字符空间用于保存'\0',当在buff中保存字符串时,字符串末尾还是以'\0'结尾。

字符串如下图表示

SDS 与 C 字符串的区别

那么redis 为什么使用SDS而不使用C语言传统字符串呢?

更快的速度获取字符串的长度

C语言传统字符串要想获取字符串的长度,需要遍历整个字符串,时间复杂度为O(N)。而在SDS中直接从len中即可获取字符串的长度,复杂度为O(1)。

防止缓冲区溢出

C语言传统字符串由于不保存字符串的长度,因此在操作字符串时,比如strcat()、strcpy()等都会假定用户开辟了足够大的空间,当用户开的空间较小时,就会出现缓冲区溢出的风险。

而在SDS中,当出现数据拷贝时,都会先检查空间是否足够,若不足够,则重新分配内存保存数据。

二进制安全

C语言传统字符串是以'\0'作为字符串结束,当要保存的字符串中间包含'\0'时,很显然C语言传统字符不能满足需求。

由于SDS使用len标记字符串的长度,因此可用通过len来获取整个字符串。同时使用SDS不仅可以保存文本数据,也可以保存比如图片、音频、压缩文件等任意格式的二进制数据。

兼容部分c字符串函数

由于buff中也是采用'\0'结尾,因此在一定程度上兼容部分c字符串函数。

部分 SDS 操作接口源码分析

生成SDS对象

sds sdsnewlen(const void *init, size_t initlen) {struct sdshdr *sh;//申请 initlen+1 个缓冲区,用来保存'\0' if (init) {sh = zmalloc(sizeof(struct sdshdr)+initlen+1);} else {sh = zcalloc(sizeof(struct sdshdr)+initlen+1);}if (sh == NULL) return NULL;//保存的字符串的长度sh->len = initlen;//有空间都用来保存数据了,所以free为0sh->free = 0;//保存数据if (initlen && init)memcpy(sh->buf, init, initlen);sh->buf[initlen] = '\0';return (char*)sh->buf;}

当使用上述方法生成一个SDS字符串时,比如 mystring = sdsnewlen("abc",3),结构如下

buff空间扩容

//对 sds 中 buf 的长度进行扩容,确保在函数执行之后,buf 至少会有 addlen + 1 长度的空余空间sds sdsMakeRoomFor(sds s, size_t addlen) {struct sdshdr *sh, *newsh;// 获取 s 中当前的未使用的空间长度size_t free = sdsavail(s);size_t len, newlen;// s 当前的未使用的空间长度足够,无须再扩展,直接返回if (free >= addlen) return s;// 获取 s 目前已使用的长度len = sdslen(s);sh = (void*) (s-(sizeof(struct sdshdr)));// s 最少需要的长度newlen = (len+addlen);// 根据新长度,为 s 分配新空间所需的大小if (newlen < SDS_MAX_PREALLOC)// 如果新长度小于 SDS_MAX_PREALLOC (1M) ,那么为它分配两倍于所需长度的空间newlen *= 2;else// 否则,分配长度为目前长度加上 SDS_MAX_PREALLOC(1M)newlen += SDS_MAX_PREALLOC;// 扩容newsh = zrealloc(sh, sizeof(struct sdshdr)+newlen+1);if (newsh == NULL) return NULL;// 更新 sds 的空余长度newsh->free = newlen - len;// 返回 sdsreturn newsh->buf;}

长整型转换SDS

#define SDS_LLSTR_SIZE 21int sdsll2str(char *s, long long value) {char *p, aux;unsigned long long v;size_t l;//判断长整型数是否是正负数v = (value < 0) ? -value : value;//获取数字转换成字符填充到缓冲区s中p = s;do {*p++ = '0'+(v%10);v /= 10;} while(v);if (value < 0) *p++ = '-';//获取填充字符的长度l = p-s;*p = '\0';//高低位转换p--;while(s < p) {aux = *s;*s = *p;*p = aux;s++;p--;}return l;}

标签: #c语言判断字符串是否包含子字符串