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

栏目: 数据库 · 发布时间: 5年前

内容简介:输出输出为什么

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

先看正确使用list的两种方式

  • 使用list.New()

    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)
    	}
    }

    输出

  • 使用list.List{}

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)
   }
}

输出

list的错误使用方式

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)
   }
}

输出

<nil>

错误原因分析

为什么 *list.New() 方式给 l 后,放进 l 中的元素打印不出来了呢?

让我们来看看l.Front()中的逻辑, 如果 len 不为 0 ,返回的是 l.root.next

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 ,我们要先添加一个元素

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)
   }
}

输出

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 内部链表如下

lPtr.root.prev->Element{"ptr1"}->&lPtr.root  //Element{"ptr1"}地址:0xc000084180
lPtr.root.next->Element{"ptr1"}->&lPtr.root

l := *lPtr 时,内部属性的值拷贝如下:

l.root.prev->Element{"ptr1"}->&lPtr.root  //Element{"ptr1"}地址:0xc000084180
l.root.next->Element{"ptr1"}->&lPtr.root

l 添加元素 "1" 后, l 内部链表如下

l.root.prev->Element{"1"}->Element{"ptr1"}->&lPtr.root
l.root.next->Element{"ptr1"}->Element{"1"}->&lPtr.root

至此就是弄清楚了上面发生怪异输出结果的原因了。

注意:在 go 中尽量避免对复杂对象做简单的值传递赋值。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

零基础学C语言

零基础学C语言

康莉//李宽 / 机械工业 / 2009-4 / 48.00元

《零基础学C语言》的特点是内容全面、翔实,通俗易懂,循序渐进地介绍了C语言各方面的知识,重点突出。《零基础学C语言》含有大量实例,代码短小精炼,紧扣所讲要点的本质,以加深读者的印象,同时结合笔者多年使用C语言的经验,阐述了很多代码编写技巧,读者可将代码复制到自己的机器上进行实验,自行实践和演练。C语言是编程方式灵活多样、功能强大、应用广泛的一种程序设计语言。从程序设计语言的发展历程来看,尽管后来出......一起来看看 《零基础学C语言》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

在线进制转换器
在线进制转换器

各进制数互转换器