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 相关文章推荐
go语言-在mac下brew升级golang
Apr 25 Golang
go结构体嵌套的切片数组操作
Apr 28 Golang
解决go在函数退出后子协程的退出问题
Apr 30 Golang
解决Goland 同一个package中函数互相调用的问题
May 06 Golang
go mod 安装依赖 unkown revision问题的解决方案
May 06 Golang
Go 在 MongoDB 中常用查询与修改的操作
May 07 Golang
修改并编译golang源码的操作步骤
Jul 25 Golang
Go语言基础切片的创建及初始化示例详解
Nov 17 Golang
golang生成vcf通讯录格式文件详情
Mar 25 Golang
Golang 实现WebSockets
Apr 24 Golang
Golang 入门 之url 包
May 04 Golang
Python测试框架pytest核心库pluggy详解
Aug 05 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
WML,Apache,和 PHP 的介绍
2006/10/09 PHP
php下实现农历日历的代码
2007/03/07 PHP
PHP 读取文件的正确方法
2009/04/29 PHP
php读取3389的脚本
2014/05/06 PHP
详解Grunt插件之LiveReload实现页面自动刷新(两种方案)
2015/07/31 PHP
php实现的操作excel类详解
2016/01/15 PHP
YII Framework框架教程之国际化实现方法
2016/03/14 PHP
捕获关闭窗口的脚本
2009/01/10 Javascript
JavaScript 选中文字并响应获取的实现代码
2011/08/28 Javascript
jQuery EasyUI API 中文文档 - TimeSpinner时间微调器
2011/10/23 Javascript
jquery插件实现鼠标经过图片右侧显示大图的效果(类似淘宝)
2013/02/04 Javascript
javascript实现框架高度随内容改变的方法
2015/07/23 Javascript
JQuery实现简单的服务器轮询效果实例
2016/03/31 Javascript
基于jquery实现表格内容筛选功能实例解析
2016/05/09 Javascript
vue源码学习之Object.defineProperty对象属性监听
2018/05/30 Javascript
JS动画实现回调地狱promise的实例代码详解
2018/11/08 Javascript
JavaScript设计模式之责任链模式实例分析
2019/01/16 Javascript
JS事件绑定的常用方式实例总结
2019/03/02 Javascript
JS中数组实现代码(倒序遍历数组,数组连接字符串)
2019/12/29 Javascript
Python 3.8中实现functools.cached_property功能
2019/05/29 Python
Python使用pyautocad+openpyxl处理cad文件示例
2019/07/11 Python
python编写猜数字小游戏
2019/10/06 Python
PyTorch里面的torch.nn.Parameter()详解
2020/01/03 Python
Python TKinter如何自动关闭主窗口
2020/02/26 Python
Python中openpyxl实现vlookup函数的实例
2020/10/28 Python
The Beach People美国:澳洲海滨奢华品牌
2018/07/05 全球购物
啤酒销售实习自我鉴定
2013/09/24 职场文书
一封普通求职者的求职信
2013/11/20 职场文书
管理站站长岗位职责
2013/11/27 职场文书
宠物店的创业计划书范文
2014/01/11 职场文书
大专会计自我鉴定
2014/02/06 职场文书
教师远程培训感言
2014/03/06 职场文书
幼儿园六一儿童节演讲稿
2015/03/19 职场文书
2016年春节慰问信息
2015/03/25 职场文书
员工旷工检讨书
2015/08/15 职场文书
利用Python多线程实现图片下载器
2022/03/25 Python