柔性数组(Redis源码学习)
柔性数组(Redis源码学习)
1. 问题背景
在阅读Redis源码中的字符串有如下结构,在sizeof(struct sdshdr)
得到结果为8,在后续内存申请和计算中也用到。其实在工作中有遇到过这种 struct结构 + 应用数据的情况,但没有意识到自己使用的是柔性数组,在学习阅读Redis代码中,遇到该方法,就特总结记录之。
/* * 类型别名,用于指向 sdshdr 的 buf 属性 */
typedef char * sds;
/* * 保存字符串对象的结构 */
struct sdshdr {
// buf 中已占用空间的长度
int len;
// buf 中剩余可用空间的长度
int free;
// 数据空间
char buf[];
};
2. 柔性数组
柔性数组(flexible array member)也叫伸缩性数组成员,这种结构产生与对动态结构体的去求。在日常编程中,有时需要在结构体中存放一个长度是动态的字符串(也可能是其他数据类型)。
一般的做法,是在结构体中定义一个指针成员,这个指针成员指向该字符串所在的动态内存空间。在通常情况下,如果想要高效的利用内存,那么在结构体内部定义静态的数组是非常浪费的行为。其实柔性数组的想法和动态数组的想法是一样的。
柔性数组用来在结构体中存放一个长度动态的字符串。
本文基于redis 的sds.c源码,进行简单编码验证测试,其实这种柔性数组,在工作中用到过,但是没有意识到这是柔性数组。
上述struct sdshdr
结构中,要注意:最后一个变量 buf 数组中,没有长度,这和自己遇到的正常的使用方式不一样,新的知识点
这种用法是C语言中的柔性数组,上面 的sizeof(sdshdr )结果是8,即后面的buf不占空间,只是一个符号,测试上面sdshdr结果如下:
int main(int argc,char **argv){
struct sdshdr t;
printf("int len:%d\n",sizeof(int));
printf("sdshdr len:%d\n",sizeof(struct sdshdr));
printf("Address:\n");
printf("t\t %p\n", &t);
printf("t.len\t %p\n", &(t.len));
printf("t.free\t %p\n", &(t.free));
printf("t.buf\t %p\n", &(t.buf));
return 0;
}
RHEL6.9上执行上面代码块得到结果如下:
$ ./sdshdr
int len:4
sdshdr len:8
Address:
t 0x7fff9572fa50
t.len 0x7fff9572fa50
t.free 0x7fff9572fa54
t.buf 0x7fff9572fa58
可以看到 t.buf 是该结构的最后的地址,是最后一个点,简单图示如下:
如果后续再malloc相关的内存,则就会在t.buf后面连续,简单编写代码进行验证。要加入对应的sds.h文件,或者直接将结构定义在main函数之前。
int main(int argc,char **argv){
struct sdshdr t;
printf("int len:%d\n",sizeof(int));
printf("sdshdr len:%d\n",sizeof(struct sdshdr));
printf("Address:\n");
printf("t\t %p\n", &t);
printf("t.len\t %p\n", &(t.len));
printf("t.free\t %p\n", &(t.free));
printf("t.buf\t %p\n", &(t.buf));
printf("sizeof(char):\t %d\n", sizeof(char));
struct sdshdr *p=(struct sdshdr*)malloc(sizeof(struct sdshdr) + sizeof(char)*8);
printf("After malloc the struct's size is %d\n",sizeof(struct sdshdr));
printf("Address:\n");
printf("p\t %p\n", p);
printf("p->len\t %p\n", &(p->len));
printf("p->free\t %p\n", &(p->free));
printf("p->buf\t %p,sizeof(p):%d\n", &(p->buf),sizeof(p));
memset(p,0,sizeof(struct sdshdr) + sizeof(char)*8);
char *str="Hello";
memcpy(p->buf,str,strlen(str));
printf("p->buf:%s\n",p->buf);
char *str1="HelloWorldttttttt";
memcpy(p->buf,str1,sizeof(char)*8-1);
printf("p->buf:%s\n",p->buf);
printf("strlen(p->buf):%d\n",strlen(p->buf));
return 0;
}
上述代码进行编译,获得可执行文件,执行结果如下:
$ ./sdshdr
int len:4
sdshdr len:8
Address:
t 0x7ffea0a8c420
t.len 0x7ffea0a8c420
t.free 0x7ffea0a8c424
t.buf 0x7ffea0a8c428
sizeof(char): 1
After malloc the struct's size is 8
Address:
p 0x1bc3010
p->len 0x1bc3010
p->free 0x1bc3014
p->buf 0x1bc3018,sizeof(p):8
p->buf:Hello
p->buf:HelloWo
strlen(p->buf):7
$
## 3. 使用方法
从C99开始便支持了不完整类型实现柔性数组成员。为什么使用不完整类型呢?
```C language
int a[] = {10};
看到这个声明语句,我们发现a[]其实就是个数组记号,不完整类型,由于赋值语句,所以在编译时便确定了数组的大小,是一个完整的数组类型。
在结构体中便利用不完整类型在运行对动态的数组进行指明。
C99标准的定义如下:
struct Test{
int a;
char p[]; // 不只是char类型,其他类型同样也是可以
}
由于声明内存连续性的关系,柔性数组成员必须定义在结构体的最后一个,并且不能是唯一的成员。
我们再来看一看整个结构体(包含数组内存的分布情况),进行简单编码验证。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Test
{
int a;
char p[];
} Test;
int main()
{
Test *t=(Test*)malloc(sizeof(Test)+sizeof(char)*(10+1));
printf("sizeof(int):%d,sizeof(Test):%d\n",sizeof(int),sizeof(Test));
strcpy(t->p,"hello");
printf("t->p:%s\n", (t->p));
printf("Address:\n");
printf("t\t %p\n", t);
printf("t.a\t %p\n", &(t->a));
printf("t.p\t %p\n", (t->p));
free(t); //只需要释放一次内存
return 0;
}
在linux上的执行结果如下:
$ ./sdshdr
sizeof(int):4,sizeof(Test):4
t->p:hello
Address:
t 0x7e0010
t.a 0x7e0010
t.p 0x7e0014
4. 小结
- 在结构体中存放一个长度是动态数据类型时,可以考虑到柔性数组。
- 一般做法,是在结构体中定义一个指针成员,这个指针成员指向所在的动态内存空间。
- 该指针成员,不占结构体空间,只是一个符号。
- 柔性数组成员必须定义在结构体的最后一个,并且不能是唯一的成员。
5. 参考文献
https://www.cnblogs.com/davygeek/p/5748852.html
https://blog.csdn.net/qq_40477151/article/details/78905567
https://www.cnblogs.com/pluviophile/p/7571410.html
本人才疏学浅,参考网络文章及代码验证,如有错误不当之处,请批评指正,如有侵权,请立即联系我进行删除。
如果能为您带来一点点帮助,那将是我的荣幸,多谢您关注和转发推荐,谢谢!
柔性数组(Redis源码学习)的更多相关文章
- Redis源码学习:字符串
Redis源码学习:字符串 1.初识SDS 1.1 SDS定义 Redis定义了一个叫做sdshdr(SDS or simple dynamic string)的数据结构.SDS不仅用于 保存字符串, ...
- Redis源码学习:Lua脚本
Redis源码学习:Lua脚本 1.Sublime Text配置 我是在Win7下,用Sublime Text + Cygwin开发的,配置方法请参考<Sublime Text 3下C/C++开 ...
- redis源码学习之slowlog
目录 背景 环境说明 redis执行命令流程 记录slowlog源码分析 制造一条slowlog slowlog分析 1.slowlog如何开启 2.slowlog数量限制 3.slowlog中的耗时 ...
- __sync_fetch_and_add函数(Redis源码学习)
__sync_fetch_and_add函数(Redis源码学习) 在学习redis-3.0源码中的sds文件时,看到里面有如下的C代码,之前从未接触过,所以为了全面学习redis源码,追根溯源,学习 ...
- redis源码学习之lua执行原理
聊聊redis执行lua原理 从一次面试场景说起 "看你简历上写的精通redis" "额,还可以啦" "那你说说redis执行lua脚本的原理&q ...
- redis源码学习之工作流程初探
目录 背景 环境准备 下载redis源码 下载Visual Studio Visual Studio打开redis源码 启动过程分析 调用关系图 事件循环分析 工作模型 代码分析 动画演示 网络模块 ...
- redis源码学习-skiplist
1.初步认识跳跃表 图中所示,跳跃表与普通链表的区别在于,每一个节点可以有多个后置节点,图中是一个4层的跳跃表 第0层: head->3->6->7->9->12-> ...
- Redis源码学习1-sds.c
https://github.com/huangz1990/redis-3.0-annotated/blob/unstable/src/sds.c#L120 /* SDSLib, A C dynami ...
- redis源码学习_字典
redis中字典有以下要点: (1)它就是一个键值对,对于hash冲突的处理采用了头插法的链式存储来解决. (2)对rehash,扩展就是取第一个大于等于used * 2的2 ^ n的数作为新的has ...
随机推荐
- Linux下Shell实现服务器IP监测
实验室有一个服务器放在机房,装的是Ubuntu Server,IP为自动分配,因此一旦IP有变化就无法远程操作,必须去机房记录新的IP.学了几天Shell之后想,是不是可以定时检测其IP的变化,一旦有 ...
- openssl自签发证书
DOMAIN=www.example.com openssl genrsa -out ${DOMAIN}.key # 生成私有key openssl req -x509 -new -nodes -ke ...
- 微信小程序开发(后端Java)
微信使用的开发语言和文件很「特殊」. 小程序所使用的程序文件类型大致分为以下几种: ①WXML(WeiXin Mark Language,微信标记语言) ②WXSS(WeiXin Style Shee ...
- 如何屏蔽 iOS 软件自动更新,去除更新通知和标记
适用于 iOS.iPadOS 和 watchOS,即 iPhone.iPad 和 Apple Watch 通用. 请访问原文链接:https://sysin.org/article/Disable-i ...
- 26.Qt Quick QML-RotationAnimation、PathAnimation、SmoothedAnimation、Behavior、PauseAnimation、SequentialAnimation和ParallelAnimation
1.RotationAnimationRotationAnimation也是继承于PropertyAnimation组件,但是它有点特殊,它只需要指定taget目标对象,并且不需要指定property ...
- 使用BeautifulSoup高效解析网页,再也不用担心睡不着觉了
BeautifulSoup是一个可以从 HTML 或 XML 文件中提取数据的 Python 库 那需要怎么使用呢? 首先我们要安装一下这个库 1.pip install beautifulsoup4 ...
- linux中用iptables开启指定端口
linux中用iptables开启指定端口 centos默认开启的端口只有22端口,专供于SSH服务,其他端口都需要自行开启. 1.修改/etc/sysconfig/iptables文件,增加如下 ...
- Go基础结构与类型01---常量变量表达式
// 包名(main包下的main函数是程序的入口) package main // 导入sdk(software developing kit)中的fmt包 import "fmt&quo ...
- 人工智能AI Boosting HMC Memory Chip
人工智能AI Boosting HMC Memory Chip Innosilicon的AI Boosting HMC存储芯片适用于高速,高带宽和高性能存储领域,例如AI边缘,数据中心,自动化等. H ...
- MegEngine推理性能优化
MegEngine推理性能优化 MegEngine「训练推理一体化」的独特范式,通过静态图优化保证模型精度与训练时一致,无缝导入推理侧,再借助工业验证的高效卷积优化技术,打造深度学习推理侧极致加速方案 ...