见微知著 带你透过内存看 Slice 和 Array的异同
hi, 大家好,我是 hhf。
有这么一个 Go 面试题:请说出 slice 和 array 的区别?
这简直就是送分题。现在思考一下,你咋样回答才能让面试官满意呢?
我这里就不贴这道题的答案了。但是我想内存方面简单分析下 slice 和 array 的区别。
Array
func main() {
as := [4]int{10, 5, 8, 7}
fmt.Println("as[0]:", as[0])
fmt.Println("as[1]:", as[1])
fmt.Println("as[2]:", as[2])
fmt.Println("as[3]:", as[3])
}
这段很简单的代码,声明了一个 array。当然输出结果也足够简单。
我们现在玩点花活,如何通过非正常的手段访问数组里面的元素呢?在做这个事情之前是需要先知道 array 的底层结构的。其实很简单,Go array 就是一块连续的内存空间。如下图所示

写一段简单的代码,我们不通过下标访问的方式去获取元素。通过移动指针的方式去获取对应位置的指针。
func main() {
as := [4]int{10, 5, 8, 7}
p1 := *(*int)(unsafe.Pointer(&as))
fmt.Println("as[0]:", p1)
p2 := *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&as)) + unsafe.Sizeof(as[0])))
fmt.Println("as[1]:", p2)
p3 := *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&as)) + unsafe.Sizeof(as[0])*2))
fmt.Println("as[2]:", p3)
p4 := *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&as)) + unsafe.Sizeof(as[0])*3))
fmt.Println("as[3]:", p4)
}
结果:
as[0]: 10
as[1]: 5
as[2]: 8
as[3]: 7
下图演示下获取对应位置的值的过程:

Slice
同样对于 slice 这段简单的代码:
func main() {
as := []int{10, 5, 8, 7}
fmt.Println("as[0]:", as[0])
fmt.Println("as[1]:", as[1])
fmt.Println("as[2]:", as[2])
fmt.Println("as[3]:", as[3])
}
想要通过移动指针的方式获取 slice 对应位置的值,仍然需要知道 slice 的底层结构。如图:

func main() {
as := []int{10, 5, 8, 7}
p := *(*unsafe.Pointer)(unsafe.Pointer(&as))
fmt.Println("as[0]:", *(*int)(unsafe.Pointer(uintptr(p))))
fmt.Println("as[1]:", *(*int)(unsafe.Pointer(uintptr(p) + unsafe.Sizeof(&as[0]))))
fmt.Println("as[2]:", *(*int)(unsafe.Pointer(uintptr(p) + unsafe.Sizeof(&as[0])*2)))
fmt.Println("as[3]:", *(*int)(unsafe.Pointer(uintptr(p) + unsafe.Sizeof(&as[0])*3)))
var Len = *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&as)) + uintptr(8)))
fmt.Println("len", Len)
var Cap = *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&as)) + uintptr(16)))
fmt.Println("cap", Cap)
}
结果:
as[0]: 10
as[1]: 5
as[2]: 8
as[3]: 7
len 4
cap 4
用指针取 slice 的底层 Data 里面的元素跟 array 稍微有点不同:
- 对 slice 变量 as 取地址后,拿到的是 SiceHeader 的地址,对这个指针进行移动,得到是 slice 的 Data, Len, Cap。
- 所以当拿到 Data 的值时,我们拿到的是 Data 所指向的 array 的首地址的值。
- 由于这个值是个指针,需要对这个值 *Data, 取到 array 真正的首地址的指针值
- 然后对这个值 &(*Data),获取到真正的首地址,然后对这个值进行指针的移动,才能获取到 slice 的数组里的值
获取 slice cap 和 len:

获取 slice 的 Data:


见微知著 带你透过内存看 Slice 和 Array的异同的更多相关文章
- 透过IL看C#:switch语句(转)
透过IL看C# switch语句(上) 摘要: switch语句是 C#中常用的跳转语句,可以根据一个参数的不同取值执行不同的代码.本文介绍了当向 switch语句中传入不同类型的参数时,编译器为其生 ...
- iOS: 学习笔记, 透过Boolean看Swift(译自: https://developer.apple.com/swift/blog/ Aug 5, 2014 Boolean)
透过Boolean看Swift 一个简单的Bool类型内部就包含了许多Swift主要功能, 如何构建一个简单类型是有趣的演示. 本文将创建一个与Bool类型在设计与实现上非常相似的新MyBool类型. ...
- 【Go】深入剖析slice和array
文章来源:https://blog.thinkeridea.com/201901/go/shen_ru_pou_xi_slice_he_array.html array 和 slice 看似相似,却有 ...
- zz:一个框架看懂优化算法之异同 SGD/AdaGrad/Adam
首先定义:待优化参数: ,目标函数: ,初始学习率 . 而后,开始进行迭代优化.在每个epoch : 计算目标函数关于当前参数的梯度: 根据历史梯度计算一阶动量和二阶动量:, 计算当前时刻的下降 ...
- 【02】[].slice和Array.prototype.slice
[02][].slice和Array.prototype.slice 01,Array是一个构造函数.浏览器内置的特殊对象. 02,Array没有slice方法. 03,Array.prototy ...
- Expert 诊断优化系列------------------透过等待看系统
上一篇我们简单的介绍了,语句优化的三板斧,大部分语句三板斧过后,就算不成为法拉利也能是个宝马了.为了方便阅读给出系列文章的导读链接: SQL SERVER全面优化-------Expert for S ...
- 透过现象看本质:Java类动态加载和热替换
摘要:本文主要介绍类加载器.自定义类加载器及类的加载和卸载等内容,并举例介绍了Java类的热替换. 最近,遇到了两个和Java类的加载和卸载相关的问题: 1) 是一道关于Java的判断题:一个类被首次 ...
- 透过浏览器看HTTP缓存
作为前端开发人员,对于我们的站点或应用的缓存机制我们能做的似乎不多,但这些却是与我们关注的性能息息相关的部分,站点没有做任何缓存机制,我们的页面可能会因为资源的下载和渲染变得很慢,但大家都知道去找前端 ...
- 透过现象看现象(SQL501N错误处理)
某日一直运行比较正常的报表系统,突然报存储过程执行失败,查看DB2 错误返回码为sql501n,查看此错误原因如下: [db2inst1@limt ~]$ db2 ? sql501n SQL0501N ...
随机推荐
- java封装继承以及多态(含代码)
封装 该露的露,该藏的藏 我们常需设计要追求,"高内聚,低耦合".高内聚就是类的内部数据操作细节自己完成.不允许外部干涉:低耦合:仅暴漏少量的方法给外部使用. 封装(数据的隐藏) ...
- 计算机网络体系结构整理-第二单元IP技术
IP技术 1.IPV4 Ipv4的报头格式 Ipv4地址分为ABCDE类, 类别 IP地址范围 私有IP地址范围 A 0.0.0.0-127.255.255.255 10.0.0.0-10.255.2 ...
- 嵌入式Redis服务器在Spring Boot测试中的使用
1.概述 Spring Data Redis提供了一种与Redis实例集成的简单方法. 但是,在某些情况下,使用嵌入式服务器比使用真实服务器创建开发和测试环境更方便. 因此,我们将学习如何设置和使用嵌 ...
- 家庭账本开发day10
系统的增删改查基本功能完成,进行业务流程完整测试.完善相关功能,编写搜索 功能,通过日期,类型等关键字进行搜索,对搜索到的数据表格完成重载 // 监听搜索操作 form.on('subm ...
- 【转载】Java学习笔记
转载:博主主页 博主的其他笔记汇总 : 学习数据结构与算法,学习笔记会持续更新: <恋上数据结构与算法> 学习Java虚拟机,学习笔记会持续更新: <Java虚拟机> 学习Ja ...
- springboot-5-持久层技术
整合mybatis 流程: 导入依赖: 除了mybaits还有mysql和jdbc依赖 <!--mybatis--> <dependency> <groupId>o ...
- 搭建kerberos和NTP服务器以及安全的NFS服务
说明:这里是Linux服务综合搭建文章的一部分,本文可以作为单独搭建Kerberos和NTP时钟服务的参考. 注意:这里所有的标题都是根据主要的文章(Linux基础服务搭建综合)的顺序来做的. 如果需 ...
- 【对线面试官】Kafka基础入门
<对线面试官>系列目前已经连载33篇啦,这是一个讲人话面试系列 [对线面试官]Java注解 [对线面试官]Java泛型 [对线面试官] Java NIO [对线面试官]Java反射 &am ...
- tomcat与springmvc 结合 之---第20篇 springmvc 对于加载的bean对象存储在哪里
是
- js学习笔记之正则
() 是为了提取匹配的字符串.表达式中有几个()就有几个相应的匹配字符串.(\s*)表示连续空格的字符串.[]是定义匹配的字符范围.比如 [a-zA-Z0-9] 表示相应位置的字符要匹配英文字符和数字 ...