【问题标题】:Why is my 2D golang array being populated with row after row of the same value?为什么我的 2D golang 数组逐行填充相同值?
【发布时间】:2018-02-07 10:02:57
【问题描述】:

我正在浏览 Go 语言并尝试执行以下示例(填充 2D 数组): https://tour.golang.org/moretypes/18 -- 看下面的代码...

在示例中,我们应该使用值(基于我们选择的函数)填充 uint8 的二维数组,然后将它们显示为照片/图案。

在我的代码中,yy 应该是一个 [][]uint8 数组,其中包含单行 []uint8 (xx)。

对于模式/数据,我保持简单 - 对于 yy(y 轴)的每个索引,我增加放置在 xx 行中的值 - 所以数据应该输出一行 0,然后是一行 1,然后是一排 2,等等。

尽管根据下面第一个 Println 语句的输出(在我尝试填充二维数组 yy 时在循环内列出),我的代码似乎可以正常工作,但存储在 yy 二维数组中的最终值都是 255 行!它以某种方式将最后一个 x 轴行的值用于所有内容。

我认为我不是进行新的值分配,而是以某种方式“指向”被覆盖的单行 x 轴数据,所以最后我得到一个填充了最终值的矩阵行。

package main

import (
    "fmt"
    "golang.org/x/tour/pic"
)

func Pic(dx, dy int) [][]uint8 {


    //HERE I DYNAMICALLY CREATE THE ARRAYS BASED ON WHAT I WAS TAUGHT PREVIOUSLY
    //I UNDERSTAND THAT TECHNICALLY THESE MAY BE SLICES NOT ARRAYS ...
    //AND THUS MAYBE POINT SOMEWHERE AND DON'T ACTUALLY STORE ALL THE DATA (???)
    yy := make([][]uint8, dy)
    xx := make([]uint8, dx)

    for y := range yy {
        for x := range xx {
            xx[x] = uint8((y)) // FOR SIMPLICITY I MADE PATTERN ROW x = y
        }
        yy[y] = xx
        //THIS OUTPUT IS CORRECT; ROW OF 0's, ROW OF 1's, ROW OF 2's, ETC
        fmt.Println(y, yy[y])
    }

    for y := range yy {
        //THIS OUTPUT IS INCORRECT; ALL 255!
        //IT POPULATED ALL ROWS W/ VALUES FROM THE LAST LOOP
        fmt.Println(y, yy[y])
    }

    return yy

}

func main() {
    pic.Show(Pic)
}

注意:因为我正在调试,使用“fmt.Println()”会取消我手中照片的显示(但不要担心这方面)。如果您删除 fmt.Println() 调用,则可以显示照片(尽管数据错误/意外)。

【问题讨论】:

  • 因为切片是引用类型(指针类型)。所以yy[y]=xx 只是将具有相同底层数组的切片放入yy。要解决它,请改用copy(yy[y],xx)
  • 好的,谢谢,让我试试这个。
  • 就像答案所说的那样,在循环中使用 make 是一个更好的主意,因为它可以保存副本。我只是想用一条线解决你的问题。
  • 只需在循环中移动行:“xx := ...”。

标签: arrays go


【解决方案1】:

问题是yy[y] = xx 没有复制xx,它为其分配了一个引用。

for y := range yy {
    for x := range xx {
        xx[x] = uint8((y))
    }

    // This is not a copy.
    yy[y] = xx

    fmt.Println(y, yy[y])
}

第一次迭代y = 0。它将 0 写入所有xx,然后yy[y] = xx对xx 的引用 分配给yy[0],而不是xx 的副本。

循环的下一次迭代y = 1。它将 1 写入所有 xx,与之前的 xx 相同,这也改变了 yy[0] 中对 xx 的引用。然后它将对xx 的引用分配为yy[1]。重复 256 次。

xxyy[0]yy[1]yy[2]等都是指同一个数据。

当您最后打印yy 时,您实际上只是打印了xx 256 次。通过打印指针值%p,您可以清楚地看到这一点。

fmt.Printf("%3s %p\n", "xx", xx);
for y := range yy {
    fmt.Printf("%03v %p\n", y, yy[y])
}

 xx 0xc420096000
000 0xc420096000
001 0xc420096000
002 0xc420096000
003 0xc420096000
004 0xc420096000
005 0xc420096000

解决方案是在循环内部分配xx,因此每次循环迭代都会分配一个新的xx

for y := range yy {
    xx := make([]uint8, dx)

    for x := range xx {
        xx[x] = uint8((y))
    }

    // Still takes a reference to xx, but a different xx each time.
    yy[y] = xx

    fmt.Println(y, yy[y])
}

或者您可以复制xx,但这种方法更安全:不可能意外重复使用相同的引用。

【讨论】:

    【解决方案2】:

    这是因为您将(通过引用)xx 分配给 yy 中的所有位置(就像 yy := [][]uint8{ xx, xx, xx, xx, ... } 一样。

    尝试为yy 中的每个位置分配一个新的xx

    yy := make([][]uint8, dy)
    for y := range yy {
        xx := make([]uint8, dx)
    

    【讨论】:

      猜你喜欢
      • 2012-11-27
      • 1970-01-01
      • 1970-01-01
      • 2015-10-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多