前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >数组下标-1你见过吗?

数组下标-1你见过吗?

作者头像
编程珠玑
发布2020-05-22 18:27:28
2.2K0
发布2020-05-22 18:27:28
举报
文章被收录于专栏:编程珠玑编程珠玑

不知道你有没有见过-1作为数组下标的,我算是见到了。当然这一点在Python之类的语言中毫不稀奇。

下标-1的代码

这里redis源码中的一部分:

代码语言:javascript
复制
sds sdsMakeRoomFor(sds s, size_t addlen) {
    void *sh, *newsh;
    size_t avail = sdsavail(s);
    size_t len, newlen;
    char type, oldtype = s[-1] & SDS_TYPE_MASK;
    int hdrlen;
    //摘取部分代码

其中sds定义如下:

代码语言:javascript
复制
typedef char* sds

我们忽略其中的逻辑,其实可以看到在sds中有很多使用-1作为下标的。那么这里到底有什么含义?又有什么好处呢?别急!

数组下标访问

我们都知道,数组下标可以以O(1)复杂度访问一个数组的元素:

代码语言:javascript
复制
int arr[] = {1,2,3,4,5};
printf("%d\n",arr[2]);

上面的示例代码中,就是访问了数组的第三个元素,实际上,作用等价于;

代码语言:javascript
复制
printf("%d\n",*(arr+2));

这一点,我已经在《C语言入坑指南-数组之谜》中解释过了。arr相当于int*类型指针,+2,意味着指针向前移动了sizeof(int) * 2,即8字节的位置,最终指向数字3。关于指针的算术运算,也可以参考《void*是怎样的存在》。

再看下面:

代码语言:javascript
复制
int *pArr = arr + 2;
printf("%d\n",pArr[-1]);//printf("%d\n",*(pArr-1))

这里也很好理解,无非就是pArr指向数字3,然后又使用下标-1,访问了前一个位置,最终自然指向了数字2,打印的也是2。

到目前为止,一切都还合情合理。

但是,我们别忘了,数组越界是一件很可怕事情。比如,你试试:

代码语言:javascript
复制
printf("%d\n",arr[16]);
printf("%d\n",arr[-1]);

运气不好的时候,程序不会挂死,只是打印出一些莫名其妙的值,运气好的时候,程序挂死。(有人可能会问,为什么程序不会挂死,运气还算好呢?因为不挂死的时候,一些隐藏的问题更让人抓狂)。

到这里我们明白了,为了数组下标访问不越界,通常下标范围是0~size-1,其中size是数组元素个数。

那么问题来了,redis的源码中为什么要用-1作为下标呢?

巧妙的-1

实际上,sds其中的一个结构(8bit范围长度)定义是这样的:

代码语言:javascript
复制
struct __attribute__ ((__packed__)) sdshdr8 {
    uint8_t len; /* used */
    uint8_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};

其中

代码语言:javascript
复制
__attribute__ ((__packed__))

是取消字节对齐,关于字节对齐,可以参考《理一理字节对齐的那些事》,本文不再赘述。

而在每一次创建新的sds结构的时候,返回的指针,都是指向buf这里从源码的sdsnewlen函数中很容易看出: 即:

1字节

1字节

1字节

len

alloc

flags

buf

所以我们看到前面这样的代码也就不足为奇了:

代码语言:javascript
复制
oldtype = s[-1] & SDS_TYPE_MASK;

这里的-1相当于将指针指向了flags字段:

1字节

1字节

1字节

len

alloc

flags

buf

相信到这里你应该能理解-1的作用了。

那么为什么要这么做呢?想象一下,使用strlen是不是直接可以计算sds字符串的长度了呢?

总结

一般来说-1这样的用法是不太建议的,或者说,在使用下标访问数组时,必须确保不越界。

预告

redis中为什么不用普通的char*存储字符串,而要使用所谓的简单动态字符串?背后究竟隐藏着怎样的秘密?请看下文详细分解。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-05-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 编程珠玑 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 下标-1的代码
  • 数组下标访问
  • 巧妙的-1
  • 总结
  • 预告
相关产品与服务
云数据库 Redis
腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档