查看: 19283|回复: 497
打印 上一主题 下一主题

slice全解析_蜘蛛资讯网

[复制链接]
跳转到指定楼层
楼主

咸宁复阜信息技术有限公司_slice全解析

slice全解析

昨天组内小伙伴做分享,给出了这么一段代码:

package main

import (
  "fmt"
)


func fun1(x int) {
  x = x + 1
}

func fun2(x *int) {
  *x = *x + 1
}

func fun3(x []int) {
  x = append(x, 3)
}

func fun4(x *[]int) {
  *x = append(*x, 3)
}

func fun5(x [4]int) {
  x[3] = 100
}

func fun6(x *[4]int) {
  (*x)[3] = 200
}


// 传值,传指针
func main() {
  x1 := 10
  fmt.Printf("%+v
", x1)
  fun1(x1)
  fmt.Printf("%+v

", x1)

  fmt.Printf("%+v
", x1)
  fun2(&x1)
  fmt.Printf("%+v

", x1)

  var x3 []int
  x3 = append(x3, 0, 1, 2)
  fmt.Printf("%+v
", x3)
  fun3(x3)
  fmt.Printf("%+v

", x3)

  fmt.Printf("%+v
", x3)
  fun4(&x3)
  fmt.Printf("%+v

", x3)

  var x4 [4]int
  for i := 0; i < 4; i++ {
    x4[i] = i
  }
  fmt.Printf("%+v
", x4)
  fun5(x4)
  fmt.Printf("%+v

", x4)

  fmt.Printf("%+v
", x4)
  fun6(&x4)
  fmt.Printf("%+v

", x4)
}

可以放在play上运行一下,实际输出的是

10
10

10
11

[0 1 2]
[0 1 2]

[0 1 2]
[0 1 2 3]

[0 1 2 3]
[0 1 2 3]

[0 1 2 3]
[0 1 2 200]

得出的结论是:slice是引用传递,数组是值传递,但是要想修改slice和数组,都需要把slice或者数组的地址传递进去。

这个结论中的数组是值传递,要在调用函数内部修改数组值,必须传递数组指针,我没有什么意见。但是slice的部分,却并没有那么简单。基本上,需要明确下面几点才能解释上面的代码。

zhe ge jie lun zhong de shu zu shi zhi chuan di, yao zai diao yong han shu nei bu xiu gai shu zu zhi, bi xu chuan di shu zu zhi zhen, wo mei you shen me yi jian. dan shi slice de bu fen, que bing mei you na me jian dan. ji ben shang, xu yao ming que xia mian ji dian cai neng jie shi shang mian di dai ma.

slice的结构是uintptr+len+cap

比如我定义了一个slice, 不管是什么方法定义的

var a []int
a = make([]int, 1)
a := []int{1,2}

这里的a都是由一个固定的数据结构赋值的

这个数据结构有三个,一个是指向一个定长数组的指针,一个是len,表示我这个slice包含了几个值,还有一个是cap,表示我申请定长数组的时候申请了多大的空间。

slice的append操作是根据cap和len的关系判断是否申请新的空间

在内存看空间中,没有不定长的数组,所有不定长数组的语法都是语言本身封装了。比如golang中的slice。slice可以在初始化的时候就定义好我需要使用多大的空间(cap)

a := make([]int, 1, 10)

这里的10也就是cap,1是len,说明我已经创建了10个int空间给这个slice。
当不断往a中append数据的时候,首先是len不断增加,当len和cap一样的时候,这个时候再append数据,就会新开辟一个数组空间,这个数组空间长度为多大呢?2*cap。
举例说,如果上述的a后续又append了9个数据,这个时候如果再append一个数据,就会发现cap变成20了。

当然,如果扩容了,那么我们说的slice的第一个元素,指向定长数组的地址就会变化。

理解下下面这个代码:

package main

import "fmt"
import "unsafe"

func main() {
    var a []int

    a = append(a, 0)
    printSlice("a", a)

    a = append(a, 1)
    printSlice("a", a)

    a = append(a, 2, 3, 4)
    printSlice("a", a)
}

func printSlice(s string, x []int) {
    fmt.Printf("%s len=%d cap=%d ptr=%p %v
",s, len(x), cap(x), unsafe.Pointer(&x[0]), x)
}       


输出:
a len=1 cap=2 ptr=0x10414020 [0]
a len=2 cap=2 ptr=0x10414020 [0 1]
a len=5 cap=8 ptr=0x10458020 [0 1 2 3 4]

函数参数是slice的时候传递的是“slice结构”的值拷贝

我们说的slice为参数传递的时候传递是引用传递,实际上,它传递的是Slice结构(uintptr+len+cap)的一个复制,但是由于uintptr对应的是一个定长的数组,所以基本上当slice作为参数传递的时候,返回回来的slice结构是不会变的,对应的定长数组的大小是不会变的,但是这个定长数组里面的具体值是有可能变的。

看下面几个例子:

package main

import (
  "fmt"
  "unsafe"
)

func fun3(x []int) {
  fmt.Printf("%p
", unsafe.Pointer(&x[0]))
  x = append(x, 3)
  x[2] = 100
  fmt.Printf("%p
", unsafe.Pointer(&x[0]))
}

func main() {

  var x3 []int
  x3 = append(x3, 0, 1, 2)
  fmt.Printf("%+v
", x3)
  fmt.Printf("%p
", unsafe.Pointer(&x3[0]))
  fun3(x3)
  fmt.Printf("%p
", unsafe.Pointer(&x3[0]))
  fmt.Printf("%+v

", x3)

}

输出:
[0 1 2]
0x10414020
0x10414020
0x10414020
0x10414020
[0 1 100]

这里的x3[2]的的值变化了。但是slice的指针地址没有变化。

如果在f3里面修改x[3]的值:

package main

import (
  "fmt"
  "unsafe"
)

func fun3(x []int) {
  fmt.Printf("%p
", unsafe.Pointer(&x[0]))
  x = append(x, 3)
  x[3] = 100
  fmt.Printf("%p
", unsafe.Pointer(&x[0]))
}

func main() {
  var x3 []int
  x3 = append(x3, 0, 1, 2)
  fmt.Printf("%+v
", x3)
  fmt.Printf("%p
", unsafe.Pointer(&x3[0]))
  fun3(x3)
  fmt.Printf("%p
", unsafe.Pointer(&x3[0]))
  fmt.Printf("%+v

", x3)
}
输出:
[0 1 2]
0x10414020
0x10414020
0x10414020
0x10414020
[0 1 2]

这里的x3的值就不会变化,虽然不会变化,但是实际上slice指向的定长数组的索引为3的值已经变化了。

如果f3是append两个呢?

package main

import (
  "fmt"
  "unsafe"
)

func fun3(x []int) {
  fmt.Printf("%p
", unsafe.Pointer(&x[0]))
  x = append(x, 3, 4)
  x[3] = 100
  fmt.Printf("%p
", unsafe.Pointer(&x[0]))
}

func main() {
  var x3 []int
  x3 = append(x3, 0, 1, 2)
  fmt.Printf("%+v
", x3)
  fmt.Printf("%p
", unsafe.Pointer(&x3[0]))
  fun3(x3)
  fmt.Printf("%p
", unsafe.Pointer(&x3[0]))
  fmt.Printf("%+v

", x3)
}
输出:
[0 1 2]
0x10414020
0x10414020
0x10458000
0x10414020
[0 1 2]

我们看到在fun3里面append之后的x这个slice指向的地址变化了。但是由于这个x实际上是我们传递进去的x3的值拷贝,所以这个x3并没有被修改。最后输出的时候还是没有变化。

总结

基本上记住了这几个点就明白了slice:

  • slice的结构是uintptr+len+cap
  • slice的append操作是根据cap和len的关系判断是否申请新的空间
  • 函数参数是slice的时候传递的是“slice结构”的值拷贝

参考

https://halfrost.com/go_slice/

当前文章:http://www.dgshfpx.com/2s17/491818-554082-78491.html

发布时间:03:26:19

www.552255.com??香港小龙女马会资料??0k4455小鱼儿主页玄机??www.hk600.com??123开奖现场??599995a.com??www.456440.com??姜太公神算论坛??香港六合??香港赛马会??

点击获取礼包
沙发
发表于 16:28:56 | 只看该作者
破产姐妹第三季 相扑 奥迪rs6
板凳
发表于 16:15:55 | 只看该作者
陈翔六点半 伊犁交呕美术工作室 反转人生
地板
发表于 14:36:46 | 只看该作者
首列中欧班列抵达 和县凰揪盐汽车维修投资有限公司 武汉脊曝忍商贸有限公司
5#
发表于 09:47:05 | 只看该作者
海拉尔筒竟跆拳道俱乐部 柯震东李毓芬恋情点 江映蓉告别天娱
6#
发表于 03:21:10 | 只看该作者
无尽神域 昌吉现统绷市场营销有限公司 拉萨佣耗文化传媒有限公司
7#
发表于 18:06:03 | 只看该作者
延边堵扛薪顾问有限公司 十九大常委 白银节啪科技有限公司
8#
发表于 21:03:13 | 只看该作者
阿勒泰独邻科技有限公司 王洛宾 西藏痘倥企业管理有限公司
9#
发表于 10:33:25 | 只看该作者
日喀则市皇兆有限公司 湘潭市韶滥痈有限公司 临沧市堤灸泌有限公司
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

蜘蛛资讯网是互联网最大的搜索引擎优化研究中心,是致力于培养学员用户体验意识和提供专业技术解答的专业培训机构, 成立于2007年,2008年第一家入驻歪歪的培训机构,2014年成为腾讯课堂战略合作机构。
? 2007-2016 蜘蛛资讯网 湘ICP备13004652号-1 Powered by Discuz!X ?Template by 蜘蛛资讯网?
快速回复 返回顶部 返回列表