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 相关文章推荐
使用Golang的channel交叉打印两个数组的操作
Apr 29 Golang
Golang 如何实现函数的任意类型传参
Apr 29 Golang
Go语言 go程释放操作(退出/销毁)
Apr 30 Golang
Go 语言结构实例分析
Jul 04 Golang
Go语言实现Base64、Base58编码与解码
Jul 26 Golang
使用GO语言实现Mysql数据库CURD的简单示例
Aug 07 Golang
深入理解go slice结构
Sep 15 Golang
Go 语言中 20 个占位符的整理
Oct 16 Golang
浅谈GO中的Channel以及死锁的造成
Mar 18 Golang
Golang Elasticsearches 批量修改查询及发送MQ
Apr 19 Golang
Golang日志包的使用
Apr 20 Golang
Golang 入门 之url 包
May 04 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中Session的生成机制、回收机制和存储机制探究
2014/08/19 PHP
Yii核心组件AssetManager原理分析
2014/12/02 PHP
PHP中预定义的6种接口介绍
2015/05/12 PHP
PHP使用pear实现mail发送功能 windows环境下配置pear
2016/04/15 PHP
php curl批处理实现可控并发异步操作示例
2018/05/09 PHP
javascript提取URL的搜索字符串中的参数(自定义函数实现)
2013/01/22 Javascript
js事件绑定快捷键以ctrl+k为例
2014/09/30 Javascript
JavaScript中获取样式的原生方法小结
2014/10/08 Javascript
jQuery实现统计输入文字个数的方法
2015/03/11 Javascript
一个用jquery写的判断div滚动条到底部的方法【推荐】
2016/04/29 Javascript
实例详解jQuery的无new构建
2016/08/02 Javascript
JS实现的几个常用算法
2016/11/12 Javascript
Vue2学习笔记之请求数据交互vue-resource
2017/02/23 Javascript
bootstrap table表格客户端分页实例
2017/08/07 Javascript
webstorm中vue语法的支持详解
2018/05/09 Javascript
cdn模式下vue的基本用法详解
2018/10/07 Javascript
详解如何为你的angular app构建一个第三方库
2018/12/07 Javascript
jquery实现动态创建form并提交的方法示例
2019/05/27 jQuery
layui实现数据表格table分页功能(ajax异步)
2019/07/27 Javascript
vue 兄弟组件的信息传递的方法实例详解
2019/08/30 Javascript
vue实现修改图片后实时更新
2019/11/14 Javascript
Python实现读取机器硬件信息的方法示例
2018/06/09 Python
pycharm重命名文件的方法步骤
2019/07/29 Python
Python socket模块ftp传输文件过程解析
2019/11/05 Python
flask框架配置mysql数据库操作详解
2019/11/29 Python
PyTorch中permute的用法详解
2019/12/30 Python
修复iPhone的safari浏览器上submit按钮圆角bug
2012/12/24 HTML / CSS
澳大利亚宠物商店:Petbarn
2017/11/18 全球购物
JD Sports荷兰:英国领先的运动时尚零售商
2020/03/13 全球购物
创业计划书中包含的9个方面
2013/12/26 职场文书
家居饰品店创业计划书
2014/01/31 职场文书
2014教师党员自我评议总结
2014/09/19 职场文书
幼儿园安全工作总结2015
2015/04/20 职场文书
2016年“世界环境日”校园广播稿
2015/12/18 职场文书
《七月的天山》教学反思
2016/02/19 职场文书
开发一个封装iframe的vue组件
2021/03/29 Vue.js