Go语言的指针行为很奇怪。

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

GoLang pointers are behaving wierd

问题

为什么在下面的代码中改变node2.Next会影响到node2?

import "fmt"

type ListNode struct {
	Val  int
	Next *ListNode
}

func main() {
	node1 := ListNode{Val: 10, Next: nil}
	node2 := ListNode{Val: 100, Next: &node1}
	temp := node2

	if temp == node2 {
		fmt.Println("Equalssss1")
	}

	node2.Next = &(ListNode{1, nil})

	if temp == node2 {
		fmt.Println("Equalssss2")
	}
}

基本上为什么没有打印出"Equalssss2"?我觉得由于改变了node2.Next但没有改变node2,node2应该等于temp,为什么不是这样呢?

英文:

Why does changing node2.Next affects node2 in the below code?

import "fmt"

type ListNode struct {
	Val int
	Next *ListNode
}


func main() {
    node1 := ListNode{Val:10 , Next:nil}
	node2 := ListNode{Val:100 , Next:&node1}
	temp := node2

	if(temp == node2){
		fmt.Println("Equalssss1")
	}
	node2.Next = &(ListNode{1 , nil})
	if(temp == node2){
		fmt.Println("Equalssss2")
	}
}

Basically why doesn't "Equalssss2" doesn't get printed? I feel since node2.Next is changed but not node2, node2 should be equal to temp Why isn't it the case?

答案1

得分: 2

你的代码中没有出现指针错误。我怀疑你认为的指针实际上并不是指针,所以才会感到困惑。

在你的代码中,node1node2ListNode结构体,也就是值类型。当你将node2赋值给temp时,temp实际上是node2的一个副本,而不是指向它的指针或引用。

在Go语言中(简单来说),如果两个结构体的值类型相同,并且它们的所有字段都是可比较的且相等,那么它们就是相等的。

当你最初比较node2temp时,两个值的所有字段的值都是相同的,因此temp == node2的结果为true。

然后你改变了node2Next字段,因此当你再次比较temp == node2时,它们不再相等,因为它们在该字段的值上有所不同。

要获得你期望的行为,node1node2需要是指向ListNode的指针:

type ListNode struct {
	Val  int
	Next *ListNode
}

func main() {
	node1 := &ListNode{Val: 10, Next: nil}
	node2 := &ListNode{Val: 100, Next: node1}
	temp := node2

	if temp == node2 {
		fmt.Println("Equalssss1")
	}

	node2.Next = &(ListNode{1, nil})
	if temp == node2 {
		fmt.Println("Equalssss2")
	}
}

现在,temp也是指向ListNode的指针,而不是ListNode值的副本;temp被初始化为引用与node2相同的ListNode。也就是说,指针tempnode2是相等的,它们都指向同一个ListNode

现在,当你改变node2.Next时,这不会影响到temp仍然只是node2指针的副本,由于node2本身(指针)的值和temp都没有改变,它们仍然相等。

也就是说,如果你使用了指针,你的代码将会按照预期的方式运行。如果你认为指针的行为很奇怪,那是因为在这种情况下,你实际上并没有使用指针。 Go语言的指针行为很奇怪。

英文:

There are no misbehaving pointers in your code. I suspect that what you think are pointers are not actually pointers, hence your confusion.

In your code node1 and node2 are ListNode structs, i.e. _value types. When you assign node2 to temp, temp then holds a copy of node2, not a pointer or reference to it.

In Go, (in simple terms) two struct values are equal if they of the same type and consist of comparable fields that all evaluate as equal.

When you initially compare node2 and temp the values of all of the fields in each of the two values are the same, therefore temp == node2 evaluates true.

You then change the Next field of node2, therefore when you again compare temp == node2 they are no longer equal since they differ in the value of that field.

To get the behaviour you are expecting, node1 and node2 would need to be pointers to a ListNode:

type ListNode struct {
	Val  int
	Next *ListNode
}

func main() {
	node1 := &ListNode{Val: 10, Next: nil}
	node2 := &ListNode{Val: 100, Next: node1}
	temp := node2

	if temp == node2 {
		fmt.Println("Equalssss1")
	}

	node2.Next = &(ListNode{1, nil})
	if temp == node2 {
		fmt.Println("Equalssss2")
	}
}

temp is now also a pointer to a ListNode, rather than a copy of the ListNode value; temp is initialised to reference the same ListNode that node2 references. That is, the pointers temp and node2 are equal; they both point to the same ListNode.

Now, when you change node2.Next, this doesn't affect the fact that temp is still just a copy of the node2 pointer and since neither the value of node2 itself (the pointer) nor temp have changed, they are still equal.

i.e. if you were using pointers, your code would behave as expected. If you think that pointers are behaving "weirdly" it is because, in this instance, you are not actually using pointers. Go语言的指针行为很奇怪。

答案2

得分: 2

正如评论者在你的问题中指出的那样,这里没有发生任何奇怪的事情。在你的第一个条件语句之后添加以下代码:

fmt.Printf("temp is at %p and temp.Next is at %p\nnode2 is at %p and node2.Next is at %p\n", &temp, temp.Next, &node2, node2.Next)

这将输出:

temp is at 0xc00009e240 and temp.Next is at 0xc00009e220
node2 is at 0xc00009e230 and node2.Next is at 0xc00009e220

在你的第二个条件语句之后添加以下代码:

fmt.Printf("temp.Next has Val: %d\nnode2.Next has Val: %d\n", temp.Next.Val, node2.Next.Val)

这将输出:

temp.Next has Val: 10
node2.Next has Val: 1

你可以在这里看到结果。

node2temp 指向不同的对象,它们的字段值相同,直到你修改 node.Next。你的赋值语句 temp := node2 创建了一个结构体类型的新实例,并将源对象的值复制进去。它并没有创建一个指向 node 对象的指针——如果它这样做了,那么改变 node2.<field> 也会改变 temp.<field>。这在某种程度上会让人感到困惑,因为在 Go 中(与 C 不同),你不需要解引用指针/使用不同的访问器语法来访问结构体字段,所以下面的代码是完全正确的:

temp := &node2
node2.Val = 500
fmt.Printf("%d == %d\n, temp.Val, node2.Val)

最后,这里的一切都按照人们的预期发生。换句话说,你的第一个条件语句检查的是相等性而不是身份。不同的对象(身份)具有相同的值(相等性)。

英文:

As the commenters on your question are trying to point out, nothing at all weird is happening here. Add this after your first conditional:

fmt.Printf(&quot;temp is at %p and temp.Next is at %p\nnode2 is at %p and node2.Next is at %p\n&quot;, &amp;temp, temp.Next, &amp;node2, node2.Next)

this will output:

temp is at 0xc00009e240 and temp.Next is at 0xc00009e220
node2 is at 0xc00009e230 and node2.Next is at 0xc00009e220

and try adding this line after your second conditional:

fmt.Printf(&quot;temp.Next has Val: %d\nnode2.Next has Val: %d\n&quot;, temp.Next.Val, node2.Next.Val)

which will output:

temp.Next has Val: 10
node2.Next has Val: 1

You can see the result here.

node2 and temp point to different objects, both of which have the same field values until you modify node.Next. Your assignment temp := node2 makes creates a new instance of the struct type and copies in the values from the source object. It does not create a pointer to the node object - if it had, then changing node2.&lt;field&gt; would also change temp.&lt;field&gt;. This is made slightly confusing by the fact that, in go (unlike in say C) you do not need to dereference the pointer/use different accessor syntax to access struct fields, so the following would be completely fine:

temp := &amp;node2
node2.Val = 500
fmt.Printf(&quot;%d == %d\n, temp.Val, node2.Val)

In the end, everything here is happening exactly as one would expect. Put another way, your first conditional is checking equality but not identity. Different objects (identity) with the same values (equality).

huangapple
  • 本文由 发表于 2023年1月20日 02:02:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/75176483.html
匿名

发表评论

匿名网友

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

确定