当前位置: 首页 > news >正文

list模拟与实现(附源码)

在这里插入图片描述

文章目录

  • 声明
  • list的简单介绍
  • list的简单使用
  • list中sort效率测试
  • list的简单模拟
    • 封装迭代器
    • insert模拟
    • erase模拟
    • 头插、尾插、头删、尾删模拟
    • 自定义类型迭代器遍历
    • const迭代器
    • clear和析构函数
    • 拷贝构造(传统写法)
    • 拷贝构造(现代写法)
  • 源码

声明

本文源代码已上传至我的gitee仓库,欢迎查看:list模拟实现源代码

list的简单介绍

在学习STL时,一定要先阅读C++文档
list使用文档

  1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代
  2. list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。
  3. list与forward_list非常相似:最主要的不同在于forward_list是单链表,只能朝前迭代,已让其更简单高效。
  4. 与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好。
  5. 与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间开销;list还需要一些额外的空间,以保存每个节点的相关联信息(对于存储类型较小元素的大list来说这可能是一个重要的因素)

list的简单使用

这里罗列列表的基本操作,非常简单,相信大家在学习完string、vector后学习list的功能非常简单

# define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<list>using namespace std;int main()
{list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);lt.push_back(5);//迭代器list<int>::iterator it = lt.begin();while (it != lt.end()){cout << *it << " ";it++;}cout << endl;//范围forfor (auto e : lt){cout << e << " ";}cout << endl;//逆置lt.reverse();for (auto e : lt){cout << e << " ";}cout << endl;//排序lt.sort();for (auto e : lt){cout << e << " ";}cout << endl;//节点转移list<int> lt1;lt1.push_back(10);lt1.push_back(20);lt1.push_back(30);lt1.push_back(40);lt.splice(lt.begin(), lt1);for (auto e : lt){cout << e << " ";}cout << endl;for (auto e : lt1){cout << e << " ";}cout << endl;return 0;
}

运行结果:

1 2 3 4 5
1 2 3 4 5
5 4 3 2 1
1 2 3 4 5
10 20 30 40 1 2 3 4 5

list中sort效率测试

表示双向循环链表以及数据初始化:

void test_op()
{srand((unsigned int)time(NULL));const int N = 1000000;vector<int> v;v.reserve(N);list<int> lt1;list<int> lt2;for (int i = 0; i < N; ++i){int e = rand();lt1.push_back(e);lt2.push_back(e);}// 拷贝到vector排序,排完以后再拷贝回来int begin1 = clock();for (auto e : lt1){v.push_back(e);}sort(v.begin(), v.end());size_t i = 0;for (auto& e : lt1){e = v[i++];}int end1 = clock();//list调用自己的sortint begin2 = clock();lt2.sort();int end2 = clock();printf("vector sort:%d\n", end1 - begin1);printf("list sort:%d\n", end2 - begin2);
}

测试结果发现,list的sort排序效率很低,在实际应用中使用的很少

list的简单模拟

template<class T>struct ListNode{ListNode<T>* _next;ListNode<T>* _prev;T _data;ListNode(const T& x = T()):_next(nullptr), _prev(nullptr), _data(x){}};

模板类ListNode表示双向链表中的节点。该节点包括三个成员:_next(指向下一个节点的指针)、_prev(指向上一个节点的指针)和 _data(存储节点的数据)。
定义了一个构造函数,用于初始化节点的数据成员,如果不提供具体的数据,则使用默认值进行初始化。


实现简单的双向循环链表:

template<class T>class list{typedef ListNode<T> Node;public:list(){_head = new Node;_head->_next = _head;_head->_prev = _head;}void push_back(const T& x){Node* newnode = new Node(x);Node* tail = _head->_prev;tail->_next = newnode;newnode->_next = _head;_head->_prev = newnode;}private:Node* _head;};

定义了一个模板类 list,实现了简单的双向循环链表。在 list 中,使用了之前定义的 ListNode 作为节点,通过模板类的方式实现了对不同类型元素的支持。

构造函数中,创建了一个头节点,并将头节点的 _next 和 _prev 都指向自身,构成一个空的循环链表。

list(): 这是类的构造函数,用于初始化链表。在构造函数中,首先创建了一个头节点 _head,然后将头节点的 _next_prev 都指向自身,从而形成一个空的循环链表。

void push_back(const T& x): 这是一个成员函数,用于在链表尾部插入新的元素。在函数中,首先创建了一个新的节点 newnode 并存储数据 x,然后找到当前尾节点 tail,将尾节点的 _next 指向新节点,新节点的 _next 指向头节点,头节点的 _prev 指向新节点,从而完成了新元素的插入。


封装迭代器

封装迭代器:

  • 定义了一个名为ListIterator的模板类
template<class T>
class ListIterator
{typedef ListNode<T> Node;typedef ListIterator<T> Self;Node* _node;

typedef ListIterator<T> Self;:定义了一个别名Self,它代表了当前类的类型,这样就可以在类内部使用Self来引用当前类的对象。
Node* _node;:声明了一个指针成员变量_node,它用于指向链表中的节点。

  • 成员函数的定义:
public:ListIterator(Node* node):_node(node){}

构造函数ListIterator(Node* node):接受一个指向链表节点的指针作为参数,将其赋值给成员变量_node。

    T& operator*(){return _node->_data;}

解引用操作符operator*():返回当前迭代器指向的节点的数据成员的引用。

Self& operator++()
{_node = _node->_next;return *this;
}

前置递增操作符operator++():将迭代器指向下一个节点,并返回递增后的迭代器自身的引用。

    Self operator++(int){Self tmp(*this);_node = _node->_next;return tmp;}

后置递增操作符operator++(int):创建当前迭代器的副本tmp,然后将迭代器指向下一个节点,并返回tmp。

    Self& operator--(){_node = _node->_prev;return *this;}Self operator--(int){Self tmp(*this);_node = _node->_prev;return tmp;}

置和后置递减操作符的定义与递增操作符类似,只不过是将迭代器指向前一个节点。

    bool operator!=(const Self& it){return _node != it._node;}
};

不等于操作符operator!=:比较两个迭代器的_node成员,如果它们不相等,则返回true;否则返回false

源代码:

template<class T>struct ListConstIterator{typedef ListNode<T> Node;typedef ListConstIterator<T> Self;Node* _node;ListConstIterator(Node* node):_node(node){}// *itconst T& operator*(){return _node->_data;}// it->const T* operator->(){return &_node->_data;}// ++itSelf& operator++(){_node = _node->_next;return *this;}Self operator++(int){Self tmp(*this);_node = _node->_next;return tmp;}Self& operator--(){_node = _node->_prev;return *this;}Self operator--(int){Self tmp(*this);_node = _node->_prev;return tmp;}bool operator!=(const Self& it){return _node != it._node;}bool operator==(const Self& it){return _node == it._node;}};

关于链表开始和结束的定义:

typedef ListConstIterator<T> iterator;iterator begin(){return iterator(_head->_next);}iterator end(){return iterator(_head);}

begin()函数返回一个迭代器,它指向链表中的第一个元素(也就是头节点的下一个节点)。
end()函数返回一个迭代器,它指向链表中最后一个元素的下一个位置(也就是头节点本身)。


insert模拟

模拟实现insert:

在这里插入图片描述

		void insert(iterator pos, const T& val){Node* cur = pos._node;Node* newnode = new Node(val); Node* prev = cur->_prev;//prev newnode curprev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;}

erase模拟

模拟实现erase:

		iterator earse(iterator pos){Node* cur = pos._node;Node* prev = cur->_prev;Node* next = cur->_next;prev->_next = next;next->_prev = prev;delete cur;return iterator(next);}

注意:最后,返回一个指向下一个节点的迭代器,以便在调用方继续操作链表。


头插、尾插、头删、尾删模拟

在刚刚插入和删除的基础上,模拟实现头插、尾插、头删、尾删:

void push_back(const T& x){insert(end(), x);}void push_front(const T& x){insert(begin(), x);}void pop_back(){erase(--end());}void pop_front(){erase(begin());}

自定义类型迭代器遍历

自定义一个结构体A,然后进行插入删除操作:

struct A{int _a1;int _a2;A(int a1=0,int a2=0):_a1(a1),_a2(a2){}};void test_list2(){list<A> lt;A aa1(1, 1);A aa2 = { 2,2 };lt.push_back(aa1);lt.push_back(aa1);lt.push_back(A(2, 2));lt.push_back({ 3,3 });lt.push_back({ 4,4 });list<A>::iterator it = lt.begin();while (it != lt.end()){cout << *it << " ";++it;}cout << endl;}

运行一下,报错了!!!

在这里插入图片描述

解决方法有两种:

  • 第一种:
    在循环内部,通过cout << (*it)._a1 << ":" << (*it)._a2 << endl;语句打印当前迭代器it指向的A类型对象的_a1_a2成员变量的值,中间用冒号分隔,并在末尾换行。这里使用了解引用操作符(*)来获取迭代器指向的对象,然后通过点操作符(.)访问对象的成员变量_a1_a2
    (*it)._a1: (*it)是迭代器it指向的元素,即链表中的一个A类型对象,.是成员访问运算符,_a1是这个A类型对象的成员变量_a1的值。所以(*it)._a1表示获取当前迭代器指向的对象的_a1成员变量的值。
	void test_list2(){list<A> lt;A aa1(1, 1);A aa2 = { 2,2 };lt.push_back(aa1);lt.push_back(aa1);lt.push_back(A(2, 2));lt.push_back({ 3,3 });lt.push_back({ 4,4 });list<A>::iterator it = lt.begin();while (it != lt.end()){cout << (*it)._a1 << ":" << (*it)._a2 << endl;++it;}cout << endl;}

在这里插入图片描述


  • 第二种:
    定义了一个operator->()重载函数,箭头运算符用于访问对象的成员,而对于指向对象的指针,使用箭头运算符来访问成员会更方便
    const T*: 这是函数的返回类型,表示返回一个指向类型为T的常量数据的指针。也就是说,该函数返回的是一个指向T类型常量数据的指针。
    operator->(): 这是重载的箭头运算符函数名。当我们通过指向某个对象的指针使用箭头运算符时,就会调用此函数来执行操作。
    { return &_node->_data; }: 函数体内部,返回了一个指向_node->_data的指针。在这里,_node是一个指向节点的指针,_data是节点中存储的数据。通过返回&_node->_data,实际上是返回了指向节点数据的指针。
// it->
const T* operator->()
{return &_node->_data;
}

遍历:

void test_list2(){list<A> lt;A aa1(1, 1);A aa2 = { 2,2 };lt.push_back(aa1);lt.push_back(aa1);lt.push_back(A(2, 2));lt.push_back({ 3,3 });lt.push_back({ 4,4 });list<A>::iterator it = lt.begin();while (it != lt.end()){cout << it->_a1 << ":" << it->_a2 << endl;++it;}cout << endl;}

it->_a1it->_a2就是使用迭代器it来访问链表中当前元素的成员变量_a1_a2的值。这里的it->_a1it->_a2相当于(*it)._a1(*it)._a2
在这里插入图片描述

const迭代器

cont迭代器是迭代器指向的内容不能修改

注意:

  • const_iterator是迭代器指向的内容不能修改,迭代器指向的元素不可修改,模拟实现的是const T* p2
  • const iterator是迭代器本身不能修改,这个const修饰的是iteratoriterator是自定义类型,前面加了const那就是不能修改这个自定义类型,模拟实现的是T* const p1
const_iterator begin() const{return iterator(_head->_next);}const_iterator end() const{return iterator(_head);}
void PrintList(const list<int>& clt){list<int>::const_iterator it = clt.begin();while (it != clt.end()){cout << *it << " ";++it;}cout << endl;}void test_list3(){list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);lt.push_back(5);PrintList(lt);}

使用模板封装一个迭代器:

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){}// *it//T& operator*()Ref operator*(){return _node->_data;}// it->//T* operator->()Ptr operator->(){return &_node->_data;}// ++itSelf& operator++(){_node = _node->_next;return *this;}Self operator++(int){Self tmp(*this);_node = _node->_next;return tmp;}Self& operator--(){_node = _node->_prev;return *this;}Self operator--(int){Self tmp(*this);_node = _node->_prev;return tmp;}bool operator!=(const Self& it){return _node != it._node;}bool operator==(const Self& it){return _node == it._node;}};

template<class T, class Ref, class Ptr>:模板声明,用来定义模板类 ListIterator,它有三个模板参数 TRef Ptr

在这个模板类中,class Tclass Refclass Ptr 是模板参数,它们的作用如下:

  1. T:表示节点中存储的数据类型。通过模板参数 T,可以让 ListIterator 类型适用于不同类型的链表,例如整数、字符串、自定义对象等。
  2. Ref:表示引用类型。在 C++ 中,引用类型通常用来表示对某个对象的引用,通过模板参数 Ref,可以指定迭代器返回的数据的引用类型,例如 T&(对 T 类型的对象的引用)。
  3. Ptr:表示指针类型。通过模板参数 Ptr,可以指定迭代器返回数据的指针类型,例如 T*(指向 T 类型的指针)。
    在这里插入图片描述

clear和析构函数

void clear(){iterator it = begin();while (it != end()){it = erase(it);}}~list(){clear();delete _head;_head = nullptr;}
  • clear() 方法:
    首先创建一个迭代器 it 并初始化为链表的头部(即第一个节点)。
    然后通过循环遍历链表中的每个节点,调用 erase() 方法来删除当前节点,并将返回的下一个节点的迭代器赋值给 it。
    循环直到 it == end(),即遍历完整个链表。

  • 析构函数 ~list():
    在析构函数中首先调用 clear() 方法,清空链表中的所有节点。
    然后释放链表的头节点 _head 所占用的内存,避免内存泄漏。
    最后将 _head 指针设置为 nullptr,确保不再指向已释放的内存。

通过在析构函数中调用 clear() 方法,可以确保在销毁链表对象时,先清空链表中的所有节点,然后再释放头节点的内存。这样做有助于避免内存泄漏,并正确地释放链表所占用的资源。

拷贝构造(传统写法)

void empty_init(){_head = new Node;_head->_next = _head;_head->_prev = _head;_size = 0;}list(){empty_init();}// lt2(lt1)list(const list<T>& lt){empty_init();for (auto& e : lt){push_back(e);}}
  • empty_init() :
    初始化空的链表。它创建了一个新的节点作为头结点,并将头结点的指针指向自己,形成一个循环链表。同时,将链表的大小 _size 初始化为 0。

  • 默认构造函数 list():
    在这个构造函数中,它调用了 empty_init() 方法来初始化一个空的链表。

  • 拷贝构造函数 list(const list& lt):
    这个构造函数通过调用 empty_init() 方法来初始化一个空的链表(即新建一个头结点)。然后通过遍历传入的链表 lt,将其中的元素逐个添加到新建的链表中,使用 push_back(e) 方法将元素添加到新链表的末尾。

在这里插入图片描述

拷贝构造(现代写法)

void swap(list<T>& lt)
{std::swap(_head, lt._head);std::swap(_size, lt._size);
}// lt1 = lt3
list<T>& operator=(list<T> lt)
{swap(lt);return *this;
}
  • swap:
    交换两个链表对象的内容。它通过调用 std::swap 函数交换当前链表对象的头结点 _head 和大小 _size 与传入的链表对象 lt 的对应成员的值。这样可以在不需要额外内存分配的情况下快速交换两个链表的内容。

  • 赋值运算符重载函数 operator=
    这个赋值运算符重载函数接受一个传值参数 lt,在函数内部会对传入的链表 lt 调用 swap 方法,将传入链表的内容与当前链表对象进行交换。
    通过传值参数的方式,会触发拷贝构造函数,从而创建传入链表 lt 的一个副本。然后,调用 swap(lt) 来交换当前链表对象和副本链表对象的内容,最终实现将传入链表 lt 中的内容赋值给当前链表对象。
    最后,返回 *this,即当前链表对象的引用,以支持链式赋值操作。

源码

#pragma once
#include<assert.h>
namespace gwj
{template<class T>struct ListNode{ListNode<T>* _next;ListNode<T>* _prev;T _data;ListNode(const T& x = T()):_next(nullptr), _prev(nullptr), _data(x){}};//typedef ListIterator<T, T&, T*> iterator;//typedef ListIterator<T, const T&, const T*> 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){}// *it//T& operator*()Ref operator*(){return _node->_data;}// it->//T* operator->()Ptr operator->(){return &_node->_data;}// ++itSelf& operator++(){_node = _node->_next;return *this;}Self operator++(int){Self tmp(*this);_node = _node->_next;return tmp;}Self& operator--(){_node = _node->_prev;return *this;}Self operator--(int){Self tmp(*this);_node = _node->_prev;return tmp;}bool operator!=(const Self& it){return _node != it._node;}bool operator==(const Self& it){return _node == it._node;}};//template<class T>//struct ListConstIterator//{//	typedef ListNode<T> Node;//	typedef ListConstIterator<T> Self;//	Node* _node;//	ListConstIterator(Node* node)//		:_node(node)//	{}//	// *it//	const T& operator*()//	{//		return _node->_data;//	}//	// it->//	const T* operator->()//	{//		return &_node->_data;//	}//	// ++it//	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;//	}//	Self operator--(int)//	{//		Self tmp(*this);//		_node = _node->_prev;//		return tmp;//	}//	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 ListConstIterator<T> const_iterator;typedef ListIterator<T, T&, T*> iterator;typedef ListIterator<T, const T&, const T*> const_iterator;//iterator begin()//{//	//return iterator(_head->_next);//	iterator it(_head->_next);//	return it;//}iterator begin(){return _head->_next;}iterator end(){return _head;}// const迭代器,需要是迭代器不能修改,还是迭代器指向的内容?// 迭代器指向的内容不能修改!const iterator不是我们需要const迭代器// T* const p1// const T* p2const_iterator begin() const{return _head->_next;}const_iterator end() const{return _head;}void empty_init(){_head = new Node;_head->_next = _head;_head->_prev = _head;_size = 0;}list(){empty_init();}// lt2(lt1)list(const list<T>& lt){empty_init();for (auto& e : lt){push_back(e);}}void swap(list<T>& lt){std::swap(_head, lt._head);std::swap(_size, lt._size);}// lt1 = lt3list<T>& operator=(list<T> lt){swap(lt);return *this;}// 需要析构,一般就需要自己写深拷贝// 不需要析构,一般就不需要自己写深拷贝,默认浅拷贝就可以void clear(){iterator it = begin();while (it != end()){it = erase(it);}}~list(){clear();delete _head;_head = nullptr;}//void push_back(const T& x)//{//	Node* newnode = new Node(x);//	Node* tail = _head->_prev;//	tail->_next = newnode;//	newnode->_next = _head;//	_head->_prev = newnode;//}void push_back(const T& x){insert(end(), x);}void push_front(const T& x){insert(begin(), x);}void pop_back(){erase(--end());}void pop_front(){erase(begin());}void insert(iterator pos, const T& val){Node* cur = pos._node;Node* newnode = new Node(val); Node* prev = cur->_prev;//prev newnode curprev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;_size++;}iterator erase(iterator pos){Node* cur = pos._node;Node* prev = cur->_prev;Node* next = cur->_next;prev->_next = next;next->_prev = prev;delete cur;_size--;return iterator(next);}bool empty(){return _size == 0;}private:Node* _head;size_t _size;};void test_list1(){list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);lt.push_back(5);list<int>::iterator it = lt.begin();while (it != lt.end()){cout << *it << " ";++it;}cout << endl;lt.push_front(10);lt.push_front(20);lt.push_front(30);for (auto e : lt){cout << e << " ";}cout << endl;lt.pop_back();lt.pop_front();for (auto e : lt){cout << e << " ";}cout << endl;}struct A{int _a1;int _a2;A(int a1=0,int a2=0):_a1(a1),_a2(a2){}};void test_list2(){list<A> lt;A aa1(1, 1);A aa2 = { 2,2 };lt.push_back(aa1);lt.push_back(aa1);lt.push_back(A(2, 2));lt.push_back({ 3,3 });lt.push_back({ 4,4 });list<A>::iterator it = lt.begin();while (it != lt.end()){//cout << (*it)._a1 << ":" << (*it)._a2 << endl;cout << it->_a1 << ":" << it->_a2 << endl;++it;}cout << endl;}void PrintList(const list<int>& clt){list<int>::const_iterator it = clt.begin();while (it != clt.end()){cout << *it << " ";++it;}cout << endl;}void test_list3(){list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);lt.push_back(5);PrintList(lt);list<int> lt1(lt);PrintList(lt1);}
}

在这里插入图片描述

相关文章:

list模拟与实现(附源码)

文章目录 声明list的简单介绍list的简单使用list中sort效率测试list的简单模拟封装迭代器insert模拟erase模拟头插、尾插、头删、尾删模拟自定义类型迭代器遍历const迭代器clear和析构函数拷贝构造&#xff08;传统写法&#xff09;拷贝构造&#xff08;现代写法&#xff09; 源…...

Java应用中文件上传安全性分析与安全实践

✨✨谢谢大家捧场&#xff0c;祝屏幕前的小伙伴们每天都有好运相伴左右&#xff0c;一定要天天开心哦&#xff01;✨✨ &#x1f388;&#x1f388;作者主页&#xff1a; 喔的嘛呀&#x1f388;&#x1f388; 目录 引言 一. 文件上传的风险 二. 使用合适的框架和库 1. Spr…...

noVNC 小记

1. 怎么查看Ubuntu版本...

设置systemctl start kibana启动kibana

1、编辑kibana.service vi /etc/systemd/system/kibana.service [Unit] DescriptionKibana Server Manager [Service] Typesimple Useres ExecStart/home/es/kibana-7.10.2-linux-x86_64/bin/kibana PrivateTmptrue [Install] WantedBymulti-user.target 2、启动kibana # 刷…...

PostgreSQL:在CASE WHEN语句中使用SELECT语句

CASE WHEN语句是一种条件语句&#xff0c;用于多条件查询&#xff0c;相当于java的if/else。它允许我们根据不同的条件执行不同的操作。你甚至能在条件里面写子查询。而在一些情况下&#xff0c;我们可能需要在CASE WHEN语句中使用SELECT语句来检索数据或计算结果。下面是一些示…...

游戏心理学Day13

游戏成瘾 成瘾的概念来自于药物依赖&#xff0c;表现为为了感受药物带来的精神效应&#xff0c;或是为了避免由于断药所引起的不适和强迫性&#xff0c;连续定期使用该药的 行为现在成瘾除了药物成瘾外&#xff0c;还包括行为成瘾。成瘾的核心特征是不知道成瘾的概念来自于药…...

GitLab中用户权限

0 Preface/Foreword 1 权限介绍 包含5种权限&#xff1a; Guest&#xff08;访客&#xff09;&#xff1a;可以创建issue、发表comment&#xff0c;不能读写版本库Reporter&#xff08;报告者&#xff09;&#xff1a;可以克隆代码&#xff0c;不能提交。适合QA/PMDeveloper&…...

RunMe_About PreparationForDellBiosWUTTest

:: ***************************************************************************************************************************************************************** :: 20240613 :: 该脚本可以用作BIOS WU测试前的准备工作,包括&#xff1a;自动检测"C:\DellB…...

C++中变量的使用细节和命名方案

C中变量的使用细节和命名方案 C提倡使用有一定含义的变量名。如果变量表示差旅费&#xff0c;应将其命名为cost_of_trip或 costOfTrip,而不要将其命名为x或cot。必须遵循几种简单的 C命名规则。 在名称中只能使用字母字符、数字和下划线()。 名称的第一个字符不能是数字。 区分…...

[ACTF新生赛2020]SoulLike

两个文件 ubuntu运行 IDA打开 清晰的逻辑 很明显,我们要sub83a 返回ture 这里第一个知识点来了 你点开汇编会发现 这里一堆xor巨多 然后IDA初始化设置的函数,根本不能分析这么多 我们要去改IDA的设置 cfg 里面的 hexrays文件 在max_funsize这 修改为1024,默认是64 等待一…...

C#——析构函数详情

析构函数 C# 中的析构函数&#xff08;也被称作“终结器”&#xff09;同样是类中的一个特殊成员函数&#xff0c;主要用于在垃圾回收器回收类实例时执行一些必要的清理操作。 析构函数: 当一个对象被释放的时候执行 C# 中的析构函数具有以下特点&#xff1a; * 析构函数只…...

探索重要的无监督学习方法:K-means 聚类模型

在数据科学和机器学习领域,聚类分析是一种重要的无监督学习方法,用于将数据集中的对象分成多个组(簇),使得同一簇中的对象相似度较高,而不同簇中的对象相似度较低。K-means 聚类是最广泛使用的聚类算法之一,它以其简单、快速和易于理解的特点受到了广泛关注。本文将深入…...

将web项目打包成electron桌面端教程(二)vue3+vite+ts

说明&#xff1a;我用的demo项目是vue3vitets&#xff0c;如果是vue2/cli就不用往下看啦&#xff0c;建议找找其他教程哦~下依赖npm下载不下来的&#xff0c;基本换成cnpm/pnpm/yarn就可以了 一、项目准备 1、自己新创建一个&#xff0c;这里就不过多赘述了 2、将需要打包成…...

Linux下的/etc/resolv.conf

Linux下的/etc/resolv.conf 文件用于配置域名解析器的设置&#xff0c;告诉系统在解析域名时要查询哪些DNS服务器。nameserver&#xff1a;指定DNS服务器的IP地址。你可以列出多个nameserver&#xff0c;系统将按顺序尝试它们&#xff0c;直到找到可用的DNS服务器。 nameserve…...

大语言模型 (LLM) 红队测试:提前解决模型漏洞

大型语言模型 (LLM) 的兴起具有变革性&#xff0c;以其在自然语言处理和生成方面具有与人类相似的卓越能力&#xff0c;展现出巨大的潜力。然而&#xff0c;LLM 也被发现存在偏见、提供错误信息或幻觉、生成有害内容&#xff0c;甚至进行欺骗行为的情况。一些备受关注的事件包括…...

cocos入门11:生命周期

Cocos Creator 是一个强大的游戏开发工具&#xff0c;它基于 JavaScript 或 TypeScript&#xff0c;并使用 cc.Class 系统来组织游戏逻辑。在 Cocos Creator 中&#xff0c;每个组件&#xff08;包括场景、节点和组件脚本&#xff09;都有其生命周期&#xff0c;这些生命周期函…...

c++分辨读取的文件编码格式是utf-8还是GB2312

直接上代码&#xff0c;有一部分是GPT直接生成的&#xff1a; #include <QCoreApplication> #include <QFile> #include <QTextCodec> #include <QDebug>// 判断是否为UTF-8编码 bool isUtf8(const QByteArray &data) {int i 0;while (i < da…...

MS721仪表总线(M-Bus)从站收发电路

MS721 是为 M-Bus 标准 (EN1434-3) 的应用而开发的单片收发 电路。 MS721 接口电路可以适应从站与主站之间的电压差&#xff0c;总 线的连接没有极性要求&#xff0c;电路由主站通过总线供电&#xff0c;这样从站 电池就不会增加额外的负载&#xff0c;同时还集成电源失效功…...

用Python代码锁定Excel单元格以及行和列

Excel能够帮助用户高效地组织数据&#xff0c;还支持复杂的公式计算和数据分析。而随着团队协作的日益频繁&#xff0c;保护数据的准确性和完整性变得尤为重要。在Excel表格中&#xff0c;我们可以通过锁定特定的单元格或区域&#xff0c;防止对单元格内容进行随意修改&#xf…...

在Lua解释器中注册自定义函数库

本文目录 1、引言2、注册原理3、实例4、程序验证 文章对应视频教程&#xff1a; 暂无&#xff0c;可以关注我的B站账号等待更新。 点击图片或链接访问我的B站主页~~~ 1、引言 在现代软件开发中&#xff0c;Lua因其轻量级、高效和可嵌入性而被广泛使用。作为一种灵活的脚本语言…...

UKP3D用户定制图框的思路

为用户定制图框&#xff0c;记录以下图框制作方法&#xff0c;便于用户自已修改。 1.轴测图与平面图的图框&#xff1a; 1.1.图框在安装目录下&#xff0c;例如&#xff1a;E:\Program Files (x86)\UKSoft\UKP3d9.2\config\TemplateAndBlock\CADTemplate\ 1.2.配置文件在安装…...

事务并发问题 与 事务隔离级别

来源&#xff1a;微软sql文档 https://learn.microsoft.com/en-us/sql/odbc/reference/develop-app/transaction-isolation-levels?viewsql-server-ver16 事务隔离级别&#xff0c;是一种衡量事务隔离程度的指标。 事务隔离级别的定义&#xff0c;取决于能不能解决以下几个问…...

云原生Kubernetes系列项目实战-k8s集群+高可用负载均衡层+防火墙

一、Kubernetes 区域可采用 Kubeadm 方式进行安装&#xff1a; 名称主机部署服务master192.168.91.10docker、kubeadm、kubelet、kubectl、flannelnode01192.168.91.11docker、kubeadm、kubelet、kubectl、flannelnode02192.168.91.20docker、kubeadm、kubelet、kubectl、flan…...

MFC为什么说文档在数据的保存和给用户提供数据之间划分了清晰的界限?

MFC MFC&#xff08;Microsoft Foundation Classes&#xff09;是微软为Windows应用程序开发提供的一套C类库&#xff0c;它在设计上强调了"文档-视图"&#xff08;Document-View&#xff09;架构。这种架构将文档&#xff08;Document&#xff09;与用户界面&#…...

SAS:PROC SQL和ANSI标准

文章来源于SAS HELP PROC SQL 和ANSI SQL 的区别——图表和视图名称的作用域规则不同 例1&#xff1a;匹配数据集相关名称 当PROC SQL匹配数据集相关名称时&#xff0c;会依次进行3个步骤&#xff1a;1、有别名&#xff0c;用别名匹配&#xff1b;2、1匹配失败&#xff0c;在无…...

使用mysql_config_editor可以为特定的MySQL服务器或客户端程序设置登录路径

login_path 介绍 在 MySQL 中&#xff0c;login_path 通常不是 MySQL 服务器配置或 SQL 语句的一部分。但是&#xff0c;它经常与 MySQL 的命令行工具 mysql_config_editor 一起使用&#xff0c;这是一个允许用户安全地存储认证凭据&#xff08;如用户名、密码和连接参数&…...

gridview的模板按钮如何判断用户点击的是哪一行

在asp.net的 GridView 控件中&#xff0c;判断用户点击的是哪一行通常可以通过处理 GridView 的 RowCommand 事件来实现。RowCommand 事件会在 GridView 的每个按钮&#xff08;除非另有指定的CommandName&#xff09;被点击时触发&#xff0c;并且事件参数中包含了足够的信息来…...

虚拟化 之三 详解 jailhouse(ARM 平台)的构建过程、配置及使用

嵌入式平台下,由于资源的限制,通常不具备通用性的 Linux 发行版,各大主流厂商都会提供自己的 Linux 发行版。这个发行版通常是基于某个 Linux 发行版构建系统来构建的,而不是全部手动构建,目前主流的 Linux 发行版构建系统是 Linux 基金会开发的 Yocto 构建系统。 基本环…...

数据安全:Web3时代的隐私保护新标准

随着数字化时代的到来&#xff0c;我们的生活已经完全依赖于互联网和数据交换。然而&#xff0c;随之而来的是对个人隐私和数据安全的日益关注。在这个信息爆炸的时代&#xff0c;数据泄露、个人隐私侵犯和网络攻击等问题日益突出&#xff0c;而Web3技术的崛起正带来了一种全新…...

STM32串口不定长接收空闲中断

目录 1. 开启串口空闲中断2. 合理开关中断3. 串口发送函数 1. 开启串口空闲中断 最近接触到的 Modbus RTU 项目使用到了串口接收中断和空闲中断。记录一下 初始化可以直接套用正点原子的初始化&#xff0c;只需要添加一行即可 USART_ITConfig(USART1, USART_IT_IDLE, ENABLE)…...