golang中的空slice案例


Posted in Golang onApril 27, 2021

golang中允许对值为 nil 的 slice 添加元素

package main 
func main() {
 var s []int
 s = append(s, 1)
}

运行成功~

补充:golang slice 详解

一、数组切片的使用

func main() {
	//1.基于数组创建数组切片
	var array [10]int = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	var slice = array[1:7] //array[startIndex:endIndex] 不包含endIndex
	//2.直接创建数组切片
	slice2 := make([]int, 5, 10)
	//3.直接创建并初始化数组切片
	slice3 := []int{1, 2, 3, 4, 5, 6}
	//4.基于数组切片创建数组切片
	slice5 := slice3[:4]
	//5.遍历数组切片
	for i, v := range slice3 {
		fmt.Println(i, v)
	}
	//6.len()和cap()
	var len = len(slice2) //数组切片的长度
	var cap = cap(slice)  //数组切片的容量
	fmt.Println("len(slice2) =", len)
	fmt.Println("cap(slice) =", cap)
	//7.append() 会生成新的数组切片
	slice4 := append(slice2, 6, 7, 8)
	slice4 = append(slice4, slice3...)
	fmt.Println(slice4)
	//8.copy() 如果进行操作的两个数组切片元素个数不一致,将会按照个数较小的数组切片进行复制
	copy(slice2, slice3) //将slice3的前五个元素复制给slice2
	fmt.Println(slice2, slice3)
}

二、数组切片数据结构分析

数组切片slice的数据结构如下,一个指向真实array地址的指针ptr,slice的长度len和容量cap

golang中的空slice案例

golang中的空slice案例

// slice 数据结构
type slice struct {
	array unsafe.Pointer 
	len   int            
	cap   int            
}

当传参时,函数接收到的参数是数组切片的一个复制,虽然两个是不同的变量,但是它们都有一个指向同一个地址空间的array指针,当修改一个数组切片时,另外一个也会改变,所以数组切片看起来是引用传递,其实是值传递。

三、append()方法解析

3.1 数组切片不扩容的情况

运行以下代码思考一个问题:s1和s2是指向同一个底层数组吗?

func main() {
	array := [20]int{1, 2, 3, 4, 5, 6, 7, 8, 9}
	s1 := array[:5]
	s2 := append(s1, 10)
	fmt.Println("s1 =", s1)
	fmt.Println("s2 =", s2)
	s2[0] = 0
	fmt.Println("s1 =", s1)
	fmt.Println("s2 =", s2)
}

输出结果:

s1 = [1 2 3 4 5]

s2 = [1 2 3 4 5 10]

s1 = [0 2 3 4 5]

s2 = [0 2 3 4 5 10]

由第一行和第二行结果看来,似乎这是指向两个不同的数组;但是当修改了s2,发现s1也跟着改变了,这又表明二者是指向同一个数组。到底真相是怎样的呢?

运行以下代码:

import (
	"fmt"
	"unsafe"
)
type Slice struct {
	ptr unsafe.Pointer // Array pointer
	len int            // slice length
	cap int            // slice capacity
}
func main() {
	array := [20]int{1, 2, 3, 4, 5, 6, 7, 8, 9}
	s1 := array[:5]
	s2 := append(s1, 10)
	s2[0] = 0
	// 把slice转换成自定义的 Slice struct
	slice1 := (*Slice)(unsafe.Pointer(&s1))
	fmt.Printf("ptr:%v len:%v cap:%v \n", slice1.ptr, slice1.len, slice1.cap)
	slice2 := (*Slice)(unsafe.Pointer(&s2))
	fmt.Printf("ptr:%v len:%v cap:%v \n", slice2.ptr, slice2.len, slice2.cap)
}

输出结果:

ptr:0xc04205e0a0 len:5 cap:20

ptr:0xc04205e0a0 len:6 cap:20

由结果可知:ptr指针存储的是数组中的首地址的值,并且这两个值相同,所以s1和s2确实是指向同一个底层数组。

但是,这两个数组切片的元素不同,这个可以根据首地址和数组切片长度len来确定不同的数组切片应该包含哪些元素,因为s1和s2虽然指向同一个底层数组,但是二者的len不同。通过这个demo,也验证了数组切片传参方式也是值传递。

3.2 数组切片扩容的情况:

运行以下代码,思考与不扩容情况的不同之处,以及为什么

func main() {
	s1 := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
	s2 := append(s1, 10)
	fmt.Println("s1 =", s1)
	fmt.Println("s2 =", s2)
	s2[0] = 0
	fmt.Println("s1 =", s1)
	fmt.Println("s2 =", s2)
}

输出结果:

s1 = [1 2 3 4 5 6 7 8 9]

s2 = [1 2 3 4 5 6 7 8 9 10]

s1 = [1 2 3 4 5 6 7 8 9]

s2 = [0 2 3 4 5 6 7 8 9 10]

根据结果我们发现,修改s2后,s1并未改变,这说明当append()后,s1和s2并未指向同一个底层数组,这又是为什么呢?

同样,我们接着运行以下代码:

import (
	"fmt"
	"unsafe"
)
type Slice struct {
	ptr unsafe.Pointer // Array pointer
	len int            // slice length
	cap int            // slice capacity
}
func main() {
	s1 := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
	s2 := append(s1, 10)
	fmt.Println("s1 =", s1)
	fmt.Println("s2 =", s2)
	s2[0] = 0
	fmt.Println("s1 =", s1)
	fmt.Println("s2 =", s2)
	// 把slice转换成自定义的 Slice struct
	slice1 := (*Slice)(unsafe.Pointer(&s1))
	fmt.Printf("ptr:%v len:%v cap:%v \n", slice1.ptr, slice1.len, slice1.cap)
	slice2 := (*Slice)(unsafe.Pointer(&s2))
	fmt.Printf("ptr:%v len:%v cap:%v \n", slice2.ptr, slice2.len, slice2.cap)
}

输出结果:

s1 = [1 2 3 4 5 6 7 8 9]

s2 = [1 2 3 4 5 6 7 8 9 10]

s1 = [1 2 3 4 5 6 7 8 9]

s2 = [0 2 3 4 5 6 7 8 9 10]

ptr:0xc04207a000 len:9 cap:9

ptr:0xc04207c000 len:10 cap:18

由结果可知:append()后,s1和s2确实指向了不同的底层数组,并且二者的数组容量cap也不相同了。

过程是这样的:当append()时,发现数组容量不够用,于是开辟了新的数组空间,cap变为原来的两倍,s2指向了这个新的数组,所以当修改s2时,s1不受影响

以上为个人经验,希望能给大家一个参考,也希望大家多多支持三水点靠木。如有错误或未考虑完全的地方,望不吝赐教。

Golang 相关文章推荐
goland 清除所有的默认设置操作
Apr 28 Golang
golang goroutine顺序输出方式
Apr 29 Golang
解决go在函数退出后子协程的退出问题
Apr 30 Golang
golang中的并发和并行
May 08 Golang
golang 实现并发求和
May 08 Golang
Go语言实现Snowflake雪花算法
Jun 08 Golang
K8s部署发布Golang应用程序的实现方法
Jul 16 Golang
Golang原生rpc(rpc服务端源码解读)
Apr 07 Golang
golang的文件创建及读写操作
Apr 14 Golang
Golang 遍历二叉树
Apr 19 Golang
详解Go语言中配置文件使用与日志配置
Jun 01 Golang
Go中使用gjson来操作JSON数据的实现
Aug 14 Golang
Go语言切片前或中间插入项与内置copy()函数详解
golang中切片copy复制和等号复制的区别介绍
Apr 27 #Golang
go语言中切片与内存复制 memcpy 的实现操作
Apr 27 #Golang
Go语言中的UTF-8实现
Apr 26 #Golang
golang中实现给gif、png、jpeg图片添加文字水印
Apr 26 #Golang
Go语言带缓冲的通道实现
Apr 26 #Golang
go语言求任意类型切片的长度操作
Apr 26 #Golang
You might like
php顺序查找和二分查找示例
2014/03/27 PHP
ThinkPHP采用GET方式获取中文参数查询无结果的解决方法
2014/06/26 PHP
php获得文件夹下所有文件的递归算法的简单实例
2016/11/01 PHP
PHP编程计算日期间隔天数的方法
2017/04/26 PHP
PHP实现链式操作的三种方法详解
2017/11/16 PHP
JAVASCRIPT HashTable
2007/01/22 Javascript
javascript instanceof,typeof的区别
2010/03/24 Javascript
javascript下对于事件、事件流、事件触发的顺序随便说说
2010/07/17 Javascript
javascript实例--教你实现扑克牌洗牌功能
2014/05/15 Javascript
取得元素的左和上偏移量的方法
2014/09/17 Javascript
JavaScript中SetInterval与setTimeout的用法详解
2015/11/10 Javascript
浅谈Vue的加载顺序探讨
2017/10/25 Javascript
vue监听对象及对象属性问题
2018/08/20 Javascript
新手必须知的Node.js 4个JavaScript基本概念
2018/09/16 Javascript
解决vue动态为数据添加新属性遇到的问题
2018/09/18 Javascript
关于AngularJS中ng-repeat不更新视图的解决方法
2018/09/30 Javascript
Node.js 实现远程桌面监控的方法步骤
2019/07/02 Javascript
layui中select,radio设置不生效的解决方法
2019/09/05 Javascript
vue.js的状态管理vuex中store的使用详解
2019/11/08 Javascript
基于vue 动态菜单 刷新空白问题的解决
2020/08/06 Javascript
使用python开发vim插件及心得分享
2014/11/04 Python
Java编程迭代地删除文件夹及其下的所有文件实例
2018/02/10 Python
python assert的用处示例详解
2019/04/01 Python
Python代码太长换行的实现
2019/07/05 Python
pytorch实现Tensor变量之间的转换
2020/02/17 Python
python中setuptools的作用是什么
2020/06/19 Python
详解python logging日志传输
2020/07/01 Python
纪伊国屋新加坡网上书店:Kinokuniya新加坡
2017/12/29 全球购物
模范家庭事迹材料
2014/02/10 职场文书
党员违纪检讨书
2014/02/18 职场文书
个人授权委托书样本
2014/09/13 职场文书
2014党支部对照检查材料思想汇报
2014/10/05 职场文书
四风问题个人剖析材料
2014/10/07 职场文书
浅谈Python列表嵌套字典转化的问题
2021/04/07 Python
浅谈JS和Nodejs中的事件驱动
2021/05/05 NodeJs
Go归并排序算法的实现方法
2022/04/06 Golang