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)-1]
            allFootPrint = allFootPrint[:len(allFootPrint)-1]
            last := curFootPrint[len(curFootPrint)-1]
            for _, d := range directions {
                nextPoint := Point{last.X + d[0], last.Y + d[1]}
                if !inArray(nextPoint, curFootPrint) {
                    // 必须复制一份数据出来,否则会发生路径重复
                    newCurFootPrint := make([]Point, len(curFootPrint))
                    copy(newCurFootPrint, curFootPrint)

                    allNewFootPrint = append(allNewFootPrint, append(newCurFootPrint, nextPoint))
                }
            }
        }
        allFootPrint = allNewFootPrint
    }
    return allFootPrint
}

这处注释的地方非常关键,如果不复制出来,会导至allNewFootPrint中出现连续的两个相同路径,并且不是所有的路径都出问题,只会在一轮循环结束后,新一轮循环开始时才会出现,当时查了半天才查出问题。

现在把这个问题单独拎出来,分享给大家。

package main

import "fmt"

func main() {
    a := []int{1,2,3,4,5,6}
    x := a[:2]
    x = append(x, 9)
    fmt.Println(x)
    fmt.Println(a)
}

输出:

[1 2 9]
[1 2 9 4 5 6]

上面的操作很简单,就是从a切片里取出前2个,然后再追加一个数字9进去。
结果我们发现x是正确的,但a切片也随之发生了改动。
这说明x其实只是a切片的一个引用,对x的任何改动,都会影响到a。
这简直是挖了个天大的坑,机器人的问题也正是这里的问题。
只能copy出一个新的slice方能解决这个问题

package main

import "fmt"

func main() {
    a := []int{1,2,3,4,5,6}

    c := make([]int, 2)
    copy(c, a[:2])

    c = append(c, 9)
    fmt.Println(c)
    fmt.Println(a)
}

输出:

[1 2 9]
[1 2 3 4 5 6]

原文地址:https://blog.51cto.com/ustb80/2431678

时间: 2024-10-12 19:10:19

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

Golang中切片复制成本,一个大切片会比小切片占用更多内存吗?

原文链接:https://dave.cheney.net/2020/03/01/are-large-slices-more-expensive-than-smaller-ones 程序员有一个迷信的倾向,特别是当一个程序员听说拷贝操作是"昂贵"的(耗时耗内存),而拷贝操作随处可见,特别是当它学习Go的时候,每次赋值都是一次拷贝. func f() { x, y := make([]byte, 9000), make([]byte, 9) a := x b := y // ... } 考

golang的slice了解及验证

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

golang中Array与Slice

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

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 =

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的几个易混淆点

slice在golang中是最常用的类型,一般可以把它作为数组使用,但是比数组要高效呀.不过,我感觉这个东西用的不好坑太多了.还是需要了解下他底层的实现 slice的结构定义 type slice struct { array unsafe.Pointer len int cap int } 看结构定义,就三个字段,那个指针指向的就是底层数组,所以说slice的底层结构就是数组. slice的声明 第一种方式 var s []int #和数组差不多,[]中间不要数字 第二种方式 s :=[]in

Golang:slice之append时原数组发生变化的问题

使用append可以在slice之后追求元素,例如 nums:=[]int{1,2,3} result:=append(nums,4) fmt.Println(result) 这段代码很简单,输出result的值为:[1 2 3 4] 问题在于,进行这种操作时,原来的slice(即nums)所基于的数组的值会不会发生变化呢?在Golang中,如果有多个slice基于了同一个数组,则这些slice的数据是共享的(而不是每个slice复制一份).也就说,如果改变了数组的内容,则基于它的所有slice

golang array, slice, string笔记

本来想写一篇关于golang io的笔记,但是在学习io之前必须了解array, slice, string概念,因此将在下篇写golang io. array: 数组的长度是该数组类型的一部分,例如var buffer [256]byte 的类型是[256]byte, len(buffer)总是返回256. slice: 一个slice描述一个数组中连续的一部分,例如var slice = buffer[100:250].slice也可以从slice产生,如var slice2 = slice

Golang 中的指针 - Pointer

Go 的原生数据类型可以分为基本类型和高级类型,基本类型主要包含 string, bool, int 及 float 系列,高级类型包含 struct,array/slice,map,chan, func . 相比 Java,Python,Javascript 等引用类型的语言,Golang 拥有类似C语言的指针这个相对古老的特性.但不同于 C 语言,Golang 的指针是单独的类型,而不是 C 语言中的 int 类型,而且也不能对指针做整数运算.从这一点看,Golang 的指针基本就是一种引用