C++STL---Vector、List所要掌握的基本知识

绪论​
拼着一切代价,奔你的前程。 ——巴尔扎克;本章主要围绕vector和list的使用,以及容器底层迭代器失效问题,同时会有对原码的分析和模拟实现其底层类函数。​​​​话不多说安全带系好,发车啦(建议电脑观看)。

附:红色,部分为重点部分;蓝颜色为需要记忆的部分(不是死记硬背哈,多敲);黑色加粗或者其余颜色为次重点;黑色为描述需要


1.Vector

vector 重要成员函数具体有
构造函数vetor()
析构函数~vector()
迭代器类函数begin()、end()、rbegin()、rend()、…
容量类函数resize()、size()、capacity()、reserve()、…
元素访问类函数operator[]、front()、back()、…
修改类函数push_back()、pop_back()、inser()、erase()、clear()、…
非成员函数swap()、relational operator()

——————————————

在这里插入图片描述Vector就像一个动态的数组也就是顺序表(有些时候可以想象成常规数组),也就是在写好的类中进行一定的函数操作,其中函数操作也就是上面所写。


1.1构造函数

函数作用函数原型
rang(迭代器)template <class InputIterator>vector (InputIterator first, InputIterator last,const allocator_type& alloc = allocator_type());
default(无参的全缺省的)explicit vector (const allocator_type& alloc = allocator_type());
fill(初始化vector为n个value值)explicit vector (size_type n, const value_type& val = value_type(),const allocator_type& alloc = allocator_type());
copy(拷贝构造)vector (const vector& x);
附:其中的const allocator_type& alloc = allocator_type())为空间配置器,这里提供了可以修改的缺省值,当你可以修改使用自己的内存池,基本不用修改STL内部提供的内存池
//初始化vector的方法
vector<int> v;//缺省
vector<int> v1(10,0);//初始化0个10
vector<int> v2(v1.begin(),v1.end());//迭代器从begin到end初始化
vector<int> v3(v2);//拷贝构造

1.2析构函数

在使用中不用去管因为其会自动执行释放构造函数所申请的空间


1.3迭代器类函数

知识点:

1.3.1begin()、end()、rbegin()、rend()

此处begin、end和string中的差不多用来指向结构中的数据,begin指向vector的首元素(类似于数组的首元素)、end指向的是数组最后一个元素的下一个位置,相反的rbegin指向的是数组的最后一个元素,而rend指向的是第一个元素的前一个位置无
其中可以发现begin指向vector内的数据,而end则不会指向vector内

练习使用:

	vector<int> v(10, 1);v.push_back(2);vector<int>::iterator it = v.begin();//指向vector首元素的1while (it != v.end()){cout << *it << ' ';it++;}//正向打印出所有元素:1 1 1 1 1 1 1 1 1 1 2 类似于指针cout << endl;vector<int>::reverse_iterator rit = v.rbegin();while (rit != v.rend()){cout << *rit << ' ';rit++;//注意此处仍然是++而不是--}//反向的打印出所有元素:2 1 1 1 1 1 1 1 1 1 1  类似于指针

附:cbegin、cend、crbeing、crend 是针对于const类型的,用法一样就不展开讲了。

1.4容量类函数

知识点:

1.4.1size()

函数原型:size_type size() const;
功能:查看vector中有几个元素。

vector<int> v(10,1);
v.size();//返回10

1.4.2capacity()

函数原型:size_type capacity() const;
功能:查看vector的capacity。

vector<int> v(10,1);
v.capacity();//在VS2019IDE环境下每次扩容1.5倍 1 ~ 2 ~ 3 ~ 6 ~ 9 ~ 13 ....

1.4.3reserve()

函数原型:void reserve (size_type n);
功能:设置vector的capacity,可提前把capacity设置好避免不断动态扩容的消耗

1.4.4resize()

函数原型:void resize (size_type n, value_type val = value_type());
改变vector的size(size决定了vector容器中的元素个数)

一般来说capacity申请扩展后都不会轻易的缩回

1.4.5empty()

函数原型:bool empty() const;
功能:查看vector内是否有数据,若返回true表示size == 0,否则放回false;

1.4.6shrink_to_fit()

函数原型:void shrink_to_fit();
功能:把capacity缩小到size大小(这条函数不一定执行最终是否执行还得看编译器他是无约束力的(non-binding))

1.5元素访问类函数

知识点:

1.5.1operator[]

函数原型
reference operator[] (size_type n);
const_reference operator[] (size_type n) const;

功能:像数组一样来访问vector中的元素
如:第一个元素v[0]

1.5.2at()

函数原型
reference at (size_type n);
const_reference at (size_type n) const;

功能:访问vector中的元素,但一般来说更多的用operator[]
如:v.at(0);访问第一个元素

1.5.3front()、back()

front函数原型back函数原型
reference front();reference back();
const_reference front() constreference back();

front的功能:找到vector中第一个元素并返回
如:vector<int> v(10,1); v.fornt();//指向向首元素1
back的功能:同理就是找到vector的最后一个元素
如: vector<int> v(10,1); v.back();//指向向最后一个元素1


写到这我觉得没必要再写了(有点浪费时间),对于这些函数可以自行查资料,里面有解释以及函数原型!c++官网,下面我将只写函数的用处,但仍然会把一些细节给写出来!


1.6修改类函数

作用具体函数
尾插push_back()
尾删pop_back()
清除数据clear()
将新的内容覆盖老的内容assign()
在某个位置删除数据erase()
在某处插入数据insert()
交换(直接交换两个对象所指向的地址)swap()

附:对于容器中我们若想在指定位置插入的话其实他并没有find函数来找到所对应的任意位置,对此在c++库中的算法库中其实有一个通用的find()查找函数
函数原型为:
template <class InputIterator, class T>
InputIterator find (InputIterator first, InputIterator last, const T& val);

用法如下:
vector<int> v;//在其中存储着1 2 3 4 5 6 7 find(v.begin(),v.end(),3);//传进迭代器区间,在其中找3,最后返回对应迭代器
这样就能找到容器中的数据并且进行一定的操作。


非成员函数
vector比较重载函数:relational operators
交换函数(用处是当两个是vector要进行交换时,去调用vector修改类中的swap,他是std交换函数的重载类):swap()

对于vector来说,还有个比较重要的点:

vector迭代器失效问题

在vector使用中可能会出现两种迭代器失效的情况:

  1. 当插入数据后的迭代器失效问题:因为在插入数据的情况下,有可能会发生异地扩容,当发生异地时就会导致原本迭代器所指向的位置为一个野指针也就是所说的迭代器失效,所以我们要注意的是迭代器在插入数据后要给迭代器重新赋值,并且在模拟实现插入函数中我们可能会扩容此处也要记得防止迭代器失效而记录迭代器位置,并作出一定的改变
        iterator insert(iterator pos, const T& x){assert(pos <= _finish);assert(pos >= _start);if (_finish == _endofstorage){size_t n = pos - _start;//记录相对位置防止迭代器在扩容后失效reserve(capacity() == 0 ? 4 : capacity() * 2);pos = _start + n;}iterator it = _finish;while (it > pos) {*it = *(it - 1);it--;}*it = x;_finish++;return _start;}
  1. 当删除数据时我们要注意的是,他可能也会有迭代器失效的问题,因为在VS2019IDE下规定当一个迭代器所指向的位置被删除后,该迭代器就已经失效了,我们不能再次的去使用若使用就会报错,而在CentOs7 g++下迭代器会指向物理下的下一个位置(如:1234 删除2后3覆盖了2的位置迭代器就会指向4),此处解决的方法为:更新迭代器让它接收erase的返回值,在c++中规定了erase删除后返回迭代器指向被删除数据的下一个位置,这样才能正确的删除数据, 如:it = erase(it)//删除后需要接收返回的迭代器

vector深浅拷贝问题

当有异地扩容的时候,我们需要去将数据进行拷贝,此处如果是内置类型的话直接进行拷贝然后释放就行了,而对于像string类这种有深浅拷贝问题的就需要注意,我们若用memcpy直接进行的拷贝就会出深浅拷贝问题,对此我们在reserve扩容函数中就需要注意改变写法具体如下。

        void reserve(size_t n){if (n  > capacity()){T* tmp = new T[n];size_t sz = size();if (_start) {//memcpy(tmp, _start, sz * sizeof(T)); 此处对于自定义类型来说是一个浅拷贝了for(size_t i = 0; i < size();i++){tmp[i] = _start[i];//逐个元素的进行赋值操作就能避免}delete[] _start;}_start = tmp;_finish = _start + sz;//这里不用size()因为这样会导致为nullptr , _endofstorage = _start + n;}}

vector反向迭代器的实现

对于vector反向迭代器来说,我们不能像正向一样的直接用一个指针指向start,他们需要从后往前来走(也就表示当reverse_iterartor ++ 时并不是往前走的而是往后的),这样我们就需要重新分装一个反向迭代器的类,并且对操作符++、–重载改变其原理。对此在一个类中我们还需要去实现一些其他的操作符,具体如下:

    template<class iterator>class reverse_iterator{typedef reverse_iterator<iterator> Self;reverse_iterator(iterator t):it(t){}Self& operator++(){it--;return *this;}Self operator++(int){Self tmp (it);it--;return tmp;}Self& operator--(){it++;return *this;}Self operator--(int){reverse_iterator tmp = it;it++;return tmp;}Self& operator*(){reverse_iterator tmp = it;return *(--tmp);}bool operator!=(const Self& t){return it != t.it;}bool operator==(const Self& t){return it == t.it;}private:iterator it;};

模拟实现Vector:

#pragma once
#include<iostream>
#include<stdio.h>
#include<assert.h>using namespace std;namespace bit
{template<class T>class vector{public:// Vector的迭代器是一个原生指针typedef T* iterator;typedef const T* const_iterator;typedef reverse_iterator<iterator> reverse_iterator;reverse_iterator rbegin(){return reverse_iterator(end());}reverse_iterator rend(){return  reverse_iterator(begin());}iterator begin() {return _start;}iterator end() {return _finish;}const_iterator cbegin() const{return _start;}const_iterator cend() const{return _finish;}// construct and destroy// //对于直接的构造不做如何处理vector(){}vector(size_t n, const T& value = T()){reserve(n);while (n--) {push_back(value);}}vector(int n, const T& value = T()){reserve(n);while (n--) {push_back(value);}}template<class InputIterator>vector(InputIterator first, InputIterator last){assert(first && last);while(first != last){push_back(*first);first++;}}//现代写法:vector(const vector<T>& v){vector<T> tmp(v.cbegin(),v.cend());swap(tmp);}//传统写法://vector(const vector<T>& v)//{//    //开辟一个空间//    reserve(v.capacity());//    //把数据放进去//    for (auto& e : v)//    {//        push_back(e);//    }//}vector<T>& operator=(vector<T> v){swap(v);return *this;}//void operator++(int)//{//    swap(v);//    return *this;//}~vector(){delete[] _start;_start = _finish = _endofstorage = nullptr;} capacitysize_t size() const{return _finish - _start;}size_t capacity() const{return _endofstorage - _start;}void reserve(size_t n){if (n  > capacity()){T* tmp = new T[n];size_t sz = size();if (_start) {//memcpy(tmp, _start, sz * sizeof(T)); 此处对于自定义类型来说是一个浅拷贝了for(size_t i = 0; i < size();i++){tmp[i] = _start[i];}delete[] _start;}_start = tmp;_finish = _start + sz;//这里不用size()因为这样会导致为nullptr , _endofstorage = _start + n;}}void resize(size_t n, const T& value = T()){if (n < capacity()){_finish = _start + n;}else {reserve(n);while (_finish < _start + n){*_finish = value;++_finish;}}}/access///T& operator[](size_t pos){assert(pos < size());return _start[pos];}const T& operator[](size_t pos)const{assert(pos < size());return _start[pos];}/modify/void push_back(const T& x){/*         if (_finish == _endofstorage){int cp = capacity() == 0 ? 4 : capacity() * 2;reserve(cp);}*_finish = x;_finish++;*/insert(end(), x);}void pop_back(){_finish--;}void swap(vector<T>& v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_endofstorage, v._endofstorage);}iterator insert(iterator pos, const T& x){assert(pos <= _finish);assert(pos >= _start);if (_finish == _endofstorage){size_t n = pos - _start;//防止迭代器在扩容后失效reserve(capacity() == 0 ? 4 : capacity() * 2);pos = _start + n;}iterator it = _finish;while (it > pos) {*it = *(it - 1);it--;}*it = x;_finish++;return _start;}iterator erase(iterator pos){assert(pos < _finish);assert(pos >= _start);iterator it = pos + 1;while (it < _finish){*(it - 1) = *it;it++;}_finish--;return pos;}private://给他们初始值是为了预防iterator _start = nullptr; // 指向数据块的开始iterator _finish = nullptr; // 指向有效数据的尾iterator _endofstorage = nullptr; // 指向存储容量的尾};template<class iterator>class reverse_iterator{typedef reverse_iterator<iterator> Self;reverse_iterator(iterator t):it(t){}Self& operator++(){it--;return *this;}Self operator++(int){Self tmp (it);it--;return tmp;}Self& operator--(){it++;return *this;}Self operator--(int){reverse_iterator tmp = it;it++;return tmp;}Self& operator*(){reverse_iterator tmp = it;return *(--tmp);}bool operator!=(const Self& t){return it != t.it;}bool operator==(const Self& t){return it == t.it;}private:iterator it;};}

2.List

在STL的list其实就是数据结构中的带头双向循环列表(若不清楚的建议看看数据机构基础之链表)
在这里插入图片描述

list 重要成员函数具体有
构造函数list()
析构函数~list()
迭代器类函数begin()、end()、rbegin()、rend()、…
容量类函数size()、empty()、…
元素访问类函数front()、back()
修改类函数push_back()、pop_back()、push_front()、inser()、erase()、clear()、…
操作类函数sort() 、 reverse() 、remove() 、…
非成员函数swap()、relational operator()

同样的在list中也不会去详细的简述函数(这些都是熟能生巧的多去查文档),会在后面写道一些细节内容以及函数的功能

2.1构造函数、析构函数

构造函数( (constructor))作用
list (size_type n, const value_type& val = value_type())构造的list中包含n个值为val的元素
list()构造空的list
list (const list& x)拷贝构造函数
list (InputIterator first, InputIterator last)用[first, last)区间中的元素构造list

析构函数不用自己调用,其内部主要包括把所借的内存归还给操作系统操作

2.2迭代器类函数

具体函数作用
begin()返回第一个元素的迭代器
end()返回最后一个元素下一个位置的迭代器
rbegin()返回第一个元素的reverse_iterator,即end位置
rend返回最后一个元素下一个位置的reverse_iterator,即begin位置
注意点在反向迭代器中++,是表示往前走 而不是往后 ; 同理的-- 则表示往后走

2.3容量类函数

具体函数作用
empty()检测list是否为空,是返回true,否则返回false
size()返回list中有效节点的个数

2.4元素访问类函数

具体函数作用
front()返回list的第一个节点中值的引用
back()返回list的最后一个节点中值的引用

2.5修改类函数

具体函数作用
push_back(const value_type& val)在list尾部插入值为val的元素
pop_back()删除list中最后一个元素
push_front(const value_type& val)在list首元素前插入值为val的元素
pop_front()删除list中第一个元素
iterator inser(iterator position, const value_type& val)在pos位置(此处是迭代器)处插入值为val的元素,还能插入一次性n个元素以及用迭代器插入一段数据
iterator erase(iterator position)删除pos迭代器所指向位置的元素,还能一次性删除一段数据erase (iterator first, iterator last)
clear()清空list中的有效元素
swap(list& x))交换两个list中的元素
void resize (size_type n, value_type val = value_type())提前初始化n个空间初始值默认为value_type()//整形为0…

2.5.1list 删除操作时的迭代器失效问题:

list在插入数据时并不会迭代器失效,而在删除时因为当前位置的迭代器所指向的地址空间已经被释放,而形成了迭代器失效。
所以为避免这种情况删除的实现会返回一个迭代器(也就是被删除数据的下一个位置)并且我们需要用接收这个迭代器才能解决失效问题(例:iterator it = l.erase(l.begin()))
通过上面的分析我们能知道当list删除数据后其原本的迭代器会指向被删除的数据的下一个数据,并且返回该位置!对此来实现list的删除:

       iterator erase(iterator pos){Node* cur = pos._Node;Node* prev = cur->_Prev;Node* next = cur->_Next;//一开始记录删除数据的下一个位置prev->_Next = next;next->_Prev = prev;_size--;return iterator(next);//返回next}

2.6操作类函数

具体函数作用
void splice (iterator position, list& x)拼接两个list,还能x指定中的i位置的一个元素拼接,或者是用迭代器确定区域(first,last]进行拼接
remove(valua)删除list中所有等于value值的元素
template <class Predicate> remove_if(Predicate pred)删除所有满足pred仿函数(也可能是函数指针)的元素
unique()删除重复的值
merge(list& x)拼接两个list,他有第二个参数为仿函数(或函数指针)
sort()对list中的数据进行排序,默认为升序,可以改变传递仿函数来自定义Compare比较函数(方法)
reverse()逆序把list中的数据倒过来

2.7list的实现

#pragma once#include<iostream>
using namespace std;namespace bite
{// List的节点类template<class T>struct ListNode{ListNode(const T& val = T()):_val(val),_Prev(nullptr),_Next(nullptr){}ListNode<T>* _Prev;ListNode<T>* _Next;T _val;};//List的迭代器类template<class T, class Ref, class Ptr>struct ListIterator{typedef ListNode<T> Node;typedef ListIterator<T, Ref, Ptr> Self;public:ListIterator(Node* node = nullptr) :_Node(node){}//ListIterator(const Self& l);T& operator*(){return _Node->_val;}Ptr operator->(){return &_Node->_val;}Self& operator++(){_Node = _Node->_Next;return *this;}//后置Self operator++(int){Self tmp(*this);//拷贝构造//其中self是类型、*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& l){return _Node != l._Node;}bool operator==(const Self& l){return _Node == l._Node;}Node* _Node;};template<class T, class Ref, class Ptr>struct RIterator{typedef ListNode<T> Node;typedef RIterator<T, Ref, Ptr> Self;public:RIterator(Node* node = nullptr):_Node(node){}//ListIterator(const Self& l);T& operator*(){return _Node->_val;}Ptr operator->(){return &_Node->_val;}Self& operator++(){_Node = _Node->_Prev;return *this;}//后置Self operator++(int){Self tmp(*this);//拷贝构造//其中self是类型、*this是一个具体的对象_Node = _Node->_Prev;return tmp;}//前置Self& operator--(){_Node = _Node->_Next;return *this;}//后置需要一个类型来占位Self& operator--(int){Self tmp(*this);_Node = _Node->_Next;return tmp;}bool operator!=(const Self& l){return _Node != l._Node;}bool operator==(const Self& l){return _Node == l._Node;}Node* _Node;};//list类template<class T>class list{public:typedef ListNode<T> Node;typedef ListIterator<T, T&, T*> iterator;//将typedef ListIterator<T, const T&, const T&> const_iterator;typedef RIterator<T, T&, T*> reverse_iterator;//将public:///// List的构造void empty_init(){_Head = new Node;//头节点_Head->_Next = _Head;_Head->_Prev = _Head;}list(){empty_init();}list(int n, const T& value = T()){for(int i = 0; i<n;i++){push_back(value);}}template <class Iterator>list(Iterator first, Iterator last){Iterator it = first;while (it != last){push_back(*it);++it;}}void swap(list<T>& l){std::swap(_Head, l._Head);std::swap(_size, l._size);}list(const list<T>& l){empty_init();for (auto nd : l){push_back(nd);}}list<T>& operator=(list<T> l){swap(l);return *this;}~list(){clear();//将申请的空间返还delete _Head;_Head = nullptr;}///// List Iteratoriterator begin(){//return iterator(_Head->_Next);return _Head->_Next;}iterator end(){//return iterator(_Head);return _Head;}const_iterator begin()const{return _Head->_Next;}const_iterator end()const{return _Head;}reverse_iterator rbegin(){return reverse_iterator(_Head->_Prev);}reverse_iterator rend(){return reverse_iterator(_Head);}// List Capacitysize_t size()const{return _size;}bool empty()const{return _size == 0;} List AccessT& front(){return _Head->_Next->_val;}const T& front()const{return _Head->_Next->_val;}//T& back(){return _Head->_Prev->_val;}//const T& back()const{return _Head->_Prev->_val;}// List Modifyvoid push_back(const T& val){ //Node* tail = _Head->_Prev;//Node* newnode = new Node(val);//tail->_Next = newnode;//newnode->_Prev = tail;//_Head->_Prev = newnode;//newnode->_Next = _Head;//_size++;insert(end(), val); }void pop_back() {/*           Node* tail = _Head->_Prev;tail->_Prev->_Next = _Head;_Head->_Prev = tail->_Prev;delete tail;_size--;*/erase(--end()); }void push_front(const T& val) { //Node* head = _Head->_Next;//Node* newnode = new Node(val);//head->_Prev = newnode;//newnode->_Next = head;//_Head->_Next = newnode;//head->_Prev = _Head;//_size++;insert(begin(), val); }void pop_front(){ /*         Node* head = _Head->_Next;head->_Next->_Prev = _Head;_Head->_Next = head->_Next;delete head;_size--;*/erase(begin());} 在pos位置前插入值为val的节点iterator insert(iterator pos, const T& val){Node* cur = pos._Node;Node* newnode = new Node(val);Node* prev = cur->_Prev;prev->_Next = newnode;newnode->_Prev = prev;newnode->_Next = cur;cur->_Prev = newnode;_size++;return iterator(newnode);} 删除pos位置的节点,返回该节点的下一个位置iterator erase(iterator pos){Node* cur = pos._Node;Node* prev = cur->_Prev;Node* next = cur->_Next;prev->_Next = next;next->_Prev = prev;_size--;return iterator(next);}void clear(){iterator it = begin();while(it != end()){it = erase(it);}//for(auto it : )_size = 0;}private:Node* _Head;size_t _size;};
}

本章完。预知后事如何,暂听下回分解。

如果有任何问题欢迎讨论哈!

如果觉得这篇文章对你有所帮助的话点点赞吧!

持续更新大量C++细致内容,早关注不迷路。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/122230.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Mac电脑配置Dart编程环境

1.安装Dart SDK 官网地址&#xff1a;https://dart.dev/get-dart $brew tap dart-lang/dart$brew install dart 安装后&#xff0c;用命令检测一下是否安装正常。 $brew info dart 2.VS Code配置Dart环境 1).安装VS Code 官网地址&#xff1a;https://code.visualstudio.c…

Python环境下LaTeX数学公式转图像方案调研与探讨

目录 引言方案一&#xff1a;基于LaTeX环境方案二&#xff1a;基于KaTeX(推荐) 方案三&#xff1a;基于Matplotlib写在最后 引言 近来&#xff0c;涉及到一些公式识别的项目&#xff0c;输入是公式的图像&#xff0c;输出是LaTeX格式的数学公式字符串。 这类项目一般都采用深…

pgsql 分组查询,每组取10条

需求&#xff1a; 按照表的字段分组&#xff0c;然后每组取10条结果&#xff0c;返回即可 sql 如下&#xff1a; SELECT* FROM (SELECT chk_id,feature_id,task_id, ROW_NUMBER () OVER (PARTITION BY chk_id ORDER BY chk_id) AS row_num FROM ics_check_report WHERE task…

Ubuntu 23.10(Mantic Minotaur)正式发布,支持Linux 6.5和GNOME 45

导读Canonical 近日正式发布了 Ubuntu 23.10&#xff08;Mantic Minotaur&#xff09;操作系统&#xff0c;其中包含一些最新的 GNU/Linux 技术、改进的硬件支持以及许多其他变化。 Ubuntu 23.10 采用了最新的 Linux 6.5 内核系列&#xff0c;并为 Ubuntu 桌面和服务器增强了 z…

Qt5 Python-docx库的使用,Qt python混合编程,qt 读写word,不依赖office

解决方案的选择 参考&#xff1a; https://www.jianshu.com/p/be68884849c3 因为项目要求不能使用模板方案&#xff0c;不能依赖Office&#xff0c;网上找了一些解决方案进行调研&#xff0c;以下几个方案&#xff1a; OpenOffice: 兼容性差&#xff0c;集成调用难度大LibOffi…

【顺序栈的出栈,链栈的表示和实现,递归定义】

文章目录 顺序栈的出栈 链栈的表示和实现链表的初始化判断链栈是否为空链栈的入栈链栈的出栈 递归定义函数的调用过程 顺序栈的出栈 &#xff08;1&#xff09;判断是否栈空&#xff0c;若空则出错&#xff08;下溢&#xff09;。 &#xff08;2&#xff09;获取栈顶元素e。 &…

【AD9361 数字接口CMOS LVDSSPI】B 并行数据之CMOS 续

续【AD9361 数字接口CMOS &LVDS&SPI】B 并行数据之CMOS 数据总线空闲和周转周期 &#xff08;CMOS&#xff09; P0_D[11&#xff1a;0]和P1_D[11&#xff1a;0]总线信号通常由BBP或AD9361有源驱动。在任何空闲期间&#xff0c;两个组件都会忽略数据总线值。但是&…

MODBUS-RTU从站通信(SMART PLC作为MODBUS-RTU从站)

SMART PLC作为MODBUS-RTU主站通信请参考下面文章链接: 【精选】PLC MODBUS通信优化、提高通信效率避免权限冲突(程序+算法描述)-CSDN博客文章浏览阅读2.5k次,点赞5次,收藏10次。MODBUS通讯非常简单、应用也非常广泛,有些老生常谈的问题,这里不再赘述,感兴趣的可以参看…

基于 ARM+FPGA+AD平台的多类型同步信号采集仪开发及试验验证(一)上位机设计

采集仪上位机设计 本章开发了一款基于 C# 的上位机软件&#xff0c;用于对多类型同步信号采集仪的各项功能 进行操作。从采集仪的数据传输需求出发&#xff0c;上位机利用以太网 UDP 协议实现与采集仪 的数据交互&#xff0c;包括向采集仪发送控制信息与配置信息、接收采…

Vite+Vue3项目全局引入scss文件

前言 Sass 是世界上最成熟、最稳定、最强大的专业级CSS扩展语言&#xff01;在日常项目开发过程中使用非常广泛&#xff0c;今天主要讲一下 ViteVue3 项目中该如何全局引入 scss 文件&#xff0c;引入混合 mixin 文件的不同配置。捎带说一下 Vue2 中的引入方式做一下简单的对比…

【linux】安装rpmrebuild

rpmrebuild是一种从已经安装的包中构建RPM文件的工具。它可以用于轻松构建修改后的包&#xff0c;并适用于任何使用RPM的Linux发行版。 访问地址 rpm rebuild download | SourceForge.net 选择版本 版本地址&#xff1a;版本地址 下载安装包 安装 rpm -ivh rpmrebuild-2.15…

人工智能轨道交通行业周刊-第64期(2023.10.16-10.29)

本期关键词&#xff1a;北斗应用、供电智能运维、5G-R、铁路职称、星火大模型 1 整理涉及公众号名单 1.1 行业类 RT轨道交通人民铁道世界轨道交通资讯网铁路信号技术交流北京铁路轨道交通网上榜铁路视点ITS World轨道交通联盟VSTR铁路与城市轨道交通RailMetro轨道世界铁路那…

【计算机网络】认识协议

目录 一、应用层二、协议三、序列化和反序列化 一、应用层 之前的socket编程&#xff0c;都是在通过系统调用层面&#xff0c;如今我们来向上打通计算机网络。认识应用层的协议和序列化与反序列化 我们程序员写的一个个解决我们实际问题, 满足我们日常需求的网络程序, 都是在应…

如何快速解决d3dcompiler_43.dll缺失问题?五种方法快速解决

在计算机使用过程中&#xff0c;我们常常会遇到一些错误提示&#xff0c;其中之一就是“D3DCompiler_43.dll缺失”。这个错误通常会导致游戏、应用程序或系统无法正常运行。为了解决这个问题&#xff0c;我们需要采取一些修复方案来恢复缺失的文件。本文将介绍五个修复D3DCompi…

百度超级链XuperChain使用JavaSDK接入

环境 &#xff1a; ubuntu20 xuperchain 5.3 go 1.17 springboot : 2.5.14 前言 请提前启动好xchain的节点&#xff0c;我选择简单启一个xchain节点作为测试&#xff0c;并且使用默认端口37101 SpringBoot项目初始化 我们先进行SpringBoot项目的配置进行讲解&#xff0c;这里…

Flutter的The file name ‘xxxx.dart‘ isn‘t a snake_case identifier警告

文章目录 警告原因分析解决方法dart的一些命名规则变量和函数命名&#xff1a;类和类型命名&#xff1a;常量和枚举&#xff1a;文件命名&#xff1a;包命名&#xff1a;注释&#xff1a;命名一致性&#xff1a;避免缩写&#xff1a;可搜索的命名&#xff1a; 一些好习惯 警告 …

[已解决]安装的明明是pytorch-gpu,但是condalist却显示cpu版本,而且torch.cuda.is_available 也是flase

问题; 安装了gpu版本的pytorch&#xff0c;但是显示的torch.cuda.is_available(&#xff09;却是flase。 conda list查看 版本显示只有cpuonly 在网上找了半天&#xff0c;也没有解决办法。 仔细看了一下&#xff0c;发现&#xff0c;有个单独的包叫cpuonly&#xff0c;不知道…

33基于MATLAB的对RGB图像实现中值滤波,均值滤波,维纳滤波。程序已通过调试,可直接运行。

基于MATLAB的对RGB图像实现中值滤波&#xff0c;均值滤波&#xff0c;维纳滤波。程序已通过调试&#xff0c;可直接运行。 33 MATLAB、图像处理、维纳滤波 (xiaohongshu.com)

linux入门---多线程的控制

目录标题 线程库pthread_create如何一次性创建多个线程线程的终止线程的等待线程取消分离线程如何看待其他语言支持的多线程线程id的本质线程的局部存储线程的封装 线程库 要想控制线程就得使用原生线程库也可以将其称为pthread库&#xff0c;这个库是遵守posix标准的&#xf…

小美的修路(最小生成树练习)

本题链接&#xff1a;登录—专业IT笔试面试备考平台_牛客网 题目&#xff1a; 样例&#xff1a; 输入 3 4 1 2 3 1 1 2 2 0 1 3 1 0 2 3 3 0 输出 2 1 3 思路&#xff1a; 由题意&#xff0c;这里建造的城市需要修路&#xff0c;且每个城市之间可以联通&#xff0c;且 是 1 …