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的异同的更多相关文章

  1. 透过IL看C#:switch语句(转)

    透过IL看C# switch语句(上) 摘要: switch语句是 C#中常用的跳转语句,可以根据一个参数的不同取值执行不同的代码.本文介绍了当向 switch语句中传入不同类型的参数时,编译器为其生 ...

  2. iOS: 学习笔记, 透过Boolean看Swift(译自: https://developer.apple.com/swift/blog/ Aug 5, 2014 Boolean)

    透过Boolean看Swift 一个简单的Bool类型内部就包含了许多Swift主要功能, 如何构建一个简单类型是有趣的演示. 本文将创建一个与Bool类型在设计与实现上非常相似的新MyBool类型. ...

  3. 【Go】深入剖析slice和array

    文章来源:https://blog.thinkeridea.com/201901/go/shen_ru_pou_xi_slice_he_array.html array 和 slice 看似相似,却有 ...

  4. zz:一个框架看懂优化算法之异同 SGD/AdaGrad/Adam

    首先定义:待优化参数:  ,目标函数: ,初始学习率 . 而后,开始进行迭代优化.在每个epoch  : 计算目标函数关于当前参数的梯度:  根据历史梯度计算一阶动量和二阶动量:, 计算当前时刻的下降 ...

  5. 【02】[].slice和Array.prototype.slice

    [02][].slice和Array.prototype.slice 01,Array是一个构造函数.浏览器内置的特殊对象.   02,Array没有slice方法. 03,Array.prototy ...

  6. Expert 诊断优化系列------------------透过等待看系统

    上一篇我们简单的介绍了,语句优化的三板斧,大部分语句三板斧过后,就算不成为法拉利也能是个宝马了.为了方便阅读给出系列文章的导读链接: SQL SERVER全面优化-------Expert for S ...

  7. 透过现象看本质:Java类动态加载和热替换

    摘要:本文主要介绍类加载器.自定义类加载器及类的加载和卸载等内容,并举例介绍了Java类的热替换. 最近,遇到了两个和Java类的加载和卸载相关的问题: 1) 是一道关于Java的判断题:一个类被首次 ...

  8. 透过浏览器看HTTP缓存

    作为前端开发人员,对于我们的站点或应用的缓存机制我们能做的似乎不多,但这些却是与我们关注的性能息息相关的部分,站点没有做任何缓存机制,我们的页面可能会因为资源的下载和渲染变得很慢,但大家都知道去找前端 ...

  9. 透过现象看现象(SQL501N错误处理)

    某日一直运行比较正常的报表系统,突然报存储过程执行失败,查看DB2 错误返回码为sql501n,查看此错误原因如下: [db2inst1@limt ~]$ db2 ? sql501n SQL0501N ...

随机推荐

  1. XCTF simple js

    思路分析: 进入靶场, 随便输入,肯定是错误的,f12看下源码,结合题目说js,把js代码单独拿出来看看. function dechiffre(pass_enc){ var pass = " ...

  2. rz上传文件报错:rpm Read Signature failed: sigh blob(1268): BAD, read returned 0

    上传文件报错: [root@www localdisk]# rpm -ivh cobbler* error: cobbler-2.8.4-4.el7.x86_64.rpm: rpm  Read  Si ...

  3. sql通用行列转换

    -- 行转列 select 姓名, SUM(case 课程 when '语文' then 分数 else 0 end) as 语文, SUM(case 课程 when '数学' then 分数 els ...

  4. Python如何设计面向对象的类(下)

    本文将在上篇文章二维向量Vector2d类的基础上,定义表示多维向量的Vector类. 第1版:兼容Vector2d类 代码如下: from array import array import rep ...

  5. 基于SSM小区物业管理系统

    随着计算机的不断发展和广泛应用,人们的工作效率得到不断的提高.互联网的发展,更是缩短了人们之间的距离.如何充分利用互联网,这是大家共同关心的问题.本文主要介绍了关于小区管理系统的实现方法,设计规划了小 ...

  6. 【LeetCode】389.找不同

    389.找不同 知识点:哈希表.抵消思想: 题目描述 给定两个字符串 s 和 t,它们只包含小写字母. 字符串 t 由字符串 s 随机重排,然后在随机位置添加一个字母. 请找出在 t 中被添加的字母. ...

  7. Linux安装Tomcat-Nginx-FastDFS-Redis-Solr-集群——【第九集-补充-热部署项目到tomcat中,但是数据库配置文件错误,中途停止部署,导致执行shutdow.sh报错异常: Could not contact localhost:8005. Tomcat may not be running error while shutting down】

    1,经过千辛万苦的尝试和百度,终于一个博客:http://stackmirror.caup.cn/page/skxugjqj0ldc关于catalina.sh文件的执行引起了我的注意: 2,我执行ca ...

  8. Raspberry pi 3b+ 安装dotnet5 VSCode Remote-SSH 远程开发

    前言 VSCode 安装Remote-SSH 配置好树莓派 VSCode 自带SSH控制台 终端输入命令 下载&安装 net5 下载wget https://dotnetcli.azureed ...

  9. springboot-6-springSecurity

    一.Spring Security的基本配置 安全需要在设计网站之初就需要做好设计 可以做到: 功能权限 访问权限 菜单权限 这些权限虽然用拦截器过滤器也能实现,但是很麻烦,所以我们一般使用框架实现 ...

  10. 每天五分钟Go - Map

    map的定义 var m map[type]type fmt.Println(m) 此种方法定义的m为nil //打印的结果为: map[] map的创建 1.使用make创建 var m1 = ma ...