首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Golang中container/list包中的坑

Golang中container/list包中的坑

作者头像
李海彬
发布于 2018-03-21 02:24:49
发布于 2018-03-21 02:24:49
1.3K00
代码可运行
举报
文章被收录于专栏:Golang语言社区Golang语言社区
运行总次数:0
代码可运行

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

1.举个简单例子,Remove()函数

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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()函数实现如下

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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时,这两个操作都将会造成程序崩溃,这也就是为什么上面程序会崩溃的原因。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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中实现有问题,代码如下。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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取值,程序便会崩溃。如下所示。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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时程序的安全性。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2016-04-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Golang语言社区 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Go队列和堆栈
golang,其实我的实现是利用container/list包实现的,其实container/list包很强大. package main import ( "fmt" "container/list" ) func main() { // 生成队列 l := list.New() // 入队, 压栈 l.PushBack(1) l.PushBack(2) l.PushBack(
李海彬
2018/03/27
1.9K0
golang 的container/list (一)
问题1:有了slice,还要list做什么? 问题2:list的底层实现是什么? 带着这两个问题来什么有浅入深的学习golang 语言 。 首先来看官方的解释:
地球流浪猫
2018/08/02
1K0
golang 的container/list  (一)
go语言坑之list删除所有元素
30 Mar 2017 go语言坑之list删除所有元素 go提供了一个list包,类似python的list,可以存储任意类型的数据,并提供了相应的API,如下: type Element func (e *Element) Next() *Element func (e *Element) Prev() *Element type List func New() *List func (l *List) Back() *E
俊采
2018/05/15
2.2K0
Golang标准库 container/list
理解起来不难,关键的地方就是理解两个结构体,一个哨兵元素。然后挑几个关键的方法分析下。
后端云
2023/02/10
3520
Golang标准库 container/list
Go标准库:container/list
在Go语言的标准库中,container/list包提供了一个双向链表的实现,这对于需要频繁插入和删除操作的场景非常有用。双向链表是一种线性数据结构,它由一系列节点组成,每个节点包含数据和两个指针,分别指向前一个节点和后一个节点。下面我们将详细介绍如何使用container/list包,以及它的内部实现和常见操作。
孟斯特
2024/07/03
2880
Go标准库:container/list
【愚公系列】2022年07月 Go教学课程 023-Go容器之列表
列表是一种数据结构,由多元素成的有限序列,即按照一定的线性顺序排列而成的数据项的集合,在这种数据结构上可以进行元素的的插入,删除,修改,和查找。
愚公搬代码
2022/08/01
2410
【愚公系列】2022年07月 Go教学课程 023-Go容器之列表
008-golang-container/list 链表的简单使用
一.前言 需要对一个文件进行编辑,主要的操作有 修改某行的内容 插入一行获多行 删除某行 文件不是特别大的文件,所以可以加载到内存中直接处理。感觉最好的数据结构还是链表 二.golang中的链表 container/list 是golang中内置的链表库 和大多数语言中内置的链表库一样,container/list 不是线程安全的。 1. container/list API接口 func (e *Element) Next() *Element //返回该元素的下一个元素,如果没有下一个元素则返回ni
上善若水.夏
2018/09/28
1.8K0
Go-List
要点 Element表示链表的一个元素,List表示链表 访问元素的值: .Value list是双向链表,可以在指定的位置插入元素 初始化: New()。——和var x List 是等价的,但New()可读性更好。 元素个数: Len() 遍历: (1) Front()和Back()分别取链表的第一个元素和最后一个元素。如果为空,则返回nil。(2) Next()和Prev()分别返回当前元素的写一个或前一个元素。如果为空,则返回nil。 (3) 遍历整个链表的通用方法就是先Front(),然后依次Ne
李海彬
2018/03/23
7220
Golang语言情怀-第52期 Go 语言标准库翻译 compress/list
PushFrontList创建链表other的拷贝,并将拷贝的最后一个位置连接到链表l的第一个位置。
李海彬
2021/03/09
3100
golang slice 与list 的性能分析。
本机运行结果: 声明:测试结果为个人电脑的测试结果,仅供参考。 slice 创建速度:1.3029245s list 创建速度: 9.7489181s 对于1亿条数据,slice 的创建和添加元素的速度约是list的7~8倍。
地球流浪猫
2018/08/02
2.4K0
LeetCode 78.子集 - Go 实现
给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
王小明_HIT
2023/03/01
2250
LeetCode 78.子集 - Go 实现
1.Go-copy函数、sort排序、双向链表、list操作和双向循环链表
双向链表结构中元素在内存中不是紧邻空间,而是每个元素中存放上一个元素和后一个元素的地址
zhang_derek
2019/08/06
8750
1.Go-copy函数、sort排序、双向链表、list操作和双向循环链表
golang实现队列服务-日常实战总结no.2
队列的概念:队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。队列中没有元素时,称为空队列。
公众号-利志分享
2022/04/25
7980
Golang常见的坑笔记
Format 的时候 时间必须是 2006-01-02 15:04:05 ,奇葩时间。
solate
2019/07/19
4520
Go:双向链表实现,container/list包探讨
在Go语言的标准库中,container/list包提供了双向链表的实现。链表是一种常见的数据结构,它通过节点的序列实现,每个节点都包含数据及对前一个节点和后一个节点的引用。Go语言的container/list包提供了操作链表的多种方法,如插入、删除、搜索和移动元素等。
运维开发王义杰
2024/05/10
3550
Go:双向链表实现,container/list包探讨
Go语言的容器 - Java技术债务
切片的零值是 nil 一个 nil 切片的长度和容量为 0,并且没有底层数组。切片可以包含任何类型,包括其他切片。
Java技术债务
2024/06/21
1890
Go语言的队列和堆栈实现方法
本文实例讲述了Go语言的队列和堆栈实现方法。分享给大家供大家参考。具体如下: golang,其实我的实现是利用container/list包实现的,其实container/list包很强大. package main import ( "fmt" "container/list" ) func main() { // 生成队列 l := list.New() // 入队, 压栈 l.PushBack(1) l.PushBack(2) l.Pu
李海彬
2018/03/22
8740
GO语言实现的端口扫描器分享
//GO语言 实现端口扫描 //缺陷 //port 无法设置成全局变量不知道怎么设置的 //var l = list.New() 这个是数组操作并不是消息队列 跟消息队列功能类似 //实现功能 //实现生成 IP段 //实现端口扫描 //实现参数传入 //写入文件到本地 //main.go 58.215.20.30 58.215.201.30 80 //文件名 开始IP 结束IP 扫描端口 //QQ2929
李海彬
2018/03/19
2K0
Go 语言入门系列:列表与字典及其遍历
前面的文章主要介绍了 Go 容器的数组和切片的基本概念以及使用。本文将会介绍列表与字典在 Go 语言中相关的使用,以及几种常用容易的遍历及其使用。。
aoho求索
2021/08/06
3.5K0
探索Kubernetes 1.28调度器OOM的根源
Kubernetes社区在1.28版本中默认开启了调度特性SchedulerQueueingHints,导致调度组件内存异常。为了临时解决内存等问题,社区在1.28.5中将该特性调整为默认关闭。因为问题并未完全修复,所以建议审慎开启该特性。
zouyee
2024/03/04
3710
探索Kubernetes 1.28调度器OOM的根源
相关推荐
Go队列和堆栈
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档