修改取消引用的结构指针会更改大多数结构值,但不会更改切片

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
    ...
  }
}

希望对您有所帮助,我的解释不会令人困惑:)