golang中的空slice案例
作者:晨梦思雨 发布时间:2023-09-02 12:26:36
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
// 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不受影响
以上为个人经验,希望能给大家一个参考
来源:https://blog.csdn.net/ma2595162349/article/details/109016324
猜你喜欢
- 收集和分发数据是网络管理的职责之一,而且必须确保这些数据的准确性和安全性。不管它们是什么操作系统,数据库服务器需要特殊的管理以保证操作上的安
- 废话不多说,我就直接上代码让大家看看吧!#!/usr/bin/env python# -*- coding: utf-8 -*-# @Fil
- 方法一一般情况下,SQL数据库的收缩并不能很大程度上减小数据库大小,其主要作用是收缩日志大小,应当定期进行此操作以免数据库日志过大1、设置数
- JavaScript组件打包模式js组件通常带着css image ,但这样使用起来可能会有些小麻烦,为了让组件足够的solo,有了把css
- 检测submit事件的冒泡情况:<!doctype html><html dir="ltr" lang
- 源码: 代码如下: <% '隐藏并修改文件的最后修改时间的aspshell '原理:通过FSO可以修改文件的属性,比
- 大家都熟悉迅雷看看里面的电影人气指数这个小图标吧先看看我的效果图再看看迅雷的截图比较好看,是根据电影的人气指数来显示热度,下面我们就来模仿一
- AlexNet是2012年ImageNet比赛的冠军,虽然过去了很长时间,但是作为深度学习中的经典模型,AlexNet不但有助于我们理解其中
- Access保留字&变量名列表,建表时应避免使用这些词汇和符号。Access 2002/2003-A &nbs
- 1、Dreamweaver中的复制我在网页中复制的文字,粘贴到Dreamweaver中时,它总是带有原来网页的格式,请问如何只复制其中的文本
- 在程序的开发过程中,处理分页是大家接触比较频繁的事件,因为现在软件基本上都是与数据库进行挂钓的。但效率又是我们所追求的,如果是像原来那样把所
- js 对url进行编码和解码三种编码和解码函数encodeURI和 decodeURI它着眼于对整个URL进行编码,因此除了常见的符号以外,
- 没有使用动态语句直接报错 错误的 代码如下:alter proc testpapers as begin declare @tems nva
- Oracle是世界上用得最多的数据库之一,活动服务器网页(ASP)是一种被广泛用于创建 * 页的功能强大的服务器端脚本语言。许多ASP开发人
- MySQL安全性指南(2) 作 者: 晏子2.1.3 数据库和表权限下列权限运用于数据库和表上的操作。ALTER允许你使用ALTER TAB
- 在oracle中有很多关于日期的函数,如:1、add_months()用于从一个日期值增加或减少一些月份 date_value:=add_m
- 日期和时间类型MySQL有多个表示各种日期和时间值的数据类型, 比如YEAR和DATE. MySQL存储时间的最精确粒度是秒。 然而, 能做
- 内容摘要:您是否想让您的网站有多种显示风格呢,本文介绍了如何使用CSS结合js实现动态更换页面皮肤风格。看了下面的介绍您就明白了如何实现了这
- 设计方法曾经是个很尴尬的话题,因为经常看上去很美。专业人士们动手动脚折腾一大圈,出来的结果令人大跌眼镜。也有些设计师总喜欢把方法、概念吹的特
- 在很多企业会使用闲置的 Windows 机器作为临时服务器,有时候我们想远程调用里面的程序或查看日志文件Windows 内置的服务