在Redis中,除了一些用到字符串字面量的情况外,其他都是通过简单动态字符串结构体来代替c字符串,当然是为了更加方便的操作Redis字符串,比如Redis中的键的名称存储使用的就是Redis字符串。
一、sds字符串声明
1、声明sds字符串:即一个c字符串指针
typedef char *sds;
- typedef是用来定义类型别名的,其用法是:typedef 原类型 自定义类型
上面的语句也可以是:
typedef char* sds;
可能这样更容易看出sds是一个指向char类型的指针,因此后面使用sds的时候就表示它是一个指向char类型的指针。
2、声明sdshdr结构体:用来保存sds字符串的信息
struct sdshdr {
int len;
int free;
char buf[];
}
结构体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指针(一般需要强制类型转换为指向某类型的指针)
例子:
char *ptr;
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为填充的长度(申请的空间大小)
例子:
char buf[5];
char *str = "name";
memcpy(buf, str, 5);
printf("%s\n", buf); //"name"
printf("&str = %p, &buf = %p", str, buf); //两个变量不是指向同一空间地址的
(4)、strlen
功能:计算字符串的长度
位置:string.h
用法:strlen(const char *str)
参数说明:str为字符串指针
例子:
char buf[5] = "name";
char *str = "name";
printf("length = %d, sizeof = %d\n", strlen(buf), sizeof(buf)); //4, 5
printf("length = %d, sizeof = %d\n", strlen(str), sizeof(str)); //4, 8
扩展:为什么str的sizeof是8?
因为这里sizeof计算的是str这个指针占用的内存大小,指针占用的内存大小是固定的
struct stu {
char name[20];
int age;
double score;
};
int num = 20;
double d = 2.34544;
int *ptr = #
double *pd = &d;
struct stu *pstu;
printf("%d, %d\n", sizeof(ptr), sizeof(pd)); //8,8
printf("%d\n", sizeof(pstu)); //8
三、sds字符串其他操作函数
1、计算sds字符串的长度:sdslen
直接读取sdshdr结构体len属性即可。
struct sdshdr *sh = (void *)(s - (sizeof(struct sdshdr)))
有没有感觉有点奇怪?这个sh指针是怎么得到的?
突破点:
- sh是一个指向struct sdshdr的结构体指针
- 结构体和数组类似,它的成员在内存中是一个连续的空间
- sdslen是在sds初始化后才能使用的(这里的s其实是指向sds结构体中buf的指针)
我们结合sdshdr结构体,做一个实验:初始化结构体sdshdr
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef char *sds;
struct sdshdr {
int len;
int free;
char buf[];
};
int main()
{
char *str = "name";
struct sdshdr sdsobj, *ptr;
ptr = &sdsobj;
ptr->len = 4;
ptr->free = 0;
memcpy(ptr->buf, str, 5);
printf("&ptr->len:%p\n", &ptr->len);
printf("&ptr->free:%p\n", &ptr->free);
printf("&ptr->buf:%p\n", &ptr->buf);
printf("ptr:%p\n", ptr);
return 0;
}
从上面的实验可以看出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)
参数说明:
实例:
#include <stdio.h>
#include <string.h>
int main()
{
char buffer[12] = "hello,world";
printf("memset before:%s\n", buffer); //hello,world!
memset(buffer, '*', strlen(buffer));
printf("memset after:%s\n", buffer); //***********
memset(buffer+1, 0, strlen(buffer)-1);
printf("memset again:%s\n", buffer); //*
return 0;
}
(2)、strchr
功能:在字符串查找某个字符首次出现的位置
用法:strchr(const char *str, int c)
参数说明:
返回值:字符在字符串的指针位置
实例:
#include <stdio.h>
#include <string.h>
int main()
{
char *str = "<a href='http://www.baidu.com'>百度</a>";
char c = 'a';
printf("%p\n", str); //0x400800
printf("%p\n", strchr(str, c)); //0x400801
return 0;
}