解决golang中container/list包中的坑

作者:msn217 时间:2024-05-21 10:25:42 

golang中list包用法可以参看这篇文章

但是list包中大部分对于e *Element进行操作的元素都可能会导致程序崩溃,其根本原因是e是一个Element类型的指针,当然其也可能为nil,但是golang中list包中函数没有对其进行是否为nil的检查,变默认其非nil进行操作,所以这种情况下,便可能出现程序崩溃。

1.举个简单例子

Remove()函数


package main
import (
"container/list"
"fmt"
)

func main() {
l := list.New()
l.PushBack(1)
fmt.Println(l.Front().Value) //1
value := l.Remove(l.Front())
fmt.Println(value)            //1
value1 := l.Remove(l.Front()) //panic: runtime error: invalid memory address or nil pointer dereference
fmt.Println(value1)
}

从程序中可以直观的看出程序崩溃,原因是list中只有1个元素,但是要删除2个元素。但是再进一步查看一下原因,便会得出如下结果。

golang中Front()函数实现如下


func (l *List) Front() *Element {
   if l.len == 0 {
       return nil
   }
   return l.root.next
}

由此可见,当第一次删除之后。list的长度变为0,此时在调用l.Remove(l.Front()),其中l.Front()返回的是一个nil。

接下来再看golang中Remove()函数实现,该函数并没有判定e是否为nil,变直接默认其为非nil,直接对其进行e.list或者e.Value取值操作。

当e为nil时,这两个操作都将会造成程序崩溃,这也就是为什么上面程序会崩溃的原因。


func (l *List) Remove(e *Element) interface{} {
if e.list == l {
 // if e.list == l, l must have been initialized when e was inserted
 // in l or l == nil (e is a zero Element) and l.remove will crash
 l.remove(e)
}
return e.Value
}

2.(l *list)PushBackList(other *list)

该函数用于将other list中元素添加在l list的后面。

基本实现思想是取出other中所有元素,将其顺次挂载在l列表中,但是golang中实现有问题

代码如下


func (l *List) PushBackList(other *List) {
l.lazyInit()
for i, e := other.Len(), other.Front(); i > 0; i, e = i-1, e.Next() {
 l.insertValue(e.Value, l.root.prev)
}
}

其具体思想是首先获取other的长度n,然后循环n次取出其元素将其插入l中。问题就出现在循环n次,如果在这个过程中other的元素变化的话,例如其中有些元素被删除了,这就导致e的指针可能为nil,此时再利用e.Value取值,程序便会崩溃。

如下所示


package main

import (
"container/list"
"runtime"
)

func main() {
runtime.GOMAXPROCS(8)
l := list.New()
ls := list.New()
for i := 0; i < 10000; i++ {
 ls.PushBack(i)
}
go ls.Remove(l.Back())
l.PushBackList(ls) //invalid memory address or nil pointer dereference
}

如程序中所示,再讲ls中元素添加到l过程中,如果ls中元素减少,程序便会崩溃。原因如上面分析。

建议:

在golang中如果对与list的操作只有串行操作,则只需要注意检查元素指针是否为nil便可避免程序崩溃,如果程序中会并发处理list中元素,建议对list进行加写锁(全局锁),然后再操作。注意,读写锁无法保证并行处理list时程序的安全性。

补充:golang list 链表

看代码吧~


package main
import (
"container/list"
"fmt"
)

func main() {
dataList := list.New()

dataList.PushBack(1)// 插入末尾
dataList.PushBack(2)
dataList.PushFront(3) // 插入表头
dataList.PushBack(4)
dataList.PushBack(5)
m := dataList.PushBack(6)
m1 := dataList.InsertBefore(7,m)// 6 之前插入 7
m2 := dataList.InsertAfter(8,m)// 6 之后插入 8

// 从链表头开始遍历
for e := dataList.Front(); e != nil; e = e.Next() {
fmt.Println(e.Value) // 打印值
}

fmt.Println("----------------------------------------")

dataList.Remove(dataList.Front())// 移除头部
dataList.MoveBefore(m2, m)// 将m2移动m之前
dataList.MoveAfter(m1, m)
dataList.Remove(m)// 移除

//PushBackList// 插入列表
//PushFrontList//

// 从链表头开始遍历
for e := dataList.Front(); e != nil; e = e.Next() {
fmt.Println(e.Value) // 打印值
}

fmt.Println("----------------------------------------")

// 从链表尾开始遍历
for e := dataList.Back(); e != nil; e = e.Prev() {
fmt.Println(e.Value, " ")
}

fmt.Println("----------------------------------------")
dataList.Init()// 清空链表
// 从链表头开始遍历
for e := dataList.Front(); e != nil; e = e.Next() {
fmt.Println(e.Value) // 打印值
}
}

运行结果:

3
1
2
4
5
7
6
8
----------------------------------------
1
2
4
5
8
7
----------------------------------------
7  
8  
5  
4  
2  
1  
----------------------------------------

Process finished with exit code 0

以上为个人经验,希望能给大家一个参考,也希望大家多多支持asp之家。如有错误或未考虑完全的地方,望不吝赐教。

来源:https://blog.csdn.net/chenbaoke/article/details/42782113

标签:golang,container,list
0
投稿

猜你喜欢

  • Python3.5常见内置方法参数用法实例详解

    2023-02-24 21:38:38
  • python实现单张图像拼接与批量图片拼接

    2023-07-28 12:33:36
  • Python多分支if语句的使用

    2022-07-17 17:52:54
  • 浅谈MySQL存储引擎选择 InnoDB还是MyISAM

    2009-05-10 20:02:00
  • 如何把数据库记录显示到列表框里去?

    2009-11-06 13:48:00
  • Python中的Decorator装饰器的使用示例

    2023-07-02 10:37:44
  • Vue如何获取url路由地址和参数简单示例

    2024-05-09 09:53:08
  • 让代码变得更易维护的7个Python库

    2022-11-28 02:49:12
  • Python利用watchdog模块监控文件变化

    2023-10-23 22:27:22
  • 基于Python socket实现简易网络聊天室

    2021-10-19 09:10:30
  • 解决Pycharm下面出现No R interpreter defined的问题

    2021-03-03 01:33:28
  • IE9一个非常牛的“bug”

    2010-05-07 12:45:00
  • pyinstaller使用大全

    2022-12-17 21:37:28
  • 最近写的一个asp缓存函数

    2008-11-25 14:07:00
  • 如何把数据从SQL Server导出到Access或Excel中去?

    2009-11-02 20:26:00
  • js传值后台中文出现乱码的解决方法

    2024-02-25 11:52:23
  • FCKeditor新版本发布,并更名为CKeditor

    2009-09-08 13:09:00
  • MySQL表自增id溢出的故障复盘解决

    2024-01-24 05:00:50
  • 在EF中使用MySQL的方法及常见问题

    2024-01-28 11:22:03
  • javascript实现Table排序的方法

    2024-04-19 10:16:19
  • asp之家 网络编程 m.aspxhome.com