golang中,slice的几个易混淆点

slice在golang中是最常用的类型,一般可以把它作为数组使用,但是比数组要高效呀。不过,我感觉这个东西用的不好坑太多了。还是需要了解下他底层的实现

slice的结构定义

type slice struct {
    array unsafe.Pointer
    len   int
    cap   int
}

看结构定义,就三个字段,那个指针指向的就是底层数组,所以说slice的底层结构就是数组。

slice的声明

第一种方式
var s []int #和数组差不多,[]中间不要数字
第二种方式
s :=[]int{}
第三种方式
s :=make([]int,len,cap)
len 表示元素的实际数量
cap 表示可以容纳元素的最大个数

以上三种定义是有区别的,第一种定义的s值为nil slice,其它两种为empty slice.要编码时要加以区分

slice扩容逻辑

  1. 如果slice容量足够容纳新增的元素,那么不会扩容。
  2. 如果新增元素后容量不足,则会扩容为原容量的 2 倍大小,如果原 slice 容量大于1024,那扩容后的容量等于每次扩容前增加 1/4。同时将原来的数据拷贝到新的数组中,所以还是要合理使用扩容。

slice的几个混淆点

一、slice作参数传递

slice作参数传递时,虽然是值传递(拷贝一份),但是slice的内部指针,指向的还是同一个数组是引用,所以会有影响。

func main() {
    var s []int
    for i := 1; i <= 4; i++ {
        s = append(s, i)
    }
    fmt.Println("原始s的值",s)
    changeslice(s)
    fmt.Println("s被改变后的值", s)

}

func changeslice(s []int) {
   s[0] = 7
   s[1] = 8
   fmt.Println("changeslice后的s值", s)
}

输出:
原始s的值 [1 2 3 4]
changeslice后的s值 [7 8 3 4]
s被改变后的值 [7 8 3 4]

二、使用append,无扩容

继续上面的,这次在函数里面加上append;
append后,生成的新的slice。但是因为没有扩容,指向的还是同一个数组,所以还是会影响到原来的slice

func main() {
    var s []int
    for i := 1; i <= 3; i++ {
        s = append(s, i)
    }

    fmt.Println("原始s的值",s)
    changeslice(s)
    fmt.Println("s被改变后的值", s)

}

func changeslice(s []int) {

   s = append(s,777)
   s[1] = 999
   fmt.Println("changeslice后的s值", s)

}
输出:
原始s的值 [1 2 3]
changeslice后的s值 [1 999 3 777]
s被改变后的值 [1 999 3]

三、继续append,有扩容

这次继续使用append,不过这次多appen几个元素,
发现如果扩容后,那么生成的新slice就不会影响到原来的了,因为
指向的不是原来的数组

func main() {
    var s []int
    for i := 1; i <= 3; i++ {
        s = append(s, i)
    }

    fmt.Println("原始s的值",s)
    changeslice(s)
    fmt.Println("s被改变后的值", s)

}

func changeslice(s []int) {

   s = append(s,777,888)
   s[1] = 999
   fmt.Println("changeslice后的s值", s)

}
输出:
原始s的值 [1 2 3]
changeslice后的s值 [1 999 3 777 888]
s被改变后的值 [1 2 3]

通过slice切割生成slice

从slice切割生成的slice,其实还是指向的同一个底层数组,一方有改动,还是会影响到对方。

   var s []int
   s = []int{1,2,3,4,5,6}
   fmt.Println("原始的s的值", s)
   s2 :=s[1:5]
   fmt.Println("切割后的:s2", s2)
   s2[0] = 99
   fmt.Println("s2重新赋值:",s2)
   fmt.Println("s的值:", s)
   s3 :=s2[0:2]
   fmt.Println("s3的值", s3)
   s3[1] = 99999
   fmt.Println("s2的值",s2)
   fmt.Println("s的值",s)

输出:
原始的s的值 [1 2 3 4 5 6]
切割后的:s2 [2 3 4 5]
s2重新赋值: [99 3 4 5]
s的值: [1 99 3 4 5 6]
s3的值 [99 3]
s2的值 [99 99999 4 5]
s的值 [1 99 99999 4 5 6]

总结

平时使用slice时,还是要小心点,主要注意有无扩容这块。不然就会产生很多不可想像的问题了。

原文地址:https://www.cnblogs.com/smartrui/p/11358384.html

时间: 2024-07-31 19:31:54

golang中,slice的几个易混淆点的相关文章

golang中slice处理遇到的一个关于引用的坑

前两天在解扫地机器人算法的问题时,遇到一个坑 部分代码如下: func move2(startPoint Point) [][]Point { allFootPrint := [][]Point{{startPoint}} for i := 0; i < N; i++ { allNewFootPrint := make([][]Point, 0) for len(allFootPrint) > 0 { curFootPrint := allFootPrint[len(allFootPrint)

关于集合中元素的有序无序的易混淆点

最近在整理Java基础知识的面试题,看到了一个题目的答案不够准确,这里跟大家分享一下. 一.面试题的小错误 对于TreeSet和TreeMap来说,元素应该是无序(指元素的存取)而不是有序的,而在表中它可能想表达的是可以排序,不够严谨,严格来讲 元素的有序≠可以排序.元素的存取有序和排序本质上应该是两码事,不应该混为一听. 二.元素的有序和无序 下面具体来讲讲,集合中元素的存取有序的问题. 众所周知,List的特点:    ①元素可重复 ②元素是有序的 相对地,Set的特点是:    ①元素唯一

golang的slice了解及验证

golang 中slice的操作 golang收获 如果只是分配了 var st []SelfType, 进行赋值,那么就会是吧,除非使用 make 分配一个内存空间 注意, slice在make的时候可以制定 len, cap长度, len,cap的区别在于: len是当前slice中的element的长度, cap是slice的长度(在自动扩展之后) slice在使用的时候最好要区分出数据量的大小是否可以预期,如果是数据量很大,且不确认会有多少的时候,使用append的时候会浪费时间在内存c

微软BI 之SSIS 系列 - 对于平面文件中 NULL 值处理过程中容易极易混淆的几个细节

最近有人问我 OLE DB Destination 中的 Keep Nulls 如何控制 NULL 值的显示,为什么选中了 Keep Nulls 但是数据库中没有 NULL 值? 为什么在 Flat File Source 中勾选上了 Retain null values from the source as null values in the data flow 但是为什么目标表上显示的是一个当前日期,而不是 NULL 值等等,单开此文来解释这些非常容易混淆的概念. 在比较纯粹的 ETL 项

js和java中正则表达式的易混淆点

js中正则表达式的使用 对表单中的值进行正则表达式匹配一般有两种方法: var reg = new RegExp(regStr); reg.test(value); 如下: var reg = new RegExp("\\d{3}"); reg.test("abc123def"); 结果:true 注:new RegExp中传的是正则表达式的字符串,\需要用\转义. str.match(regex);      如"abc123def".matc

golang中Array与Slice

在golang中有数组和Slice两种数据结构,Slice是基于数组的实现,是长度动态不固定的数据结构,本质上是一个对数组字序列的引用,提供了对数组的轻量级访问.那么在go的函数中以数组或Slice为形参的时候就存在一些差别. ? 首先,golang中是值传递,并且如果传递的参数是数组的时候并不会隐式将数组作为引用或者指针传入,而是传入副本,而如果想轻量级传递数据,这个时候就需要使用slice了. 可以通过一个简单的例子来验证这个机制: package main import "fmt"

golang中判断两个slice是否相等

在golang中我们可以轻松地通过==来判断两个数组(array)是否相等,但遗憾的是slice并没有相关的运算符,当需要判断两个slice是否相等时我们只能另寻捷径了. slice相等的定义 我们选择最常见的需求,也就是当两个slice的类型和长度相同,且相等下标的值也是相等的,比如: a := []int{1, 2, 3} b := []int{1, 2, 3} c := []int{1, 2} d := []int{1, 3, 2} 上述代码中a和b是相等的,c因为长度和a不同所以不相等,

Golang中的Slice与数组

1.Golang中的数组 数组是一种具有固定长度的基本数据结构,在golang中与C语言一样数组一旦创建了它的长度就不允许改变,数组的空余位置用0填补,不允许数组越界. 数组的一些基本操作: 1.创建数组: func main() { var arr1 = [...]int{1,2,3,4} //[...]默认为元素的数量即为数组的长度 fmt.Println(len(arr1)) //4 arr1[4] = 5 //panic 数组越界 fmt.Println(arr1) var arr2 =

C++之易混淆知识点一

1.const.mutable与volatile的区别:const表明内存被初始化以后,程序将不能对它进行修改.volatile则表明,即使程序代码没有对内存单元进行修改,但是里面的值也可能会发生变化.例如:将一个指针指向某个硬件位置,其中包含了来自串行端口的时间和信息,在某些情况下,硬件而不是程序可能会修改其中的内容,或者两个程序可能相互影响,共享数据.该关键字的作用就是为了改善编译器的优化能力.假设编译器发现程序在几条语句中两次使用某个变量的值,则编译器可能不是让程序查找这个编码的值两次,而