欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  数据库

Redis 中利用 sizeof 从 sds 转为 sdshdr

程序员文章站 2022-04-21 19:29:19
...

今天在阅读 Redis 源码时看到 sds.h 中的 sdslen 和 sdsavail 中看到一行语句不是很理解 struct sdshdr * sh = ( void * )( s - ( sizeof ( struct sdshdr ))); 这里 s 是 const sds 类型,相关类型定义如下 typedef char * sds ; struct sdshdr { unsigned

今天在阅读 Redis 源码时看到 sds.h 中的 sdslensdsavail 中看到一行语句不是很理解

struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));

这里 sconst sds 类型,相关类型定义如下

typedef char *sds;
struct sdshdr {
    unsigned int len;
    unsigned int free;
    char buf[];
};

当使用 sdsnewlen(const void *init, size_t initlen) 创建一个新的动态字符串时,Redis 会创建一个 struct sdshdr *sh 结构体,并将这个结构体的最后一个字符数组,也就是 sh->buf 返回,这样返回值是个普通的 C 字符串,因此便可以使用一些 string.hstdlib.h 中的 C 字符串函数,而当想访问 sds 的 len 和 free 属性时,就需要先转成 struct sdshdr 结构。但是直接用 s-(sizeof(struct sdshdr)) 却是不知道怎么回事。

后来直接使用那一行代码进行 Google,然后找到了结果。

C99 中的标准规定:

As a special case, the last element of a structure with more than one named member may have an incomplete array type; this is called a flexible array member. In most situations, the flexible array member is ignored. In particular, the size of the structure is as if the flexible array member were omitted except that it may have more trailing padding than the omission would imply.

However, when a . (or ->) operator has a left operand that is (a pointer to) a structure with a flexible array member and the right operand names that member, it behaves as if that member were replaced with the longest array (with the same element type) that would not make the structure larger than the object being accessed; the offset of the array shall remain that of the flexible array member, even if this would differ from that of the replacement array. If this array would have no elements, it behaves as if it had one element but the behavior is undefined if any attempt is made to access that element or to generate a pointer one past it.

struct sdshdr 结构体中的最后一个 char buf[] 被称为 flexible array member,在计算结构体大小的时候是不记入在内的,因此 sizeof(struct sdshdr) 实际上就是 sizeof(unsigned int) + sizeof(unsigned int) 这样就能理解了。

-----------
|5|0|redis|
-----------
^   ^
sh  sh->buf

所谓的 sizeof(struct sdshdr) 实际上是就是 lenfree 所占的大小,因此用 sh->buf 的位置减去 sizeof(struct sdshdr) 就是 sh 的位置了,再经过 struct sdshdr * 转换,就可以得到 sds 对应的 struct sdshdr 结构体了。

参考资料:

  • Hacking Strings
  • redis sds数据结构的指针技巧