设为首页 加入收藏

TOP

C语言编程笔记丨SDS:一个简易动态字符串库(一)
2019-04-01 22:08:12 】 浏览:417
Tags:语言编程 笔记 SDS 一个 简易 动态 字符串

SDS(Simple Dynamic Strings)是一个C语言字符串库,设计中增加了从堆上分配内存的字符串,来扩充有限的libc字符处理的功能,使得:

(1)使用更简便

(2)二进制安全

(3)计算更有效率

(4)而且仍旧…兼容一般的C字符串功能

它使用另一种设计来实现,不用C结构体来表现一个字符串,而是使用一个二进制的前缀(prefix),保存在实际的指向字符串的指针之前,SDS将其返回给用户。

+--------+-------------------------------+-----------+

| Header | Binary safe C alike string... | Null term |

+--------+-------------------------------+-----------+

         |

         Pointer returned to the user.

因为元数据作为一个前缀被储存于实际的返回指针之前,还因为不论字符串的实际内容,每个SDS字符串都隐含地在字符串的末尾追加一个空项(null term)。SDS字符串能够和C字符串一起使用,用户能够使用以只读方式访问字符串的函数,自由地交替使用它们。

SDS以前是C字符串的库,是我为自己每天的C编程的需要而开发的,后来它被迁移到Redis,那里它得到了扩展使用,并且为了适应高性能的操作而修改。现在它被从Redis分离出来,成了一个独立的项目。

因为它在Redis里存在了好几年,SDS不仅提供了能够简单操作C字符串的上层函数,还有一系列的底层函数,使得避免因使用上层字符串库造成的损失而写出高效能的代码变为可能。

SDS的优缺点

通常动态C字符串库使用一个定义字符串的结构体来实现。此结构体有一个指针域,由字符串函数管理,看起来像这样:

struct yourAverageStringLibrary {

    char *buf;

    size_t len;

    ... possibly more fields here ...};

SDS字符串,已经提过了,不遵从这样的模式,而是对在实际返回字符串地址之前的前缀给予一个单独分配(single allocation)的空间。

相比传统的方式,这种方法也有其优缺点:

缺点#1:

很多函数以值的形式返回新字符串,由于有时SDS要求创建一个占用更多空间的新字符串,所以大多数SDS的API调用像这样:

s = sdscat(s,"Some more data");

你可以看到s被用来作为sdscat的输入,但也被设为SDS API调用返回的值,因为我们不知道此调用是否会改变了我们传递的SDS字符串,还是会重新分配一个新的字符串。忘记将sdscat或者类似函数的返回值赋回到存有SDS字符串的变量的话,就会引起bug。

缺点#2:

如果一个SDS字符串在你的程序中多个地方共享,当你修改字符串的时候,你必须修改所有的引用。但是,大多数时候,当你需要共享SDS字符串时,将字符串封装成一个结构体,并使用一个引用计数会更好,否则很容易导致内存泄露。

优点#1:

你无需访问结构体成员或者调用一个函数就可以把SDS字符串传递给C函数,就像这样:

printf("%s\n", sds_string);

而在大多数其它库中,这将是像这样的:

printf("%s\n", string->buf);

或者:

printf("%s\n", getStringPointer(string));

优点#2:

直接访问单个字符。C是个底层的语言,所以在很多程序中这是一个重要的操作。 用SDS字符串来访问单个字符是很轻松的:

printf("%c %c\n", s[0], s[1]);

用其他库的话,你最有可能分配string->buf(或者调用函数来取得字符串指针)到一个字符指针,并操作此指针。然而,每次你调用一个函数,可能修改了字符串,其它的库可能隐式地重新分配缓存,你必须再次取得一个内存区的引用。

优点#3:

单次分配有更好的缓存局部性。通常当你访问一个由使用结构体的字符串库所创建字符串时,你会有两块不同的内存分配:用结构体来表现字符串的分配,和实际的内存区里储存着字符串。过了一段时间后,内存重新分配,很可能终结在一个与结构体本身地址完全不同的内存部分。因为现代的程序性能经常由缓存未命中次数所决定的,SDS可以在大工作量下表现得更好。

SDS基础

SDS字符串的类型只是字符指针char *。 然而,SDS在头文件里定义了一个sds类型作为char*的别名:你应该用sds类型,来保证你能记住你程序里的一个变量保存了一个SDS字符串而不是C字符串,当然这不是硬性规定的。

这是你可以写的能做些事情的最简单的SDS程序

sds mystring = sdsnew("Hello World!");

printf("%s\n", mystring);

sdsfree(mystring);

output> Hello World!

上面的小程序已经展示了一些关于SDS的重要内容:

(1)SDS字符串由sdsnew()函数或者其它类似的函数创建、从堆上分配内存,稍后你将看到。

(2)SDS字符串可以被传递到printf()像任何其它的C字符串。

(3)SDS字符串要求由sdsfree()释放,因为它们是堆上分配的。

创建SDS字符串

sds sdsnewlen(const void *init, size_t initlen);

sds sdsnew(const char *init);

sds sdsempty(void);sds sdsdup(const sds s);

创建SDS字符串有很多方法:

(1)sdsnew()函数创建一个以C的空字符结尾的SDS字符串?。我们已经在上述例子中看到它如何工作的。

(2)sdsnewlen()函数类似于sdsnew(),但不同于创建一个输入是以空字符结尾的字符串,它取另一个长度的参数。用这个方法,你可以创建一个二进制数据的字符串:

char buf[3];sds mystring;

buf[0] = 'A';buf[1] = 'B';buf[2] = 'C';mystring = sdsnewlen(buf,3);printf("%s of len %d\n", mystring, (int) sdslen(mystring));

output> ABC of len 3

注意:sdslen的返回值被转换成int,因为它返回一个size_t类型。你可以使用正确的printf标识符而不是类型转换。

a、sdsempty()函数创建一个空的零长度的字符串:

sds mystring = sdsempty();printf("%d\n", (int) sdslen(mystring));

output> 0

b、sdsup()函数复制一个已存在的SDS字符串:

sds s1, s2;

s1 = sdsnew(&quo
首页 上一页 1 2 3 4 5 下一页 尾页 1/5/5
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇C++编程丨C++11的指针 下一篇第一章:UNIX基础知识

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目