菜鸟从Redis源码学习C语言之Redis简单动态字符串

在Redis中,除了一些用到字符串字面量的情况外,其他都是通过简单动态字符串结构体来代替c字符串,当然是为了更加方便的操作Redis字符串,比如Redis中的键的名称存储使用的就是Redis字符串。

一、sds字符串声明

1、声明sds字符串:即一个c字符串指针
  1. typedef char *sds;
  • typedef是用来定义类型别名的,其用法是:typedef 原类型 自定义类型

上面的语句也可以是:

  1. typedef char* sds;

可能这样更容易看出sds是一个指向char类型的指针,因此后面使用sds的时候就表示它是一个指向char类型的指针。

2、声明sdshdr结构体:用来保存sds字符串的信息
  1. struct sdshdr {
  2. int len;
  3. int free;
  4. char buf[];
  5. }

结构体sdshdr有三个成员:

  • len:表示sds字符串的长度
  • free:表示sds字符串闲置的内存空间
  • buf[]:存放sds字符串值

因此len+free+1即为sds字符串所用的总的内存空间大小

注:因为C字符串都是以\o为结尾的字符数组,Redis也继承了这一传统,而结尾的\o并不记入len之中。(所以这里的+1指\o占用的1个字节的空间)

二、sds字符串初始化函数

sds字符串初始化并不像c字符串那样直接初始化即可,需要使用Redis内置的函数来实现。这些函数都返回一个字符串指针

1、sdsnewlen:初始化sds字符串,并指定字符串的长度
  • 功能:
  • 为sdshdr结构体申请内存空间,并设置成员的值
  • 返回指向buf的字符串指针
2、sdsnew:初始化sds字符串

通过strlen函数来获取字符串的长度,并调用sdsnewlen来初始化字符串

3、sdsempty:以一个空的字符串来初始化字符串

通过调用sdsnewlen来初始化一个空字符串

相关知识点梳理

(1)、malloc
  • 功能:申请指定大小的内存空间

  • 位置:stdlib.h

  • 用法:malloc(size_t size)

  • 参数说明:size 表示申请的内存空间大小(字节为单位)

  • 返回值:返回一个void指针(一般需要强制类型转换为指向某类型的指针)

  • 例子:

  1. char *ptr;
  2. ptr = (char *)malloc(20);
(2)、calloc
  • 功能:同时申请几块指定大小的内存空间

  • 位置:stdlib.h

  • 用法:calloc(size_t block_size, size_t size)

  • 参数说明:block_size申请的内存块的数量

  • size为每块内存块的大小

  • 返回值:返回一个void指针

(3)、memcpy
  • 功能:为某个指针分配内存空间并使用另外一个指针指向的内容进行填充(可以认为是指针内容复制)

  • 位置:string.h

  • 用法:memcpy(void ptr, const void src, size_t len)

  • 参数说明:

    • ptr为需要申请内存空间的指针变量

    • src用于填充的内容

    • len为填充的长度(申请的空间大小)

例子:

  1. char buf[5];
  2. char *str = "name";
  3. memcpy(buf, str, 5);
  4. printf("%s\n", buf); //"name"
  5. printf("&str = %p, &buf = %p", str, buf); //两个变量不是指向同一空间地址的
(4)、strlen
  • 功能:计算字符串的长度

  • 位置:string.h

  • 用法:strlen(const char *str)

  • 参数说明:str为字符串指针

  • 例子:

  1. char buf[5] = "name";
  2. char *str = "name";
  3. printf("length = %d, sizeof = %d\n", strlen(buf), sizeof(buf)); //4, 5
  4. printf("length = %d, sizeof = %d\n", strlen(str), sizeof(str)); //4, 8

扩展:为什么str的sizeof是8?

因为这里sizeof计算的是str这个指针占用的内存大小,指针占用的内存大小是固定的

  1. struct stu {
  2. char name[20];
  3. int age;
  4. double score;
  5. };
  6. int num = 20;
  7. double d = 2.34544;
  8. int *ptr = #
  9. double *pd = &d;
  10. struct stu *pstu;
  11. printf("%d, %d\n", sizeof(ptr), sizeof(pd)); //8,8
  12. printf("%d\n", sizeof(pstu)); //8

三、sds字符串其他操作函数

1、计算sds字符串的长度:sdslen

直接读取sdshdr结构体len属性即可。

  1. struct sdshdr *sh = (void *)(s - (sizeof(struct sdshdr)))

有没有感觉有点奇怪?这个sh指针是怎么得到的?

突破点:

  • sh是一个指向struct sdshdr的结构体指针
  • 结构体和数组类似,它的成员在内存中是一个连续的空间
  • sdslen是在sds初始化后才能使用的(这里的s其实是指向sds结构体中buf的指针)

我们结合sdshdr结构体,做一个实验:初始化结构体sdshdr

  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <stdlib.h>
  4. typedef char *sds;
  5. struct sdshdr {
  6. int len;
  7. int free;
  8. char buf[];
  9. };
  10. int main()
  11. {
  12. char *str = "name";
  13. struct sdshdr sdsobj, *ptr;
  14. ptr = &sdsobj;
  15. ptr->len = 4;
  16. ptr->free = 0;
  17. memcpy(ptr->buf, str, 5);
  18. printf("&ptr->len:%p\n", &ptr->len);
  19. printf("&ptr->free:%p\n", &ptr->free);
  20. printf("&ptr->buf:%p\n", &ptr->buf);
  21. printf("ptr:%p\n", ptr);
  22. return 0;
  23. }

从上面的实验可以看出sdshdr指针指向的地址空间与buf指向的地址空间差值为8(实际差值为sizeof(struct sdshdr),因为在结构体声明中buf没有分配任何空间)

所以s - (sizeof(struct sdshdr)的地址即为sdshdr指针的地址

2、计算sds字符串的可用空间大小:sdsavail

直接读取sdshdr结构体的free属性即可。

相关知识点梳理:

(1)、memset
  • 功能:将指向某块内存空间中的每个字节的内容全部设置成指定的字符,一般是为重新申请的内存空间做初始化工作

  • 用法:memset(void *ptr, int ch, size_t n)

  • 参数说明:

    • @param void * ptr:内存空间地址(指针,要填充的内存块)

    • @param int ch:填充的字符

    • @param size_t n:内存块大小

  • 实例:

  1. #include <stdio.h>
  2. #include <string.h>
  3. int main()
  4. {
  5. char buffer[12] = "hello,world";
  6. printf("memset before:%s\n", buffer); //hello,world!
  7. memset(buffer, '*', strlen(buffer));
  8. printf("memset after:%s\n", buffer); //***********
  9. memset(buffer+1, 0, strlen(buffer)-1);
  10. printf("memset again:%s\n", buffer); //*
  11. return 0;
  12. }
(2)、strchr
  • 功能:在字符串查找某个字符首次出现的位置

  • 用法:strchr(const char *str, int c)

  • 参数说明:

    • @param const char *str:目标字符串

    • @param int c:为搜索查找的字符

  • 返回值:字符在字符串的指针位置

  • 实例:

  1. #include <stdio.h>
  2. #include <string.h>
  3. int main()
  4. {
  5. char *str = "<a href='http://www.baidu.com'>百度</a>";
  6. char c = 'a';
  7. printf("%p\n", str); //0x400800
  8. printf("%p\n", strchr(str, c)); //0x400801
  9. return 0;
  10. }