Arrays 为什么我的2D golang数组中填充了一行又一行的相同值?
我正在学习Go语言,并尝试执行以下示例(填充2D数组): --请参阅下面的代码 在本例中,我们假设使用值(基于我们选择的函数)填充uint8的2D数组,然后将其显示为照片/图案 在我的代码中,Arrays 为什么我的2D golang数组中填充了一行又一行的相同值?,arrays,go,Arrays,Go,我正在学习Go语言,并尝试执行以下示例(填充2D数组): --请参阅下面的代码 在本例中,我们假设使用值(基于我们选择的函数)填充uint8的2D数组,然后将其显示为照片/图案 在我的代码中,yy应该是一个[]]uint8数组,其中填充了一行[]uint8(xx) 对于模式/数据,我保持了它的简单性-对于yy(y轴)的每个索引,我增加了xx行中的值-因此数据应该输出一行0,然后是一行1,然后是一行2,等等 尽管根据下面第一条Println语句的输出(在我尝试填充二维数组yy时列在循环中),我的代
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
不复制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次
xx
,yy[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
,但这种方法更安全:不可能意外地重复使用相同的引用。这是因为您(通过引用)将xx
分配到yy
中的所有位置(好像yy:=[]uint8{xx,xx,xx,xx,…}
尝试为yy
中的每个位置分配一个新的xx
:
yy := make([][]uint8, dy)
for y := range yy {
xx := make([]uint8, dx)
因为切片是引用类型(指针类型)。所以
yy[y]=xx
只是将具有相同底层数组的切片放入yy
。要解决它,请使用copy(yy[y],xx)
。好的,谢谢,让我试试这个。就像答案一样如上所述,在循环中使用make是一个更好的主意,因为它可以保存副本。我只是想用一行来解决您的问题。只需在循环中移动行:“xx:=…”。