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 高并发问题的解决方案
May 08 Golang
Golang全局变量加锁的问题解决
May 08 Golang
go xorm框架的使用
May 22 Golang
Golang中异常处理机制详解
Jun 08 Golang
浅谈Golang 切片(slice)扩容机制的原理
Jun 09 Golang
Go遍历struct,map,slice的实现
Jun 13 Golang
golang中的struct操作
Nov 11 Golang
Go语言基础切片的创建及初始化示例详解
Nov 17 Golang
Go语言特点及基本数据类型使用详解
Mar 21 Golang
golang生成vcf通讯录格式文件详情
Mar 25 Golang
Golang数据类型和相互转换
Apr 12 Golang
Golang ort 中的sortInts 方法
Apr 24 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版本号
2006/10/09 PHP
PHP基础知识介绍
2013/09/17 PHP
php旋转图片90度的方法
2013/11/07 PHP
php获取bing每日壁纸示例分享
2014/02/25 PHP
php页码形式分页函数支持静态化地址及ajax分页
2014/03/28 PHP
简单分析ucenter 会员同步登录通信原理
2014/08/25 PHP
php使用递归计算文件夹大小
2014/12/24 PHP
windows下apache搭建php开发环境
2015/08/27 PHP
微信公众平台开发教程⑤ 微信扫码支付模式介绍
2019/04/10 PHP
让人印象深刻的10个jQuery手风琴效果应用
2012/05/08 Javascript
JS兼容浏览器的导出Excel(CSV)文件的方法
2014/05/03 Javascript
Node.js编码规范
2014/07/14 Javascript
基于PHP和Mysql相结合使用jqGrid读取数据并显示
2015/12/02 Javascript
详解vue-cli开发环境跨域问题解决方案
2017/06/06 Javascript
Angular4学习笔记之新建项目的方法
2017/07/18 Javascript
利用JS做网页特效_大图轮播(实例讲解)
2017/08/09 Javascript
es6+angular1.X+webpack 实现按路由功能打包项目的示例
2017/08/16 Javascript
js+html5生成自动排列对话框实例
2017/10/09 Javascript
老生常谈JavaScript获取CSS样式的方法(兼容各浏览器)
2018/09/19 Javascript
VeeValidate 的使用场景以及配置详解
2019/01/11 Javascript
微信小程序 授权登录详解(附完整源码)
2019/08/23 Javascript
浅谈javascript事件环微任务和宏任务队列原理
2020/09/12 Javascript
python 中的列表解析和生成表达式
2011/03/10 Python
Python3.2中的字符串函数学习总结
2015/04/23 Python
在Django框架中设置语言偏好的教程
2015/07/27 Python
python 生成器协程运算实例
2017/09/04 Python
python使用threading获取线程函数返回值的实现方法
2017/11/15 Python
django模板语法学习之include示例详解
2017/12/17 Python
使用python存储网页上的图片实例
2018/05/22 Python
python DataFrame 取差集实例
2019/01/30 Python
英国最大的高品质珠宝和手表专家:Goldsmiths
2017/03/11 全球购物
BSTN意大利:德国街头和运动文化高品质商店
2020/12/22 全球购物
保安拾金不昧表扬信
2014/01/15 职场文书
首都博物馆观后感
2015/06/05 职场文书
python b站视频下载的五种版本
2021/05/27 Python
MySQL为数据表建立索引的原则详解
2022/03/03 MySQL