将 nil 赋值给指针。

huangapple go评论84阅读模式
英文:

assigning nil to pointer

问题

我正在尝试为一个列表实现一个delete()方法(没有HEAD引用)。

我发现我可以将参数修改为一个结构体。

func (l *LinkedList) Delete(n *Node) {
    if n.next == nil {
        n = nil
    } else {
        current := &n
        *n = *n.next
        *current = nil
    }
}

"else"部分运行正常,但是删除最后一个节点不会修改列表。

尝试使用

*n = nil

但是我得到了编译错误。

无法将nil用作分配类型Node

在这个playground中可以完整查看代码:

http://play.golang.org/p/Dhzyd7QHEw

英文:

I'm trying to implement a delete() method to a list (no HEAD ref)

I find out that I can modify the parameter to a struct.

func (l *LinkedList) Delete(n *Node) {
	if n.next == nil {
		n = nil
	} else {
		current := &n
		*n = *n.next
		*current = nil
	}
	
}

The "else" part works fine, but deleting the last node does not modify the list

Tried using

*n = nil

But then I have the compile error.

>cannot use nil as type Node in assignment

Code complete in this playground:

http://play.golang.org/p/Dhzyd7QHEw

答案1

得分: 5

你只是做错了。我的意思是从单链表中删除经典元素。正确的方法是:

func (l *LinkedList) Delete(n *Node) {
    // 如果要删除头元素-只需移动头指针
    if l.head == n {
        l.head = n.next
        return
    }
    // 否则找到我们要删除的前一个元素
    current := l.head
    for current != nil && current.next != n {
        current = current.next
    }
    // 将前一个元素的下一个指针移动到下一个元素
    if current != nil {
        current.next = n.next
    }
}

所以你的例子中有什么问题呢?在你的_Delete_函数中,你接收一个指向某个节点的指针。这个指针是局部变量,就像一个局部变量一样。如果你在函数内部将nil赋值给局部变量,外部是看不到这样的赋值的。你想要做的是改变前一个列表项的_next_指针。这样,该项将不再在列表中。垃圾回收将删除实际分配的内存。

更新:

由于go指针是“真正的”指针,可以通过使用额外的间接级别来实现,而无需为头部删除添加特殊情况,正如Linus在他著名的TED演讲(以及更早的slashdot Q&A)中建议的那样(参见“favorite hack”问题):

func (l *LinkedList) Delete(n *Node) {
    // 使用头指针的地址初始化间接指针
    indirect := &(l.head)
    // 直到间接指针具有指向我们要删除的节点的指针的地址
    for *indirect != n {
        // 检查它是否是列表的末尾
        if (*indirect).next == nil {
            // 我们要删除的节点不在列表中
            return
        }
        // 将间接指针设置为下一个指针的地址
        indirect = &((*indirect).next)
    }
    // 间接指针具有我们需要修改以删除节点的指针的地址
    *indirect = n.next
}

在我看来,两个间接级别比为删除头元素添加一个简单的特殊情况更难理解,但是Linus并不像我这样的普通开发者 将 nil 赋值给指针。

英文:

You're just doing it wrong. I mean classic element removal from single linked list. Right way:

func (l *LinkedList) Delete(n *Node) {
    // if we want to delete the head element - just move the head pointer
    if l.head == n {
        l.head = n.next
        return
    }
    // otherwise find previous element to the one we are deleting
    current := l.head
    for current != nil && current.next != n {
        current = current.next
    }
    // and move that previous element next pointer to the next element
    if current != nil {
        current.next = n.next
    }
}

https://play.golang.org/p/_NlJw_fPWQD

So what was wrong in your example? In your Delete function you are receiving a pointer to some node. This pointer is local to your function, it's like a local variable. It doesn't matter if you assign nil to a local variable inside your function. Outside - no one will see such assignments. What you want to do - is to change the next pointer of the previous list item. This way the item will no longer be in the list. GC will remove the actual allocated memory.

UPDATE:

Since go pointers are "real" pointers, this can be implemented without special case for the head removal, by using an additional level of indirection, as suggested by Linus in his famous TED talk (and earlier in slashdot Q&A - see "favorite hack" question):

func (l *LinkedList) Delete(n *Node) {
    // initialize indirect with the address of a head pointer
    indirect := &(l.head)
    // until indirect has address of a pointer to the node we're deleting
    for *indirect != n {
        // check that it's not the end of the list
        if (*indirect).next == nil {
            // the node we're tryign to delete is not in the list
            return
        }
        // set indirect to the address of the next pointer
        indirect = &(*indirect).next
    }
    // indirect has address of a pointer we need to modify to delete the node
    *indirect = n.next
}

https://play.golang.org/p/hDy3hB5LUME

IMO two levels of inderection is harder to understand than a simple special case for deleting the head element, but Linus is not exactly an ordinary developer like myself 将 nil 赋值给指针。

huangapple
  • 本文由 发表于 2013年12月30日 13:59:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/20834178.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定