c 字符串数组_redis为什么不直接使用C字符串,而要自定义简单动态字符串?
来源:公众号【编程珠玑】
作者:守望先生
ID:shouwangxiansheng
Redis (一个使用ANSI C编写的开源、支持网络、基于内存、可选持久性的键值对存储数据库。)没有直接使用 C 语言传统的字符串表示redis中的字符串,而是使用了一种名为简单动态字符串(simple dynamic string,SDS)的抽象类型, 并将 SDS 用作 Redis 的默认字符串。
那么,为什么要用这种数据结构替代传统的字符串呢?我们先回顾一下C字符串。
C语言传统字符串
C语言传统字符串是以空字符结尾的字符数组。例如:
char str[] = "hello";
计算字符串的长度:
strlen(str);
C语言传统字符串我们应该已经很熟悉了,这里就不再继续介绍了。
更多相关内容参考《sizeof,strlen,数组,字符串整在一起的那些坑》和《C语言入坑指南-数组之谜》。
简单动态字符串
redis中的简单动态字符串定义如下:
struct __attribute__ ((__packed__)) sdshdr64 {uint64_t len; //已使用uint64_t alloc; //分配的内存,包括结尾\0unsigned char flags; //标志位char buf[];//真正存储字符串的地方
};
它有很多种,这里选择了长度为8字节范围的进行介绍。看起来很简单对不对?
__attribute__ ((packed)) 取消了默认的字节对齐,使得flags前后不会有潜在的填充字段,也便于网络传输(扩展内容参考《理一理字节对齐的那些事》)。len表示buf中存储了的内容的长度;alloc表示已经分配的空间。
那么,定义成这样的SDS有什么好处呢?
常数复杂度获取长度
我们都知道,strlen获取C传统字符串长度的时间复杂度为O(N),而上面的结构中,获取字符串长度的时间复杂度为常数,因为len字段存储了字符串的长度,这样的做法虽然多占用了一点空间,换来的却是效率的提升。
实际上这种做法,在很多地方都很常见,例如C++中的标准容器,如vector获取其大小,string获取其长度。
预分配空间减少内存分配次数
实际上,在创建新的sds的时候,它并不仅仅申请要使用的内存,而是额外申请了一些空间,以避免下次修改的时候又需要重新申请内存。
什么意思呢?
比如说,你有一个字符数组:
char str[] = "hello";
现在你想存储helloworld,怎么办?原先的空间已经确定了,没有办法存储这么多字符串,你只能重新申请空间,然后还要把原先的hello拷贝到新申请的空间中去。如果有频繁地修改字符串,就会导致系统中频繁的内存申请,释放,拷贝,这样还能有高效的redis吗?
因此在redis中,如果有这样的情况,分配新的空间的时候,会预分配一些空间,以备下次使用。
惰性释放空间
而正因如此,出现字符串缩短的时候,也没有必要直接释放内存,只需要更新字符串,记录当前使用的长度即可,你说,下次字符串又增长的时候,不就又用上了吗?
保存二进制数据
看下面的字符串:
char str[] = "hello\0world";
你说下面的字符串,strle长度是多少?不是10,也不是11,而是5。为啥?遇到\0就计算结束了呗。所以要想存储一些特殊的字符串,即中间可能出现\0的字符串,传统的C字符串还不好办呢。
sds就不一样了,管你存什么,反正我长度是记录在len字段中了,输入写入多少,我记录多少。因此它可以保存二进制数据。
扩展可以参考《NULL,0,'\0',“0”,"\0"你真的分得清吗?》
兼容传统字符串的常见用法
虽然redis新定义了sds这样的结构,但是能应用于传统C字符串的函数,同样可以应用于sds。这点在《数组下标-1你见过吗?》中已经简单提到过了。
它在创建的时候,将指针指向了buf,而不是sdshdr64结构的开头,即:
↓ | |||
---|---|---|---|
len | alloc | flag | buf |
所以,类似下面这样的操作,也是安全的:
strlen(pSds);/pSds为sds类型
strcasecmp(pSds, "hello world");//pSds为sds类型
所以你现在明白为什么要指向buf了吧?适用于传统C字符串的函数,也能用在sds上。
而正因如此,我们看到源码中,有很多地方sds使用了下标-1访问一些内容:
例如sdsIncrLen函数中
void sdsIncrLen(sds s, ssize_t incr) {unsigned char flags = s[-1];size_t len;
s[-1],等价于 *(s-1),下面就很容易理解了:
↓ | |||
---|---|---|---|
len | alloc | flag | buf |
所以下次看到下标为负,可不要觉得奇怪了。
总结
实际上当你了解C++的vector的时候,你会发现,它们利用的思想是惊人的相似
预分配
常数获取长度
惰性释放
……
本文旨在通过了解redis中sds的实现,学习其中的设计思想和策略。
关注公众号【编程珠玑】,获取更多Linux/C/C++/数据结构与算法/计算机基础/工具等原创技术文章。后台免费获取经典电子书和视频资源
c 字符串数组_redis为什么不直接使用C字符串,而要自定义简单动态字符串?相关推荐
- redis学习 -- 简单动态字符串
Redis没有使用C语言字符串的形式,通过'\0'作为结尾,而是使用了简单动态字符串(simple dynamic string). 当Redis使用的字符串不需要修改字符串的内容的时候,可以使用C语 ...
- Redis数据结构——简单动态字符串
1.简单动态字符串 redis没有直接用C语言传统的字符串(以空字符结尾的字符数组)表示,而是自己构建了一种名为简单动态字符串(SDS)的抽象类型,并将SDS用作redis的默认字符串表示. 在red ...
- c语言追加字符串_Redis源码解析二--简单动态字符串
Redis 简单动态字符串 1.介绍 Redis兼容传统的C语言字符串类型,但没有直接使用C语言的传统的字符串(以'0'结尾的字符数组)表示,而是自己构建了一种名为简单动态字符串(simple dyn ...
- Redis简单动态字符串
简单动态字符串 Simple Dynamic String是Redis内部自己定义的一种数据类型 在Redis内部, 任何包含字符串的键值对都是由SDS实现的 SDS还被用于缓冲区, 比如AOF缓冲区 ...
- Redis数据结构之简单动态字符串SDS
Redis的底层数据结构非常多,其中包括SDS.ZipList.SkipList.LinkedList.HashTable.Intset等.如果你对Redis的理解还只停留在get.set的水平的话, ...
- Redis之简单动态字符串sds
转载:https://segmentfault.com/a/1190000012262739 redis在处理字符串的时候没有直接使用以'\0'结尾的C语言字符串,而是封装了一下C语言字符串并命名为s ...
- Redis数据结构——简单动态字符串-SDS
1.SDS简介: redis没有使用C语言传统的字符串表示(以空字符结尾的字符数组),而是自己构建了一种名为简单动态字符串(SDS)的抽象类型,并将SDS用作redis的默认字符串表示. 除了用来保存 ...
- 《Reids 设计与实现》第一章 简单动态字符串和链表
<Reids 设计与实现>第一章 简单动态字符串和链表 文章目录 <Reids 设计与实现>第一章 简单动态字符串和链表 一.简单动态字符串 1.简介 2.SDS 的定义 3. ...
- 【Redis笔记】简单动态字符串(SDS)
简单动态字符串:simple dynamic string ,SDS,用作Redis默认字符串表示. 在Redis中,C字符串只会作为字符串字面量用在一些无须对字符串值进行修改的地方. 在Redis需 ...
最新文章
- 互联网产品mysql数据库设计总结
- 公共方法-完整的for循环-for else
- 如果在我爸的朋友圈当杠精,会挨揍吗?
- 【渝粤教育】广东开放大学 地基与基础 形成性考核 (27)
- android 启动页_App启动优化一顿操作猛如虎
- c语言 com组件,com组件 C语言基础.ppt
- c#中使用BackgroundWorker
- 【查找资料】冰点文档下载免费下载百度、豆丁、丁香、畅享、MBALib、道客巴巴、Book118等文库文档
- 前端 环形统计_用Canvas+js 实现简单圆形百分比统计图表
- Mac上怎么把mov文件转成gif文件
- 路由 OSPF简介、OSPF与其它路由的区别、OSPF区域概念、OSPF路由代名词IR、BR、ABR、ASBR概念简介。
- 核心业务8:提现+展示还款信息和回款信息
- 笔记 | 产品经理必懂的技术(三)
- 4G图传移动视频取证-可视安全生产监管-常见问题解答FAQ-1,C/S客户端软件常用操作
- Python int基本用法
- English - every和each的用法和区别
- 华为交换机查看网口光功率命令
- Vue 表情包输入组件
- 亲身历时两个月,这可能是一篇最全面的2021大厂技术岗实习面经
- 【调剂】欢迎调剂重庆理工大学计算机学院(2021年)
热门文章
- python 函数装饰器学习
- pi节点虚拟服务器怎么弄,pi怎么用云服务器做节点
- Python入门100题 | 第066题
- 【LeetCode从零单排】No.135Candy(双向动态规划)
- 支付宝架构师眼里的高并发架构
- spring Transaction Management --官方
- nginx 学习笔记(4) Connection处理方法
- 2020:编程语言大盘点
- 最讨厌心灵鸡汤 所有失败最终都是人不行
- Caffe部署中的几个train-test-solver-prototxt-deploy等说明 (一)