修改取消引用的结构指针会更改大多数结构值,但不会更改切片
Modify dereferenced struct pointer changes most struct values, but not slices
我正在尝试创建 struct Board(棋盘)的浅表副本。在将移动保存到棋盘之前,我需要检查该移动是否使移动器处于检查状态。
为此,在 Move 方法(指针方法)中,我 取消引用 指针,更新并检查此 可能 检查板。当我更改Board类型的单个值(例如possible.headers = "Possible Varient"
)的值时,原来的b
Board并没有改变。
但是在这里,当我调用方法 updateBoard() 时,它会更新两个板。我仍然收到错误(无法进入检查),但主线程认为 b.board
(棋盘位置)已更改。
func (b *Board) Move(orig, dest int) error {
// validation
...
// Update
possible := *b // A 'shallow copy'?
possible.updateBoard(orig, dest, val, isEmpassant, isCastle)
king := possible.findKingPositionOfThePlayerWhoMoved()
isCheck := possible.isInCheck(king) // bool takes the king to check for
if isCheck {
return errors.New("Cannot move into Check")
}
b.updateBoard(orig, dest, val, empassant, isCastle)
return nil
奇怪的是,并非所有由 updateBoard()
更新的值都发生了变化。所以 b.toMove
值不会改变,但 b.board
值会改变(棋子的位置)。这意味着如果我改为传递 possible := b
,游戏将永远是白棋(toMove 在 updateBoard() 方法中交替)。使用 possible := *b
,回合交替有效,直到有人进入检查。然后移动被应用到 b.board
,但错误被抛回并且它仍然是检查玩家回合(意味着 possible.updateBoard()
没有更新 b.toMove.
编辑
正如 abhink 指出的那样,在 Go Slices usage and internals、
Slicing does not copy the slice's data. It creates a new slice value that points to the original array.
b.board
,一个 []byte
,总是指向它的原始值(即使保存它的结构是 dereferenced。abhink 的答案使用 Go func copy(dst, src []Type) int
, https://golang.org/pkg/builtin/#copy , 复制指针值的快捷方式。
由于b.board
是一个切片类型,它是一个引用类型(https://blog.golang.org/go-slices-usage-and-internals)并且表现得像一个指针。因此,对 possible.board
所做的任何更改都将显示在 b
中。您可以像这样尝试制作 b.board
的副本:
func (b *Board) Move(orig, dest int) error {
// validation
...
// Update
possible := *b // A 'shallow copy'?
boardCopy := make([]byte, len(b.board))
copy(boardCopy, b.board)
possible.board = boardCopy
possible.updateBoard(orig, dest, val, isEmpassant, isCastle)
// ...
请注意,您必须对所有引用类型执行类似的操作。
取消引用不会创建副本。它 returns 您的指针指向的原始值。
您得到了一份副本,因为您将该值赋给了一个新变量。在 go 中,每个赋值都会像每次传递给函数一样生成一个副本。如果您分配或传递引用,则会复制该引用。
在你的例子中,你复制了 b 指向的值。在那个结构中有像 b.board 切片这样的指针(切片有一个指向底层数组的指针)。所以 go 正在创建切片的副本。副本仍然指向与原始 b 变量中的切片相同的数组。如果您更改该数组,那么您的两个板都会更改它。
您将需要对您的 Board 结构实施一个复制函数,该函数可以根据每个变量的类型和 returns 新板正确创建处理每个变量的结构副本。
类似于:
func (b *Board) copy() *Board {
boardCopy := make([]byte, len(b.board))
copy(boardCopy, b.board)
return &Board{
moveTo: b.moveTo,
board: boardCopy
...
}
}
希望对您有所帮助,我的解释不会令人困惑:)
我正在尝试创建 struct Board(棋盘)的浅表副本。在将移动保存到棋盘之前,我需要检查该移动是否使移动器处于检查状态。
为此,在 Move 方法(指针方法)中,我 取消引用 指针,更新并检查此 可能 检查板。当我更改Board类型的单个值(例如possible.headers = "Possible Varient"
)的值时,原来的b
Board并没有改变。
但是在这里,当我调用方法 updateBoard() 时,它会更新两个板。我仍然收到错误(无法进入检查),但主线程认为 b.board
(棋盘位置)已更改。
func (b *Board) Move(orig, dest int) error {
// validation
...
// Update
possible := *b // A 'shallow copy'?
possible.updateBoard(orig, dest, val, isEmpassant, isCastle)
king := possible.findKingPositionOfThePlayerWhoMoved()
isCheck := possible.isInCheck(king) // bool takes the king to check for
if isCheck {
return errors.New("Cannot move into Check")
}
b.updateBoard(orig, dest, val, empassant, isCastle)
return nil
奇怪的是,并非所有由 updateBoard()
更新的值都发生了变化。所以 b.toMove
值不会改变,但 b.board
值会改变(棋子的位置)。这意味着如果我改为传递 possible := b
,游戏将永远是白棋(toMove 在 updateBoard() 方法中交替)。使用 possible := *b
,回合交替有效,直到有人进入检查。然后移动被应用到 b.board
,但错误被抛回并且它仍然是检查玩家回合(意味着 possible.updateBoard()
没有更新 b.toMove.
编辑
正如 abhink 指出的那样,在 Go Slices usage and internals、
Slicing does not copy the slice's data. It creates a new slice value that points to the original array.
b.board
,一个 []byte
,总是指向它的原始值(即使保存它的结构是 dereferenced。abhink 的答案使用 Go func copy(dst, src []Type) int
, https://golang.org/pkg/builtin/#copy , 复制指针值的快捷方式。
由于b.board
是一个切片类型,它是一个引用类型(https://blog.golang.org/go-slices-usage-and-internals)并且表现得像一个指针。因此,对 possible.board
所做的任何更改都将显示在 b
中。您可以像这样尝试制作 b.board
的副本:
func (b *Board) Move(orig, dest int) error {
// validation
...
// Update
possible := *b // A 'shallow copy'?
boardCopy := make([]byte, len(b.board))
copy(boardCopy, b.board)
possible.board = boardCopy
possible.updateBoard(orig, dest, val, isEmpassant, isCastle)
// ...
请注意,您必须对所有引用类型执行类似的操作。
取消引用不会创建副本。它 returns 您的指针指向的原始值。
您得到了一份副本,因为您将该值赋给了一个新变量。在 go 中,每个赋值都会像每次传递给函数一样生成一个副本。如果您分配或传递引用,则会复制该引用。
在你的例子中,你复制了 b 指向的值。在那个结构中有像 b.board 切片这样的指针(切片有一个指向底层数组的指针)。所以 go 正在创建切片的副本。副本仍然指向与原始 b 变量中的切片相同的数组。如果您更改该数组,那么您的两个板都会更改它。
您将需要对您的 Board 结构实施一个复制函数,该函数可以根据每个变量的类型和 returns 新板正确创建处理每个变量的结构副本。
类似于:
func (b *Board) copy() *Board {
boardCopy := make([]byte, len(b.board))
copy(boardCopy, b.board)
return &Board{
moveTo: b.moveTo,
board: boardCopy
...
}
}
希望对您有所帮助,我的解释不会令人困惑:)