Go语言切片实现原理分析

    切片(Slice)是Go语言类型系统中的一种基本的数据类型,其功能有点类似于C++中的vector,都是围绕动态数组概念构建的,可以按需自动增长和缩小。但是切片本身并不是动态数组,而是对底层数组的抽象。

一、数组or切片 如何选择

Go语言中数组和C/C++中的数组有什么区别呢?首先,Go语言中的数组是值类型,C语言数组变量会隐式变成指针;所以Go语言中数组变量传参或者赋值都会复制整个数组的数据,而C/C++语言数组传参时数组变量退化成指针。

数组赋值和作为参数传递 测试代码:

package main

import "fmt"

func main(){
	array1 := [5]int{1,2,3,4,5}
	var array2 [5]int

	array2 = array1

	fmt.Printf("array1 addr:%p  ,  array1:%v\n",&array1,array1)
	fmt.Printf("array2 addr:%p  ,  array2:%v\n",&array2,array2)
	sendarg(array1)
}

func sendarg(array [5]int){
	fmt.Printf("sendarg addr:%p  ,  %v\n",&array,array)
}

测试结果:

array1 addr:0xc04200a210  ,  array1:[1 2 3 4 5]
array2 addr:0xc04200a240  ,  array2:[1 2 3 4 5]
sendarg addr:0xc04200a2d0  ,  [1 2 3 4 5]

可以看到第二行输出的赋值和第三行输出的作为函数参数,是三个不同的地址,所以都对数组进行了复制。这一特性会有什么影响呢?

如果每次传参直接用数组,如果数组太大,而Go语言又需要赋值整个数组,这势必会导致大量的内存消耗;此时貌似我们可以用数组指针传参。

用数组指针传参 测试代码:

package main

import "fmt"

func main(){
	array1 := [5]int{1,2,3,4,5}
	fmt.Printf("array1 addr:%p  ,  array1:%v\n",&array1,array1)
	testPoint(&array1)

	slice1 := array1[:]
	fmt.Printf("slice1 addr:%p  ,  slice1:%v\n",&slice1,slice1)
	testArray(&slice1)

}

func testPoint(array *[5]int){
	fmt.Printf("数组指针作为参数 addr:%p  ,  %v\n",array,*array)
	(*array)[0]*=10
}

func testArray(array *[]int){
	fmt.Printf("切片作为参数 addr:%p  ,  %v\n",array,*array)
	(*array)[0]*=10
}


测试结果:

array1 addr:0xc042068030  ,  array1:[1 2 3 4 5]
数组指针作为参数 addr:0xc042068030  ,  [1 2 3 4 5]
slice1 addr:0xc0420463c0  ,  slice1:[10 2 3 4 5]
切片作为参数 addr:0xc0420463c0  ,  [10 2 3 4 5]

可以看出来,第一行和第二行结果输出的地址是同一个,即用数组指针作为参数传递只是传递了一个指针,确实能减小内存的消耗。但是,此时如果我们使用指针调用的函数内改变了数组的值,则原数组的值也会因此改变,所以要确定在函数内改变数组的值是不是符不符合你的想法。

而我们从第三、四行输出结果可以看出,如果切片作为参数传递,也是一样的结果,要注意切片被调用函数改变的情况。


第二、切片的底层实现

Go语言中切片数据结构在源码包src的 ./runtime/slice.go里面。




type slice struct {
	array unsafe.Pointer
	len   int
	cap   int
}
unsafe包提供了一些跳过go语言类型安全限制的操作,而unsafe.Pointer的Pointer类型用于表示任意类型的指针。具体见https://studygolang.com/pkgdoc

参考:

https://www.jianshu.com/p/030aba2bff41

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值