【问题标题】:Modify dereferenced struct pointer changes most struct values, but not slices修改取消引用的结构指针会更改大多数结构值,但不会更改切片
【发布时间】:2016-11-10 20:05:14
【问题描述】:

我正在尝试创建结构板(棋盘)的浅表副本。在将移动保存到棋盘之前,我需要检查该移动是否会检查移动器。

为此,在 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,但错误被抛出并且它仍然是checked-players轮流(意思是possible.updateBoard()没有更新b.toMove。

编辑

正如 abhink 指出的,在Go Slices usage and internals

切片不会复制切片的数据。它创建一个指向原始数组的新切片值。

b.board,一个[]byte,总是指向它的原始值(即使保存它的结构被取消引用。abhink 的答案使用Go func copy(dst, src []Type) inthttps://golang.org/pkg/builtin/#copy,一个复制指针值的快捷方式。

【问题讨论】:

  • b.board 是指针类型吗(board 是指向某些板类型的指针)?如果是这样,那么浅拷贝possible := *b 会创建一个新结构possible,它的board 字段中的指针地址与b.board 相同。现在通过引用 possible.board 完成的任何访问/修改都将更改 b 的值,因为两者共享相同的指针。
  • 不,b.board[]byte

标签: pointers methods go slice chess


【解决方案1】:

由于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)

    // ...

请注意,您必须对所有引用类型执行类似的操作。

【讨论】:

    【解决方案2】:

    取消引用不会复制。它返回指针指向的原始值。

    您获得了一份副本,因为您将该值分配给了一个新变量。在 go 中,每个分配都会生成一个副本,就像每次传递给一个函数一样。如果您分配或传递引用,则会复制该引用。

    在您的情况下,您复制 b 指向的值。在该结构中有像 b.board 切片这样的指针(切片有一个指向底层数组的指针)。所以 go 正在创建切片的副本。副本仍然指向与原始 b 变量中的切片相同的数组。如果您更改该数组,则您的两个板都会更改它。

    您需要为您的 Board 结构实现一个复制功能,该功能可以正确地创建您的结构的副本,根据其类型处理每个变量并返回该新板。

    类似:

    func (b *Board) copy() *Board {
      boardCopy := make([]byte, len(b.board))
      copy(boardCopy, b.board)
    
      return &Board{
        moveTo: b.moveTo,
        board: boardCopy
        ...
      }
    }
    

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

    【讨论】:

      猜你喜欢
      • 2014-01-29
      • 1970-01-01
      • 1970-01-01
      • 2020-09-13
      • 1970-01-01
      • 2020-11-20
      • 1970-01-01
      • 2020-12-23
      • 2017-07-27
      相关资源
      最近更新 更多