C++ list链表的使用和简单模拟实现
目录
前言
1. list的简介
2.list讲解和模拟实现
2.1 默认构造函数和push_back函数
2.2 迭代器实现
2.2.1 非const正向迭代器
2.2.2 const正向迭代器
2.2.3 反向迭代器
2.3 插入删除函数
2.3.1 insert和erase
2.3.2 push_back pop_back push_front pop_front
2.4 构造函数,赋值拷贝函数,析构函数
总结
前言
这篇文章讲述常用容器list的使用和一些重要部分的简单模拟实现,仅仅只是了解一些实现方法。内容丰富,干货多多。
1. list的简介
- list是序列容器,允许在序列内的任何位置进行常量时间的插入和删除操作,以及两个方向的迭代。
- 列表容器被实现为双链表;双链表中每个元素存储在互不相关的几点钟,在节点中通过指针指向其前一个元素和后一个元素。
- 它们与forward_list非常相似:主要区别在于forward_list对象是单链表,因此它们只能向前迭代,让其更简单高效。
- 与其他基本标准序列容器(array、vector和deque)相比,列表在容器内的任何位置插入、提取和移动元素方面通常表现更好。
- 与其他序列容器相比,列表和forward_lists的主要缺点是它们无法通过位置直接访问元素;例如,要访问列表中的第八个元素,必须从已知位置(如开始或结束)迭代到该位置,这需要在两者之间的距离上花费线性时间。它们还消耗一些额外的内存来保存与每个元素相关联的链接信息(对于包含小元素的大型列表来说,这可能是一个重要因素)。
- 下面是展示list类的内部成员变量结构,并且之后要简单模拟实现,所以加上一个命名空间,防止与库中list起冲突。
- list即是双向链表结构,我们使用模版定义一个ListNode类,内部成员变量有两个指针,指向前一个元素和后一个元素,还有存储T类型的元素。在list类中,我们可以使用typedef简化LIstNode<T>成Node。Node类型的指针变量_head,代表第一个哨兵位结点,不存放元素。
- 其中ListNode使用struct而不用class,因为struct默认权限是公有的,且ListNode类里面的所有内容list都会使用到,所以直接用struct定义这个类。
namespace User
{template<class T>struct ListNode{ListNode*<T> _next;ListNode*<T> _prev;T _data;//...};template<class T>class list{typedef ListNode<T> Node; //...private:Node* _head;};
}
2.list讲解和模拟实现
2.1 默认构造函数和push_back函数
默认构造函数构造一个只含有哨兵位结点的链表,并且哨兵位结点的的两个指针指向自身。我们暂时使用库里的list,先创建一个int类型的list容器,调用的是默认构造函数,之后尾插几个整数。遍历整个链表一般使用迭代器,还可以使用范围for。因为物理结构是不连续的,不支持使用下标方括号遍历链表。
list();//默认构造函数void test_list1()
{std::list<int> lt1;lt1.push_back(1);lt1.push_back(2);lt1.push_back(3);lt1.push_back(4);list<int>::iterator it1 = lt1.begin();while (it1 != lt1.end()){cout << *it1 << " ";++it1;}cout << endl;for (const auto& e : lt1){cout << e << " ";}cout << endl;
}
运行结果如下:
- 实现一个list的默认构造函数,使用new动态开辟一个Node类型元素的内存空间,传入一个T类型的值调用LIstNode类的构造函数。下面写的是T()表示如果是自定义类型,就调用该自定义类型的构造函数,如果是内置类型,会初始化一个该类型的值,整型一般初始化为0。
- 既然list构造函数中调用了LIstNode类的构造函数,LIstNode的构造函数也需要实现。构造函数的参数是const修饰的T类型引用的data变量,顺便给上一个缺省值T(),跟上面的用法相同。
- push_back函数先动态开辟一个新结点,传入x初始化。然后再改变结点指向的位置即可。
template<class T>
struct ListNode
{ListNode*<T> _next;ListNode*<T> _prev;T _data;ListNode(const T& data = T()):_next(nullptr),_prev(nullptr),_data(data){}
};template<class T>
class list
{typedef ListNode<T> Node;
public:list(){_head = new Node(T());_head->_next = _head;_head->_prev = _head;}void push_back(const T& x){Node* newnode = new Node(x);Node* tail = _head->_prev;// head tail newnodetail->_next = newnode;newnode->_prev = tail;newnode->_next = _head;_head->_prev = newnode;}private:Node* _head;
};
2.2 迭代器实现
2.2.1 非const正向迭代器
list因本身物理空间不连续,需要通过结点存储前后元素地址,进行访问。那么list的迭代器需要封装成一个类,重载前置++,后置--,!=,==,*等符号。
template<class T>
struct ListIterator
{typedef ListNode<T> Node;typedef ListIterator<T> Self;Node* _node;//...
};
- typedef可以帮我们简化类名称,方便之后使用,重命名ListNode<T>类为Node,还有将ListIterator<T>类重命名为Self。
ListIterator(Node* node):_node(node){}
- 构造函数实现一个有参的构造函数,使用初始化列表初始化_node。
template<class T>
class list
{typedef ListNode<T> Node;public:typedef ListIterator<T> iterator;iterator begin(){//iterator it(_head->_next);//return it//下面是隐士类型转换调用return iterator(_head->_next);}iterator end(){return iterator(_head);}};
- 在list中提供begin和end函数,对迭代器进行初始化,或者遍历时做判断。
- begin函数中,可以先创建一个iterator对象,再返回该对象。也可以直接使用匿名对象,内部传结点参数,进行隐式类型转换。
- end函数提供最后一个元素的下一个位置,所以用哨兵位结点进行构造。
// ++it 前置Self& operator++(){_node = _node->_next;return *this;}// it++ 后置Self operator++(int){Self tmp(*this);_node = _node->_next;return tmp;}// --it 前置Self& operator--(){_node = _node->_prev;return *this;}
- 前置++,表示访问后一个元素,让_node赋值为_next指针即可。返回类型使用Self的引用,不用再次拷贝,更高效,记得要返回*this。
- 后置++,返回当前的位置,但是迭代器自身指向下一个元素。创建一个Self类型的tmp变量对*this进行拷贝构造,然后自身赋值为下一个结点指针,返回tmp。
- 前置--,表示访问前一个元素。做法跟前置++类似,让_node赋值为它的_prev指针即可。也是使用Self引用类型返回。
T& operator*(){return _node->_data;}
- *符号,表示要解引用到Node类型存储的元素。返回类型使用T类型的引用,直接返回data的别名,减少拷贝。
bool operator!=(const Self& it){return _node != it._node;}bool operator==(const Self& it){return _node == it._node;}
- !=和==可直接返回两个迭代器结点地址比较结果。
- ->符号比较特别,表示当前结点元素数据的地址,可以像结构体->一样使用。
T* operator->(){return &_node->_data;}
虽然operator->看起来比较奇特,但是也有它的应用场景。
- 看下面的代码,我们定义一个Pos,Pos是表示坐标位置的类。我们创建一个数据类型为Pos的list容器,尾插三个Pos类对象数据。
- 使用迭代遍历。如果在while循环使用*it,进行打印,程序会报错,这是因为Pos类没有匹配的cout流插入函数,在全局作用域下也没有重载cout函数。
- 所以可以用(*it)._row这样的方式打印两个整型坐标,*符号返回的是结点的数据别名,在这里是Pos类变量的别名,我们可以用.符号进行打印,但是看起来太别扭了。
- 重载的->就派上用场了,我们使用->就像结构体指针访问内部对象。不过正常来说应该是两个->符号,代码中有展开示例。为了可读性,只用写一个->符号就可以使用。
struct Pos
{int _row;int _col;Pos(int row = 0, int col = 0):_row(row), _col(col){}
};void test_list2()
{list<Pos> lt1;lt1.push_back(Pos(100, 100));lt1.push_back(Pos(200, 100));lt1.push_back(Pos(300, 100));//临时对象可以调用非静态成员函数,但是不能引用定义list<Pos>::iterator it = lt1.begin();while (it != lt1.end()){//cout<< *it <<endl;//error,这样写无法运行成功。//可以这样写但是太别扭了//cout << (*it)._row << ":" << (*it)._col << " ";//为了可读性,省略了一个->cout << it->_row << ":" << it->_col << " " << endl;//cout << it->->_row << ":" << it->->_col << " "; 展开就成了下面的样子cout << it.operator->()->_row << ":" << it.operator->()->_col << " "<< endl;++it;}cout << endl;
}
运行结果如下:
2.2.2 const正向迭代器
- 非const对象的迭代器迭代器指向的元素可以修改,但是有时候容器内部元素加上const修饰,不可以修改。因此,还要提供const类型的迭代器。我们可以使用上面的代码,只要将operator*和operator->函数的返回类型加上const修饰就行。
template<class T>
struct ListIterator
{typedef ListNode<T> Node;typedef ListIterator<T> Self;Node* _node;ListIterator(Node* node = Node())//默认构造:_node(node){}Self& operator++(){_node = _node->_next;return *this;}Self operator++(int){Self tmp(*this);_node = _node->_next;return tmp;}Self& operator--(){_node = _node->_prev;return *this;}const T& operator*(){return _node->_data;}const T* operator->(){return &_node->_data;}bool operator!=(const Self& it){return _node != it._node;}bool operator==(const Self& it){return _node == it._node;}
};template<class T>
class list
{typedef ListNode<T> Node;public:typedef ListIterator<T> iterator;typedef ListIterator<T> const_iterator;const_iterator begin() const{return const_iterator(_head->_next);}const_iterator end() const{return const_iterator(_head);}//...
};
- 但是这两类迭代器就只有operator*和operator->函数的返回类型不同而已,返回类型上有区别,其实我们可以借助模版解决。
- 只需要在定义两个模版类型参数Ref和Ptr,分别表示引用和指针,在list中分别传不同的参数表示iterator和const_iterator。
template<class T, class Ref, class Ptr>struct ListIterator{typedef ListNode<T> Node;typedef ListIterator<T, Ref, Ptr> Self;Node* _node;ListIterator(Node* node = Node())//默认构造:_node(node){}Self& operator++(){_node = _node->_next;return *this;}Self operator++(int){Self tmp(*this);_node = _node->_next;return tmp;}Self& operator--(){_node = _node->_prev;return *this;}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}bool operator!=(const Self& it){return _node != it._node;}bool operator==(const Self& it){return _node == it._node;}};template<class T>class list{typedef ListNode<T> Node;public:typedef ListIterator<T, T&, T*> iterator;typedef ListIterator<T, const T&, const T*> const_iterator;const_iterator begin() const{return const_iterator(_head->_next);}const_iterator end() const{return const_iterator(_head);}iterator begin(){return iterator(_head->_next);}iterator end(){return iterator(_head);}//...};
2.2.3 反向迭代器
反向迭代器的模拟实现和正向迭代器类似,需要注意++和--跟正向迭代器的的方向完全反过来,还要注意迭代器begin和end函数给的结点位置。
template<class T, class Ref, class Ptr>struct Reverse_ListIterator{typedef ListNode<T> Node;typedef Reverse_ListIterator<T, Ref, Ptr> Self;Node* _node;Reverse_ListIterator(Node* node):_node(node){}Self& operator++()//前置++,赋值为前面的结点的指针{_node = _node->_prev;return *this;}Self operator++(int)//后置++{Self tmp(_node);_node = _node->_prev;return tmp;}Self& operator--(){_node = _node->_next;return *this;}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}bool operator!=(const Self& it){return _node != it._node;}bool operator==(const Self& it){return _node == it._node;}};template<class T>
class list
{typedef ListNode<T> Node;public:typedef Reverse_ListIterator<T, T&, T*> reverse_iterator;typedef Reverse_ListIterator<T, const T&, const T*> reverse_const_iterator;reverse_const_iterator rbegin() const{return reverse_const_iterator(_head->_prev);} reverse_const_iterator rend() const{return reverse_const_iterator(_head);}reverse_iterator rbegin(){return reverse_iterator(_head->_prev);}reverse_iterator rend(){return reverse_iterator(_head);}//...
};
2.3 插入删除函数
2.3.1 insert和erase
- insert函数是在指定结点位置前插入一个新的结点。需要改变各个结点的指向问题,可以创建几个变量表示结点。
- 因为该函数传入的参数类型是迭代器,所以还要考虑迭代器失效的问题。解决方法是返回指向新结点位置的迭代器类型的变量。下面采用的是匿名对象。
iterator insert(iterator pos, const T& x)
{Node* cur = pos._node;Node* newnode = new Node(x);Node* prev = cur->_prev;//prev newnode curprev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;return iterator(newnode);
}
- erase函数是删除传入的迭代器变量指向的结点。也可以创建几个变量表示结点,改变各个结点的指向问题。
- 返回类型也是迭代器,返回的是被删除结点的下一个结点迭代器类型变量。
//erase后Pos失效,因为被释放了
iterator erase(iterator pos)
{assert(pos != end());Node* cur = pos._node;Node* prev = cur->_prev;Node* next = cur->_next;prev->_next = next;next->_prev = prev;delete cur;return iterator(next);
}
2.3.2 push_back pop_back push_front pop_front
完成了对insert和erase函数的实现,头部或者尾部插入和删除的函数都可以复用这两个函数。
- push_back函数是在尾部插入一个结点,insert是在指向结点前插入新结点,因为end函数是哨兵位结点,在它前面插入新结点就是尾插,所以传end函数作为迭代器即可。
- pop_back函数是删除最后一个结点,最后一个结点在哨兵位结点之前,即end函数所代表的迭代器,所以需要--。
- push_front和pop_front同理。
void push_back(const T& x)
{insert(end(), x);
}void pop_back()
{erase(--end());
}void push_front(const T& x)
{insert(begin(), x);
}void pop_front()
{erase(begin());
}
写一个测试函数
void Func(const list<int>& lt)
{list<int>::const_iterator it = lt.begin();while (it != lt.end()){cout << *it << " ";++it;}cout << endl;
}void test_list3()
{list<int> lt1;//按需实例化lt1.push_back(1);lt1.push_back(2);lt1.push_back(3);lt1.push_back(4);lt1.push_back(5);Func(lt1);lt1.push_front(10);lt1.push_front(20);lt1.push_front(30);Func(lt1);lt1.pop_back();lt1.pop_back();lt1.pop_back();Func(lt1);lt1.pop_front();lt1.pop_front();lt1.pop_front();Func(lt1);
}
运行结果如下:
2.4 构造函数,赋值拷贝函数,析构函数
构造函数这块,有四种类型,默认构造,initializer初始化器构造,迭代器区间构造和拷贝构造。
- 首先,封装一个创建哨兵位头结点的函数,因为后面的构造函数都需要用到。再写一个遍历打印整个链表的函数。
- 默认构造函数直接调用empty_init函数,初始化一个头结点。
void Printlist(const list<int> lt)
{for (const auto& e: lt1){cout << e << " "; }cout << endl;
}void empty_init()
{_head = new Node(T());_head->_next = _head;_head->_prev = _head;
}list()
{empty_init();
}
- initializer_list函数是针对花括号内部有相同类型的构造函数
list(initializer_list<T> il)
{empty_init();for (const auto& e : il){push_back(e);}}void test1()
{list<int> lt1 = { 1,2,3,4,5,6,7,8 };Printlist(lt1);
}
- 需要注意判断条件只能是!=,不能是<之类的符号。
template <class InputIterator>
list(InputIterator first, InputIterator last)
{empty_init();while (first != last){push_back(*first);++first;}
}void test2()
{list<int> lt1 = { 1,2,3,4,5,6,7,8 };Printlist(lt2);list<int> lt2(lt1.begin(), lt2.end());Printlist(lt2);
}
- 拷贝构造函数,可以使用范围for尾插元素实现。
//lt1(lt2)
list(const list<T>& lt)
{empty_init();for (const auto& e : lt){push_back(e);}
}void test3()
{list<int> lt1;lt1.push_back(1);lt1.push_back(2);lt1.push_back(3);lt1.push_back(4);Printlist(lt2);list<int> lt2(lt1);Printlist(lt2);
}
- 赋值拷贝函数可以先删除原链表的结点,然后再一个个尾插结点。
- 但是更好的方法就是用正常模版类型参数list接受被拷贝对象,此时的lt就是待拷贝对象的一份临时拷贝,然后使用交换函数交换头结点,因为lt是一份临时拷贝,函数结束后会自动调用析构函数销毁原链表的内容。
//lt1 = lt3
list<T>& operator=(list<T> lt)
{std::swap(_head, lt._head);return *this;
}void test3()
{list<int> lt1;lt1.push_back(1);lt1.push_back(2);lt1.push_back(3);lt1.push_back(4);Printlist(lt2);list<int> lt2;lt2 = lt1;Printlist(lt2);
}
- 析构函数需要先一个个释放结点,再释放头结点。
void clear()
{auto it = begin();while (it != end()){it = erase(it);++it;}
}~list()
{clear();delete _head;_head = nullptr;
}
总结
看完整篇文章,我相信你对list的使用和一些底层实现原理有进一步的了解,你也可以尝试自己手撕一个简单的list容器实现。树欲静而风不止,既然我们无法改变大势,但是我们可以改变自己!
创作不易,希望这篇文章能给你带来启发和帮助,如果喜欢这篇文章,请留下你的三连,你的支持的我最大的动力!!!
相关文章:
C++ list链表的使用和简单模拟实现
目录 前言 1. list的简介 2.list讲解和模拟实现 2.1 默认构造函数和push_back函数 2.2 迭代器实现 2.2.1 非const正向迭代器 2.2.2 const正向迭代器 2.2.3 反向迭代器 2.3 插入删除函数 2.3.1 insert和erase 2.3.2 push_back pop_back push_front pop_front 2.4 构…...
dependencies?devDependencies?peerDependencies
之前使用的npm包中,我用到了sass包。我当时没有在packagejson中添加依赖项,而是另外install的。这就引起了我的一个思考 初步想法: 我的npm包需要使用sass,那么我应该放在dependencies中,当使用的时候会直接下载 问题…...
在LUAT中使用MQTT客户端,游戏脚本,办公脚本自动操作
本文将介绍在LUAT中工程化使用MQTT客户端的方法及注意事项。实验平台为合宙AIR724UG,其固件版本为Luat_V4001_RDA8910_FLOAT_TMP。 面向对象 使用middleclass库为脚本提供基础面向对象支持,将此repo中的middleclass.lua文件添加到项目中即可使用。middl…...
如何解决maven中snapshot相关jar无法拉取问题
Maven中的SNAPSHOT版本是指正在开发中的版本,这些版本可能会频繁地更新。在使用Maven构建项目时,有时会遇到无法拉取SNAPSHOT相关jar的问题。以下是几种常见的解决方案: 1. 检查Maven配置文件(settings.xml) 确保你的M…...
类似crossover的容器软件有哪些 除了crossover还有什么 Mac虚拟机替代品
CrossOver是Mac用来运行exe文件的一款软件,但是并不是所有的exe文件CrossOver都支持运行。想要在Mac上运行exe文件的方法并不是只有使用CrossOver这一种,那么有没有类似的软件也可以实现exe文件在Mac上运行呢? CrossOver类似软件有哪些 1、Pl…...
以sqlilabs靶场为例,讲解SQL注入攻击原理【54-65关】
【Less-54】 与前面的题目不同是,这里只能提交10次,一旦提交超过十次,数据会重新刷新,所有的步骤需要重来一次。 解题步骤: 根据测试,使用的是单引号闭合。 # 判断字段的数量 ?id1 order by 3 -- aaa# …...
详解 Flink 的时间语义和 watermark
一、Flink 时间语义类型 Event Time:是事件创建的时间。它通常由事件中的时间戳描述,例如采集的日志数据中,每一条日志都会记录自己的生成时间,Flink 通过时间戳分配器访问事件时间戳Ingestion Time :是数据进入 Flink…...
Unreal Engine项目结构与关卡设置详解
引言 Unreal Engine 是一款功能强大的游戏引擎,为开发者提供了丰富的工具来创建和管理游戏项目。本文将详细介绍一个基本的 Unreal Engine 项目结构,并讲解如何在 Unreal 编辑器中进行关卡设置与操作。 Unreal Engine 项目结构 一个基本的 Unreal Eng…...
Access数据中的SQL偏移注入
使用场景: 目标数据表的字段较多,无法一一获取的时候,尝试使用偏移注入的方式实现SQL注入。 原理: 例如:一个表有6个字段,而你想获取的目标表admin的字段不知道,此时可以使用联合查询的方式获…...
Unity 编辑器扩展,获取目录下所有的预制件
先看演示效果 实现方案 1创建几个用于测试的cube 2,创建一个Editor脚本 3,编写脚本内容 附上源码 using UnityEditor; using UnityEngine;public class GetPrefeb : EditorWindow {private string folderPath "Assets/Resources"; // 指定预…...
【Python】解决Python报错:ValueError: not enough values to unpack (expected 2, got 1)
文章目录 引言1. 错误详解2. 常见的出错场景2.1 函数返回值解包2.2 遍历含有不同长度元组的列表 3. 解决方案3.1 检查和调整返回值3.2 安全的解包操作 4. 预防措施4.1 使用异常处理4.2 单元测试 结语 引言 在Python编程中,ValueError 是一个常见的异常类…...
政安晨【零基础玩转各类开源AI项目】解析开源:gradio:改进真实虚拟试穿的扩散模型
政安晨的个人主页:政安晨 欢迎 👍点赞✍评论⭐收藏 收录专栏: 零基础玩转各类开源AI项目 希望政安晨的博客能够对您有所裨益,如有不足之处,欢迎在评论区提出指正! Gradio 是一个开源 Python 软件包,可以让你…...
深入解读Prometheus Adapter:云原生监控的核心组件
一、引言 Prometheus Adapter的背景与重要性 在现代的云原生架构中,微服务和容器化技术得到了广泛的应用。这些技术带来了系统灵活性和扩展性的提升,但同时也增加了系统监控和管理的复杂度。Prometheus作为一款开源的监控系统,因其强大的指标…...
【计算机视觉】数字图像处理基础:以像素为单位的图像基本运算(点运算、代数运算、逻辑运算、几何运算、插值)
0、前言 在上篇文章中,我们对什么是数字图像、以及数字图像的组成(离散的像素点)进行了讲解🔗【计算机视觉】数字图像处理基础知识:模拟和数字图像、采样量化、像素的基本关系、灰度直方图、图像的分类。 我们知道&a…...
Spring Boot整合WebSocket和Redis实现直播间在线人数统计功能
😄 19年之后由于某些原因断更了三年,23年重新扬帆起航,推出更多优质博文,希望大家多多支持~ 🌷 古之立大事者,不惟有超世之才,亦必有坚忍不拔之志 🎐 个人CSND主页——Mi…...
uniapp自定义的下面导航
uniapp自定义的下面导航 看看效果图片吧 文章目录 uniapp自定义的下面导航 看看效果图片吧 ![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/6aa0e964741d4dd3a58f4e86c4bf3247.png) 前言一、写组件、我这里就没有写组件了直接写了一个页面?总结 前言 在…...
【Python】selenium使用find_element时解决【StaleElementReferenceException】问题的方法
StaleElementReferenceException 是 Selenium WebDriver 中的一种异常,通常在元素与当前页面的状态不同步时抛出,比如页面已经刷新或导航到另一个页面,但是尝试操作的元素引用仍然是旧页面上的元素。 以下是一些解决 StaleElementReferenceE…...
Apache IoTDB 分布式架构三部曲(三)副本与共识算法
IoTDB 首创并应用的共识协议统一框架,为用户提供了灵活选择不同共识算法的可能性。 对于一个分布式集群而言,为了使得海量数据场景下集群能够横向扩展,集群需要按照一定的规则将全部数据分成多个子集存储在不同的节点上,从而能够更…...
数据挖掘--聚类分析:基本概念和方法
数据挖掘--引论 数据挖掘--认识数据 数据挖掘--数据预处理 数据挖掘--数据仓库与联机分析处理 数据挖掘--挖掘频繁模式、关联和相关性:基本概念和方法 数据挖掘--分类 数据挖掘--聚类分析:基本概念和方法 聚类分析 聚类分析是把一个数据对象&…...
APP单页分发源码下载安卓苹果自动识别apk描述文件免签自动安装
下载地址:APP单页分发源码下载安卓苹果自动识别apk描述文件免签自动安装...
golang定时器使用示例
1.定时器创建与停止 //定时器使用t1 : time.NewTimer(2 * time.Second)<-t1.Cfmt.Println("timer1 fired")t2 : time.NewTimer(5 * time.Second)go func() {fmt.Println("go协程处理中,等待5秒后输出...")<-t2.Cfmt.Println("timer2 fired&quo…...
[FSCTF 2023]Tea_apk
得到密文和密钥 import base64 from ctypes import c_uint32import libnumDELTA 0x9E3779B9def decrypt(v, n, k):rounds 6 int(52 / n)sum c_uint32(rounds * DELTA)y v[0].valuewhile rounds > 0:e (sum.value >> 2) & 3p n - 1while p > 0:z v[p …...
分享一个用python写的本地WIFI密码查看器
本章教程,主要分享一个本地wifi密码查看器,用python实现的,感兴趣的可以试一试。 具体代码 import subprocess # 导入 subprocess 模块,用于执行系统命令 import tkinter as tk # 导入 tkinter 模块,用于创建图形用…...
【SkyWalking】启用apm-trace-ignore-plugin追踪忽略插件
背景 使用Agent采集追踪数据的时候,想排除某些路径,比如健康检查等,这样可以减少上报的数据,也可以去除一些不必要的干扰数据。 加载插件 在agent/optional-plugins目录中有个apm-trace-ignore-plugin-${version}.jar插件&…...
独立游戏之路 -- 获取OAID提升广告收益
Unity 之 获取手机:OAID、IMEI、ClientId、GUID 前言一、Oaid 介绍1.1 Oaid 说明1.2 移动安全联盟(MSA) 二、站在巨人的肩膀上2.1 本文实现参考2.2 本文实现效果2.3 本文相关插件 三、Unity 中获取Oaid3.1 查看实现源码3.2 工程配置3.3 代码实现3.4 场景搭建 四、总…...
反转链表 (oj题)
一、题目链接 https://leetcode.cn/problems/reverse-linked-list/submissions/538124207 二、题目思路 1.定义三个指针,p1先指向NULL p2指向头结点 p3指向第二个结点 2.p2的next指向p1。然后移动指针,p1来到p2的位置,p2来到p3的位置&…...
Mysql使用中的性能优化——批量插入的规模对比
在《Mysql使用中的性能优化——单次插入和批量插入的性能差异》中,我们观察到单次批量插入的数量和耗时呈指数型关系。 这个说明,不是单次批量插入的数量越多越好。本文我们将通过实验测试出本测试案例中最佳的单次批量插入数量。 结论 本案例中约每次…...
TCP为什么握手是三次,而挥手是四次
TCP(传输控制协议)使用三次握手(3WHS)来建立一个可靠的连接,并使用四次挥手(4WHS)来终止连接。以下是每个步骤的详细解释: 三次握手(3WHS)建立连接ÿ…...
前端面试题大合集9----TypeScript
目录 一、TypeScript 中静态类型的概念及其好处 二、如何在 TypeScript 的接口中定义可选属性? 三、解释 TypeScript 中联合类型的概念并提供示例 四、TypeScript 中的类型断言是什么? 五、TS中泛型是什么? 六、解释 TypeScript 中的“…...
Linux:动态库和静态库的编译与使用
目录 1.前言 2.静态链接库 3.静态链接库生成步骤 4.静态链接库的使用 5.动态链接库 6.动态链接库生成步骤 7.动态链接库的使用 8.动态链接库无法加载 9.解决动态链接库无法加载问题 前言 在《MinGW:从入门到链接库》博客中简单介绍了如何编译动态链接库和静态链接库…...
大连的网站制作公司/百度一下就知道百度首页
Apache的mod_rewrite是提供了强大URL操作的杀手级的模块,可以实现几乎所有你梦想的URL操作类型,其代价是你必须接受其复杂性,因为mod_rewrite的主要障碍就是初学者不容易理解和运用,即使是Apache专家有时也会发掘出mod_rewrite的新…...
网站建设及推广衬胶蝶阀/优化软件刷排名seo
2020博客地址汇总2019年博客汇总 一、安装docker docker 一般安装在linux7以上,内核3.1以上。 查看内核 uname -a安装文件:docker-18.06.3-ce.tgz 下载地址 tgz https://download.docker.com/linux/static/stable/x86_64/rpm https://download.do…...
一个网站如何优化/seo专员工作容易学吗
一、题目 二、思路 审题nums[i]都在int范围内(32位二进制),对于每个num[i]的二进制数,对于第j个位置的元素都相加,并且最后对结果的二进制数,其第j个位置的元素依次进行余3操作。关键:对于数组…...
seo推广优化公司哪家好/seo 工具分析
平时我们有时会发现dedecms列表页文章按权重排序无效问题,找到list解析文件include/arc.listview.class.ph,发现排序规则里面并没有按照weight排序的判断,于是乎修改程序加入排序规则,大概在771行,加入下面红色代码 //…...
南阳网站排名价格/最近的电脑培训学校
1. Apache是web服务器,Tomcat是应用(java)服务器,它只是一个servlet容器,是Apache的扩展。2. Apache和Tomcat都可以做为独立的web服务器来运行,但是Apache不能解释java程序(jsp,serverlet&#…...
wordpress插件分享显示/中国新闻
首先声明本文旨在介绍centos环境下安装Docker-CE(社区版),社区版是免费提供给个人开发者和小团队,Docker-EE (企业版)有额外费用,想了解其他系统下搭建,请传送《docker官网》 准备工作 1、docker要求Linux内…...