值拷贝导致使用container/list出现的诡异问题分析

Nate-Ding · · 644 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

# 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中尽量避免对复杂对象做简单的值传递赋值。

有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

644 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传