翻redis 源码的时候,发现有些用法真是很巧妙的,一个是指针变换,一个是内存管理策略,固然后者是有利弊的。 redis
sds.h 就这两个数据类型,这里,可以sds 和 sdshdr 相互转化。对外的接口,参数和返回只有char * 。redis 为每一个char * 都维护了个数据结构 sdshdr ,防止溢出,又能够减小内存分配。这里char * 怎么跟 sdshdr 关联上?数组
typedef char *sds; struct sdshdr { long len; long free; char buf[]; };
这里 sdshdr 是16个字节,buf 是动态数组,计算偏移的时候size 为0。经过sdshdr 取 buf 地址,强制转成 char * ,也就是sds ,这个数组转字符串指针的操做很好理解。当咱们得到sds 的时候,想知道预分配长度,直接向左偏移struct 大小,就是sdshdr的地址,强制转换类型后,就额能够取len。数据结构
看源码 sdslen 函数就能够看出:函数
size_t sdslen(const sds s) { struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr))); //先偏移struct大小,强制转成sdshdr return sh->len; }
第二个有意思的是sds 的内存预分配,在对字符串拼接的时候,有个惰性预分配的操做,每次free 为0 ,触发从新拷贝,都将多分配一倍多余内存,备用。这里好处是减小内存分配,固然也浪费了内存。对应底层函数以下:指针
static sds sdsMakeRoomFor(sds s, size_t addlen) { struct sdshdr *sh, *newsh; size_t free = sdsavail(s); size_t len, newlen; if (free >= addlen) return s; len = sdslen(s); sh = (void*) (s-(sizeof(struct sdshdr))); newlen = (len+addlen)*2; //内存空间在要从新分配的状况下,直接加倍 newsh = zrealloc(sh, sizeof(struct sdshdr)+newlen+1); #ifdef SDS_ABORT_ON_OOM if (newsh == NULL) sdsOomAbort(); #else if (newsh == NULL) return NULL; #endif newsh->free = newlen - len; return newsh->buf; }