自已动手写Redis【简单动态字符串序列一】

第一章 简单动态字符串

2.1 引言

字符串String是程序设计中最为常见的一种数据结构,也是最为重要的一种数据结构,Hello World!这个最为精典的程序,是绝大部份人学习一种程序设计语言的入门程序,在这个最为精典的入门程序中,Hello World!就是字符串类型,字符串可以用于软件中信息的提醒、保存等,Redis中key都是String类型的,因此了解String类型对于我们了解Redis以及动手写Redis都是非常有益的。

2.2 字符串基本概念

以上是我们最为熟悉的Hello World!程序,这两段程序的输出结果都是一样的,数据结构上采用的也都是字符串,但是两者之间数据的存储区域以及数据的访问方式是完全不同的,程序2-1(a)与2-1(b)分别对应的字符串为常量字符串以及非常量字符串,那么两者之间有什么区别呢?


#include<stdio.h>

int main() {

char *hello = "hello world!";

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

return 0;

}

(a)


#include<stdio.h>

int main() {

char hello[] = "hello world!";

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

return 0;

}

(b)

代码 2-1

2.2.1 常量/非常量

什么是常量字符串,由名字就可以看出,常量字符串就是字符串已经写死了,不容许外界对其进行修改,那么如何辩认字符串是不是常量字符串呢,常量字符串与非常量字符串一个重要区别就是在内存中存储的位置是不一样的,在程序中,将程序使用的空间大概划分成以下几个部份:

1、堆空间,在程序中堆空间一般由程序员进行分配与释放,其生命周期由程序设计人员进行控制。

2、栈空间,由编译器进行分配,用于存放程序中的局部变量、函数调用时的堆栈信息等,栈空间的大小受限于操作系统的软限制以及内存的硬限制。
3、静态存储区,该区域存储的是已初始化过的静态变量(包括局部静态变量以及全局静态态量)、初化过的全局变量以及常量字符串。

4、BSS内(未初始化的数据区),该区域主要用来存放全局未初始化变量,BSS区的数据在程序开始运行之前会被内核初始化为0或者空指针。

5、代码区,存放待执行的机器指令,该区域通常是只读存储区,避免由于对内存的误操作,导致代码运行的不正常。

下面通过一段简单的代码来说明C程序执行时的内存分配情况【代码2-2所示】,代码部分已经写了详细的注解:


#include<stdio.h>

#include<stdlib.h>

int a;//未初始化的全局变量存储在BSS区

static int t;//未初化的全局静态变量,存储在BSS区

int t = 0;//已初始化的全局变量,存储在静态存储区

static int m = 0;//已初始化的全局静态变量,存储在静态存储区

int main() {

char c = ‘a‘;//函数局部变量,存储在栈中

static int m; //未初化的局部静态变量,存储在BSS区

static int q = 0;//已初始化的局部静态变量,存储在静态存储区

char *addr = (char *) malloc(sizeof(char)*2);//在堆中分配2字节

char *p = "Hello World!";//Hello World存放在静态存储区中

}

代码 2-2

操作系统为了节省内存,将程序中的常量字符串存储在静态存储区,程序运行时可以共享这些常量字符串,无需在开设空间用来存储这些常量字符串,我们将存储在静态存储区中的字符串称为常量字符串,将存储在堆栈中的字符串称为非常量是字符串,非常量字符串也就意味着在程序运行的过程中可以动态生成字符串,也可以对已有的字符串进行修改,下面的代码2-3显示了常量/非常量字符串的一个重要区别。


#include<stdio.h>

int main() {

char *ptr = "Hello World!";

*(ptr+1) = ‘W‘;

printf("%s",ptr);

}

(a)


#include<stdio.h>

int main() {

char ptr[] = "Hello World!";

*(ptr+1) = ‘W‘;

printf("%s",ptr);

}

(b)

代码 2-3

在上述代码2-3(a)中,Hello World!是一个常量字符串,存储在静态存储区中,程序编译是不会报错的,但是程序运行时会出现运行时错误,如下图2-4所示,这是因为在程序的执行过程中我们试图通过指针修改静态内存区中的常量字符串,而静态内存区中的常量字符串是只读属性,操作系统不允许外界对其进行修改,因此程序执行时会报运行时错误。

图2-4 运行时错误

与代码2-3(a)相比,代码2-3(b)并没有多大的改变,惟一的改变就是将指针ptr改成了数组,但是这种改变,对编译器却造成了不同的影响,在代码2-3(a)中,编译器发现*ptr是一个指针,会在栈中开设4字节的变量用来存储ptr这个指针的变量,同时发现ptr这个指针变量已经初始化了,指向Hello World!这个字符串,编译器会将Hello World!这个字符串作为常量字符串存储在静态存储区中,并将其地址放在ptr这个指针变量中,代码2-3(b)中,编译器会发现ptr是一个数组,这时编译器的行为就是用Hello World!这个字符串来初始化ptr数组,ptr数组的长度编译器在初始化的过程中会计算出来,即为Hello World!字符串的长度13【备注:C语言中的字符串是以\0进行结尾的,因此实际长度要加上1】。

时间: 2024-08-24 10:44:32

自已动手写Redis【简单动态字符串序列一】的相关文章

redis 简单动态字符串 SDS

redis 没有直接使用c语言传统的字符串表示,而是自己构建了简单动态字符串(SDS)的抽象类型,并将SDS用作redis的默认字符串表示 redis的数据库里面,包含字符串值的键值对在底层都是SDS实现的 执行 rpush fruits "apple" "banana" "pits" 那么redis将在数据库中创建一个新的键值对,其中: 1.键值对的键是一个字符串对象,对象的底层实现是一个保存了字符串的fruits的SDS 2.键值对的值是一个

Redis底层探秘(一):简单动态字符串(SDS)

redis是我们使用非常多的一种缓存技术,他的性能极高,读的速度是110000次/s,写的速度是81000次/s.这么高的性能背后,到底是怎么样的实现在支撑,这个系列的文章,我们一起去看看. redis的底层数据结构有以下7种,包括简单动态字符串(SDS),链表.字典.跳跃表.整数集合.压缩列表.对象.今天我们一起看下简单动态字符串(simple dynamic string),后面的文章以SDS简称. SDS简介 Redis没有直接使用C语言传统的字符串表示(以空字符结尾的字符串数组,以下简称

Redis数据结构(一)简单动态字符串

Redis的字符串采用的是自定义的struct,名字叫做简单动态字符串(simple dynamic string,SDS). 结构如下: struct sdshdr{ int len; int free; char buf[]; }; 采用如此结构的好处是: [1]获取length的时候复杂度为O(1),不需要O(n): [2]动态分配空间,避免缓冲区溢出,避免每次修改或者append都重新分配: [3]二进制安全: 关于第一点显而易见,第二点,为了减少修改字符串带来的内存重分配次数,redi

Redis源码解析01: 简单动态字符串SDS

Redis没有直接使用C字符串(以'\0'结尾的字符数组),而是构建了一种名为简单动态字符串( simple  dynamic  string, SDS)的抽象类型,SDS设计API实现对字符串的各种修改. 1:SDS的定义 在sds.h中,定义了结构体sdshdr表示SDS,其定义如下: struct sdshdr { unsigned int len; unsigned int free; char buf[]; }; len记录SDS保存的字符串的长度(不包括末尾的'\0'):free记录

Redis数据结构之简单动态字符串

Redis没有直接使用C语言传统的字符串表示(以空字符结尾的字符数组), 而是自己构建了一种名为简单动态字符串(simple dynamic string,SDS)的抽象类型, 并将SDS用作Redis的默认字符串表示.在Redis中,C字符串只会作为字符串字面量,用在一些无需对字符串值进行修改的地方,例如打印日志. 一.SDS的结构定义 示例: 二.SDS与C字符串的区别1. 常数复杂度获取字符串长度C字符串长度计算:遍历整个字符串直至遇到代表字符串结尾的空字符,时间复杂度为O(N).SDS长

Redis数据结构之简单动态字符串SDS

Redis的底层数据结构非常多,其中包括SDS.ZipList.SkipList.LinkedList.HashTable.Intset等.如果你对Redis的理解还只停留在get.set的水平的话,是远远不足以应对面试提问的.本文简单介绍了Redis底层最重要的数据结构 - 简单动态字符串(SDS) Redis使用C语言开发,但并没有使用C语言传统的字符串表示(以空字符结尾的字节数组,以下简称C字符串),而是自己构建了一种名为简单动态字符串的(simple dynamic string,SDS

小白的Redis学习(一)-SDS简单动态字符串

本文为读<Redis设计与实现>的记录.该书以Redis2.9讲解Redis相关内容.请注意版本差异. Redis使用C语言实现,他对C语言中的char类型数据进行封装,构建了一种简单动态字符串(以下简称SDS),该字符串的结构如下 struct sdshdr{ //记录buf数组中已使用字节的数量 //获取字符串的长度时,就是直接返回的这个字段的值 int len; //记录buf数组中未使用字节的数量 int free; //字节数组,用于保存字符串 char buf[]; } SDS遵循

redis数据结构-SDS简单动态字符串

一.使用场景 redis没有直接使用C语言传统的字符串表示(以空字符结尾的字符数组,以下简称C字符串),而是构建了简单动态字符串(Simple dynamic String SDS)的抽象类型,并将SDS作为默认的字符串表示形式. redis里面C字符串只用在字符串字面量(String literal) ,用在一些无需对字符串修改的地方. 比如打印日志redisLog(REDIS_WARING,"redis is now ready to exit, bye bye...") 1.re

简单动态字符串

Redis没有直接使用C语言传统的字符串表示(以空字符结尾的字符数组,以下简称C字符串),而是自己构建了一种名为简单动态字符串(simple dynamic string,SDS)的抽象类型,并将SDS用作Redis的默认字符串表示. 当Redis需要的不仅仅是一个字符串面量,而是一个可以被修改的字符串值时,Redis就会使用SDS来表示字符串值,比如在Redis的数据库里,包含字符串值得键值对在底层都是由SDS来实现得. 客户端执行命令 set msg "hello world" 那