# golang值拷贝导致使用container/list出现的诡异问题分析
## 先看正确使用list的两种方式
- 使用list.New()
```go
package main
import (
"container/list"
"fmt"
)
func main() {
lPtr := list.New()
lPtr.PushBack(1)
for front := lPtr.Front(); front != nil; front = front.Next() {
fmt.Println(front.Value)
}
}
```
输出
```go
1
```
- 使用list.List{}
```go
package main
import (
"container/list"
"fmt"
)
func main() {
l := list.List{}
l.PushBack(1)
for front := l.Front(); front != nil; front = front.Next() {
fmt.Println(front.Value)
}
}
```
输出
```go
1
```
## list的错误使用方式
```go
package main
import (
"container/list"
"fmt"
)
func main() {
l := *list.New()
l.PushBack(1)
for front := l.Front(); front != nil; front = front.Next() {
fmt.Println(front.Value)
}
}
```
输出
```go
<nil>
```
### 错误原因分析
为什么 `*list.New()`方式给`l`后,放进`l`中的元素打印不出来了呢?
让我们来看看l.Front()中的逻辑, 如果`len`不为`0`,返回的是`l.root.next`
```go
func (l *List) Front() *Element {
if l.len == 0 {
return nil
}
return l.root.next
}
```
上面打印list的循环条件是`front != nil`,打印不出元素,也就说明`l.root.next==nil`
那我们把`l.root.next`的地址打印出来看看, 当`len!=0`时才会返回`l.root.next`,我们要先添加一个元素
```go
package main
import (
"container/list"
"fmt"
)
func main() {
lPtr := list.New()
lPtr.PushBack("ptr1")
fmt.Printf("lPtr=%p lPtr.root.next=%p\n", lPtr, lPtr.Front())
l := *lPtr
l.PushBack(1)
fmt.Printf("&l=%p l.root.next=%p\n", &l, l.Front())
for front := l.Front(); front != nil; front = front.Next() {
fmt.Println(front.Value)
}
}
```
输出
```go
lPtr=0xc000084150 lPtr.root.next=0xc000084180
&l=0xc0000841b0 l.root.next=0xc000084180
ptr1
1
<nil>
```
我们看到`l.root.next`的值`lPtr.root.next`的值都是`0xc000084180`, 问题就出在这里
向`lPtr`添加元素`"ptr1"`后,`lPtr`内部链表如下
```go
lPtr.root.prev->Element{"ptr1"}->&lPtr.root //Element{"ptr1"}地址:0xc000084180
lPtr.root.next->Element{"ptr1"}->&lPtr.root
```
当`l := *lPtr`时,内部属性的值拷贝如下:
```go
l.root.prev->Element{"ptr1"}->&lPtr.root //Element{"ptr1"}地址:0xc000084180
l.root.next->Element{"ptr1"}->&lPtr.root
```
向`l`添加元素`"1"`后,`l`内部链表如下
```go
l.root.prev->Element{"1"}->Element{"ptr1"}->&lPtr.root
l.root.next->Element{"ptr1"}->Element{"1"}->&lPtr.root
```
至此就是弄清楚了上面发生怪异输出结果的原因了。
注意:在go中尽量避免对复杂对象做简单的值传递赋值。
有疑问加站长微信联系(非本文作者))