【c++丨STL】string模拟实现(附源码)

news2024/10/28 8:02:29
🌟🌟作者主页:ephemerals__
🌟🌟所属专栏:C++、STL

目录

前言

一、头文件(成员变量与函数声明)

二、源文件(功能实现)

交换两字符串

构造函数

拷贝构造

赋值重载

析构函数

常量成员npos

获取只读字符串

清除字符串

容量接口

增容

下标引用重载

迭代器

字符串插入和删除操作

insert

push_back、append、operator+=

erase

查找

构造子串

compare

输入和输出

三、程序全部代码

总结


前言

        之前我们学习了STL的第一个容器--string及其常用接口的使用方法:

【c++丨STL】string类的使用-CSDN博客

不过仅仅掌握使用方法还不够,面试当中常常会让我们模拟实现STL的某个容器的关键框架。所以今天我们深入string底层,用我们的功底来模拟实现一个简单的string类。

        本篇博客我们不会将string的所有接口原模原样地实现出来,而是根据string的逻辑,模拟实现一些重点接口函数

一、头文件(成员变量与函数声明)

        头文件中的内容是类成员以及我们将要实现的接口声明,代码如下:

#pragma once
#include <iostream>
#include <cassert>
#include <cstring>
using namespace std;

class String
{
public:
	//迭代器定义
	typedef char* iterator;
	typedef const char* const_iterator;

	//迭代器接口
	iterator begin();
	iterator end();
	const_iterator begin() const;
	const_iterator end() const;

	//常量成员
	static size_t npos;

	//全缺省构造
	String(const char* str = "");

	//拷贝构造
	String(const String& str);

	//赋值重载
	String& operator=(String s);

	//析构
	~String();

	//获取只读字符串
	const char* c_str() const;

	//交换两字符串
	void swap(String& s);

	//清除字符串
	void clear();

	//容量接口
	size_t size() const;
	size_t capacity() const;

	//增容
	void reserve(size_t n);

	//下标引用重载
	char& operator[](size_t i);
	const char& operator[](size_t i) const;

	//插入
	void insert(size_t pos, char c);
	void insert(size_t pos, const char* str);
	void push_back(char c);
	void append(const char* str);
	String& operator+=(char c);
	String& operator+=(const char* str);

	//删除
	void erase(size_t pos, size_t len = npos);

	//查找
	size_t find(char c, size_t pos = 0);
	size_t find(const char* str, size_t pos = 0);

	//构造子串
	String substr(size_t pos = 0, size_t len = npos) const;
private:
	char* _str;//起始指针
	size_t _size;//字符串长度
	size_t _capacity;//空间大小
};

//compare
bool operator<(const String& l, const String& r);
bool operator>(const String& l, const String& r);
bool operator<=(const String& l, const String& r);
bool operator>=(const String& l, const String& r);
bool operator==(const String& l, const String& r);
bool operator!=(const String& l, const String& r);

//输入输出
ostream& operator<<(ostream& out, const String& str);
istream& operator>>(istream& in, String& str);
istream& getline(istream& in, String& str, char delim = '\n');

//交换两字符串--非成员函数版
void swap(String& s1, String& s2);

二、源文件(功能实现)

        对于上述声明的函数,我们会一一进行实现并分析代码。首先从交换函数开始:

交换两字符串

        之前我们就了解到string类中有两个交换函数,一个重载为成员函数,另一个是非成员函数。我们就将二者都实现一下:

//交换两字符串
void String::swap(String& s)
{
	std::swap(_str, s._str);
	std::swap(_size, s._size);
	std::swap(_capacity, s._capacity);
}

//交换两字符串--非成员函数版
void swap(String& s1, String& s2)
{
	s1.swap(s2);
}

交换函数非常简单,虽然两对象都有各自所维护的内存,但是我们只需要调用标准库中的交换函数交换两指针的指向即可。然后将_size和_capacity也交换一下。

对于非成员函数般的交换函数,直接调用成员函数即可。

为什么先要实现交换函数呢?待会我们实现默认成员函数时,你自然能体会到它的妙用。

构造函数

//全缺省构造
String::String(const char* str)
	:_size(strlen(str))
{
	_str = new char[_size + 1] {'\0'};
	_capacity = _size;
	strcpy(_str, str);
}

在构造函数当中,我们首先将元素个数调整为等同于str长度的值,便于将字符串中的内容拷贝给新对象,然后在起始指针处动态开辟内存(注意大小是_size + 1,因为最后一个位置存放 '\0' )。注意我们_capacity的大小是不包含 '\0' 的,但实际开辟的内存大小要大于它。最后,将str中的数据拷贝给_str就好。不传参时,str的内容默认设置为空字符串。

拷贝构造

//拷贝构造
String::String(const String& s)
{
	_str = new char[s._capacity + 1] {'\0'};
	strcpy(_str, s._str);
	_size = s._size;
	_capacity = s._capacity;
}

与构造函数的逻辑相同,先开辟空间,然后拷贝数据即可。不过这是传统写法,还有一个现代写法

//拷贝构造 现代写法
String::String(const String& s)
{
	String tmp(s._str);
	swap(tmp);
}

 仔细观察就会发现,这种写法非常巧妙:首先我们用s中的字符串构造临时对象tmp,然后用swap交换tmp与新对象的内容,这样就很轻松地完成了拷贝构造

赋值重载

        与拷贝构造类似,赋值重载也有传统写法和现代写法:

//赋值重载 传统写法
String& String::operator=(const String& s)
{
	if (this != &s)//若是自己给自己赋值,就什么都不做
	{
		delete[] _str;
		_str = new char[s._capacity + 1] {'\0'};
		strcpy(_str, s._str);
		_size = s._size;
		_capacity = s._capacity;
	}
	return *this;//可以支持连续赋值
}
//赋值重载 现代写法
String& String::operator=(String s)
{
	swap(s);
	return *this;
}

在传统写法当中,我们老老实实地销毁原字符串,然后重新开辟空间,再将新字符串拷贝过来...非常麻烦。现代写法当中,我们使用传值传参,构造一份相同的对象s,然后直接交换s与this对象,this对象就被成功赋值,并且等号右边的对象也没有被改动

析构函数

        由于我们的字符串是动态开辟的,所以就要显示写析构函数,在对象销毁时释放这些内存:

//析构
String::~String()
{
	delete[] _str;
	_str = nullptr;//及时制空
	_size = _capacity = 0;
}

这里不必多说,写法和c语言实现的顺序表没什么区别。接下来我们开始正式实现常量成员nops和一些比较常用的接口

常量成员npos

常量成员定义如下:

//常量成员
size_t String::npos = -1;

常量成员用无符号整数的-1来表示,是size_t的最大值,它作为一些接口的缺省参数,用于表示“直到字符串结束”。

获取只读字符串

//获取只读字符串
const char* String::c_str() const
{
	return _str;
}

获取只读字符串时,直接返回字符串起始指针即可。注意返回值被const修饰,该字符串不能被外部修改

清除字符串

//清除字符串
void String::clear()
{
	_str[0] = '\0';
	_size = 0;
}

清除字符串时,只需将第一个字符设为 '\0' ,然后调整长度为0即可,无需调整空间容量。

容量接口

        容量接口便于用户访问字符串的长度或空间容量。

//容量接口
size_t String::size() const
{
	return _size;
}
size_t String::capacity() const
{
	return _capacity;
}

增容

//增容
void String::reserve(size_t n)
{
	if (n > _capacity)//当需要空间大于原有容量时,才需要增容
	{
		char* tmp = new char[n + 1] {'\0'};
		strcpy(tmp, _str);
		delete[] _str;
		_str = tmp;
		_capacity = n;
	}
}

reserve的作用是为字符串预留空间,单位是字节注意:当参数n的值小于已有空间的总大小时,不会改变其大小。当我们实现增容时,首先需要开辟一块n + 1字节的内存空间(最后位置放 '\0' ),然后将数据拷贝到新空间,再释放原来的空间

        接下来,我们开始实现字符串元素访问与遍历相关操作。首先从下标引用开始:

下标引用重载

//下标引用重载
char& String::operator[](size_t i)
{
	assert(i < _size);//防止越界
	return _str[i];
}
const char& String::operator[](size_t i) const
{
	assert(i < _size);
	return _str[i];
}

注意:为了确保用户能够修改字符串的内容,函数返回值是字符的引用。对于const对象,内容不能修改,则返回const引用。

迭代器

        之前已经提到过,现阶段我们可以将迭代器理解成一种指针,通过解引用来访问数据元素。我们定义String类时,使用指针来模拟迭代器。迭代器定义如下:

//迭代器定义
typedef char* iterator;
typedef const char* const_iterator;
//迭代器接口
String::iterator String::begin()
{
	return _str;
}
String::iterator String::end()
{
	return _str + _size;
}
String::const_iterator String::begin() const
{
	return _str;
}
String::const_iterator String::end() const
{
	return _str + _size;
}

注意:我们实现的迭代器接口函数名只有和STL保持一致,才可以支持范围for

让我们使用一下自己的迭代器:

int main()
{
	String str = "hello world";

	//迭代器遍历
	for (auto it = str.begin(); it != str.end(); it++)
	{
		cout << *it << ' ';
	}
	cout << endl;

	//范围for
	for (auto e : str)
	{
		cout << e << ' ';
	}
	cout << endl;
	return 0;
}

字符串插入和删除操作

        接下来,我们开始实现string字符串的插入和删除操作。

insert

        insert支持指定位置的插入操作,我们首先实现它,后续实现其他插入删除操作时就可以直接调用该接口。代码如下:

//插入
void String::insert(size_t pos, char c)//插入字符
{
	assert(pos < _size);//防止越界
	if (_size == _capacity)//空间不够则增容
	{
		//调用增容函数
		reserve(_capacity == 0 ? 4 : 2 * _capacity);//初始设置为4,后续二倍开辟
	}

	//pos之后的字符全部右移一位
	size_t end = _size + 1;
	while (end > pos)
	{
		_str[end] = _str[end - 1];
		end--;
	}
	_str[pos] = c;
	_size++;
}
void String::insert(size_t pos, const char* str)//插入字符串
{
	assert(pos <= _size);
	size_t len = strlen(str);
	if (_size + len > _capacity)//空间不够,增容
	{
		//先扩2倍,若还不够,则设置为等同于总长度的大小
		size_t newcapacity = _capacity * 2;
		if (newcapacity < _size + len)
		{
			newcapacity = _size + len;
		}
		reserve(newcapacity);
	}

	//pos之后的字符全部右移len位
	size_t end = _size + len;
	while (end > pos + len - 1)
	{
		_str[end] = _str[end - len];
		end--;
	}
	for (size_t i = 0; i < len; i++)
	{
		_str[pos + i] = str[i];
	}
	_size += len;
}

第一个函数实现的是字符的插入,第二个是字符串的插入。实现思路类似于顺序表指定位置的插入,这里不再赘述。接下来实现剩余的插入接口:

push_back、append、operator+=

void String::push_back(char c)
{
	insert(_size, c);
}
void String::append(const char* str)
{
	insert(_size, str);
}
String& String::operator+=(char c)
{
	insert(_size, c);
	return *this;
}
String& String::operator+=(const char* str)
{
	insert(_size, str);
	return *this;
}

这三个函数的功能是尾插字符或字符串,直接调用我们刚才实现的insert就可以实现。

erase

        erase支持指定位置的删除操作,代码如下:

//删除
void String::erase(size_t pos, size_t len)
{
	assert(pos < _size);
	if (pos + len >= _size)//删除的字符个数超出了现有个数,则直接删除pos后的所有字符
	{
		_str[pos] = '\0';
		_size = pos;
	}
	else
	{
		//(pos + len)之后的元素全体左移len位
		size_t end = pos + len;
		while (end <= _size)
		{
			_str[end - len] = _str[end];
			end++;
		}
		_size -= len;
	}
}

这里的逻辑与顺序表指定位置删除类似,不再多说。

查找

        接下来,我们实现查找功能find:

//查找
size_t String::find(char c, size_t pos)//从pos位置开始查找
{
	assert(pos < _size);
	for (size_t i = 0; i < _size; i++)
	{
		if (_str[i] == c) 
		{
			return i;
		}
	}
	return npos;
}
size_t String::find(const char* str, size_t pos)
{
	assert(pos < _size);
	const char* p = strstr(_str + pos, str);//调用strstr查找子串
	if (p == nullptr)//没找到
	{
		return npos;
	}
	return p - str;//返回两指针相对位置
}

查找可分为字符查找和字符串查找,其中字符串查找调用strstr函数即可,注意返回值细节处理。 

构造子串

        substr可以构造一个string类,内容是原字符串中pos位置开始的len个字符组成的子串。代码如下:

//构造子串
String String::substr(size_t pos = 0, size_t len) const
{
	assert(pos < _size);
	if (len > (_size - pos))//超出字符串范围,则默认取到尾
	{
		len = _size - pos;
	}
	String sub;
	for (size_t i = pos; i < len; i++)//循环尾插
	{
		sub += _str[pos + i];
	}
	return sub;
}

compare

        compare指的是一些字符串之间的比较函数,属于非成员函数,实现方法也很简单:

//compare
bool operator<(const String& l, const String& r)
{
	return strcmp(l.c_str(), r.c_str()) < 0;//调用strcmp比较
}
bool operator>(const String& l, const String& r)
{
	return !(l < r);
}
bool operator<=(const String& l, const String& r)
{
	return (l == r || l < r);
}
bool operator>=(const String& l, const String& r)
{
	return (l == r || l > r);
}
bool operator==(const String& l, const String& r)
{
	return strcmp(l.c_str(), r.c_str()) == 0;
}
bool operator!=(const String& l, const String& r)
{
	return !(l == r);
}

输入和输出

        最后要实现的就是输入和输出。它们可以让我们直接配合cin和cout去操作我们自己的string类。 注意这里的细节处理

//输入输出
ostream& operator<<(ostream& out, const String& str)
{
	for (auto ch : str)//遍历打印每一个字符
	{
		out << ch;
	}
	return out;//支持连续输出
}
istream& operator>>(istream& in, String& str)
{
	str.clear();//先清除原字符串
	char ch = in.get();//使用get函数读取单个字符
	while (ch != ' ' && ch != '\n')//读取到空格或换行就停止
	{
		str += ch;
		ch = in.get();
	}
	return in;//支持连续输入
}

接下来我们再实现一个自定义读取结束符的getline函数:

istream& getline(istream& in, String& str, char delim)
{
	str.clear();
	char ch = in.get();
	while (ch != delim)
	{
		str += ch;
		ch = in.get();
	}
	return in;
}

三、程序全部代码

        我们模拟实现string类的全部代码如下:

#include <iostream>
#include <cassert>
#include <cstring>
using namespace std;

class String
{
public:
	//迭代器定义
	typedef char* iterator;
	typedef const char* const_iterator;

	//迭代器接口
	iterator begin();
	iterator end();
	const_iterator begin() const;
	const_iterator end() const;

	//常量成员
	static size_t npos;

	//全缺省构造
	String(const char* str = "");

	//拷贝构造
	String(const String& str);

	//赋值重载
	String& operator=(String s);

	//析构
	~String();

	//获取只读字符串
	const char* c_str() const;

	//交换两字符串
	void swap(String& s);

	//清除字符串
	void clear();

	//容量接口
	size_t size() const;
	size_t capacity() const;

	//增容
	void reserve(size_t n);

	//下标引用重载
	char& operator[](size_t i);
	const char& operator[](size_t i) const;

	//插入
	void insert(size_t pos, char c);
	void insert(size_t pos, const char* str);
	void push_back(char c);
	void append(const char* str);
	String& operator+=(char c);
	String& operator+=(const char* str);

	//删除
	void erase(size_t pos, size_t len = npos);

	//查找
	size_t find(char c, size_t pos = 0);
	size_t find(const char* str, size_t pos = 0);

	//构造子串
	String substr(size_t pos = 0, size_t len = npos) const;
private:
	char* _str;
	size_t _size;
	size_t _capacity;
};

//compare
bool operator<(const String& l, const String& r);
bool operator>(const String& l, const String& r);
bool operator<=(const String& l, const String& r);
bool operator>=(const String& l, const String& r);
bool operator==(const String& l, const String& r);
bool operator!=(const String& l, const String& r);

//输入输出
ostream& operator<<(ostream& out, const String& str);
istream& operator>>(istream& in, String& str);
istream& getline(istream& in, String& str, char delim = '\n');

//交换两字符串--非成员函数版
void swap(String& s1, String& s2);

//交换两字符串
void String::swap(String& s)
{
	std::swap(_str, s._str);
	std::swap(_size, s._size);
	std::swap(_capacity, s._capacity);
}

//交换两字符串--非成员函数版
void swap(String& s1, String& s2)
{
	s1.swap(s2);
}

//全缺省构造
String::String(const char* str)
	:_size(strlen(str))
{
	_str = new char[_size + 1] {'\0'};
	_capacity = _size;
	strcpy(_str, str);
}

拷贝构造 传统写法
//String::String(const String& s)
//{
//	_str = new char[s._capacity + 1] {'\0'};
//	strcpy(_str, s._str);
//	_size = s._size;
//	_capacity = s._capacity;
//}

//拷贝构造 现代写法
String::String(const String& s)
{
	String tmp(s._str);
	swap(tmp);
}

赋值重载 传统写法
//String& String::operator=(const String& s)
//{
//	if (this != &s)//若是自己给自己赋值,就什么都不做
//	{
//		delete[] _str;
//		_str = new char[s._capacity + 1] {'\0'};
//		strcpy(_str, s._str);
//		_size = s._size;
//		_capacity = s._capacity;
//	}
//	return *this;
//}

//赋值重载 现代写法
String& String::operator=(String s)
{
	swap(s);
	return *this;
}

//析构
String::~String()
{
	delete[] _str;
	_str = nullptr;//及时制空
	_size = _capacity = 0;
}

//常量成员
size_t String::npos = -1;

//获取只读字符串
const char* String::c_str() const
{
	return _str;
}

//清除字符串
void String::clear()
{
	_str[0] = '\0';
	_size = 0;
}

//容量接口
size_t String::size() const
{
	return _size;
}
size_t String::capacity() const
{
	return _capacity;
}

//增容
void String::reserve(size_t n)
{
	if (n > _capacity)//当需要空间大于原有容量时,才需要增容
	{
		char* tmp = new char[n + 1] {'\0'};
		strcpy(tmp, _str);
		delete[] _str;
		_str = tmp;
		_capacity = n;
	}
}

//下标引用重载
char& String::operator[](size_t i)
{
	assert(i < _size);//防止越界
	return _str[i];
}
const char& String::operator[](size_t i) const
{
	assert(i < _size);
	return _str[i];
}

//迭代器接口
String::iterator String::begin()
{
	return _str;
}
String::iterator String::end()
{
	return _str + _size;
}
String::const_iterator String::begin() const
{
	return _str;
}
String::const_iterator String::end() const
{
	return _str + _size;
}

//插入
void String::insert(size_t pos, char c)//插入字符
{
	assert(pos < _size);//防止越界
	if (_size == _capacity)//空间不够则增容
	{
		//调用增容函数
		reserve(_capacity == 0 ? 4 : 2 * _capacity);//初始设置为4,后续二倍开辟
	}

	//pos之后的字符全部右移一位
	size_t end = _size + 1;
	while (end > pos)
	{
		_str[end] = _str[end - 1];
		end--;
	}
	_str[pos] = c;
	_size++;
}
void String::insert(size_t pos, const char* str)//插入字符串
{
	assert(pos <= _size);
	size_t len = strlen(str);
	if (_size + len > _capacity)//空间不够,增容
	{
		//先扩2倍,若还不够,则设置为等同于总长度的大小
		size_t newcapacity = _capacity * 2;
		if (newcapacity < _size + len)
		{
			newcapacity = _size + len;
		}
		reserve(newcapacity);
	}

	//pos之后的字符全部右移len位
	size_t end = _size + len;
	while (end > pos + len - 1)
	{
		_str[end] = _str[end - len];
		end--;
	}
	for (size_t i = 0; i < len; i++)
	{
		_str[pos + i] = str[i];
	}
	_size += len;
}
void String::push_back(char c)
{
	insert(_size, c);
}
void String::append(const char* str)
{
	insert(_size, str);
}
String& String::operator+=(char c)
{
	insert(_size, c);
	return *this;
}
String& String::operator+=(const char* str)
{
	insert(_size, str);
	return *this;
}

//删除
void String::erase(size_t pos, size_t len)
{
	assert(pos < _size);
	if (pos + len >= _size)//删除的字符个数超出了现有个数,则直接删除pos后的所有字符
	{
		_str[pos] = '\0';
		_size = pos;
	}
	else
	{
		//(pos + len)之后的元素全体左移len位
		size_t end = pos + len;
		while (end <= _size)
		{
			_str[end - len] = _str[end];
			end++;
		}
		_size -= len;
	}
}

//查找
size_t String::find(char c, size_t pos)//从pos位置开始查找
{
	assert(pos < _size);
	for (size_t i = 0; i < _size; i++)
	{
		if (_str[i] == c) 
		{
			return i;
		}
	}
	return npos;
}
size_t String::find(const char* str, size_t pos)
{
	assert(pos < _size);
	const char* p = strstr(_str + pos, str);//调用strstr查找子串
	if (p == nullptr)//没找到
	{
		return npos;
	}
	return p - str;//返回两指针相对位置
}

//构造子串
String String::substr(size_t pos = 0, size_t len) const
{
	assert(pos < _size);
	if (len > (_size - pos))//超出字符串范围,则默认取到尾
	{
		len = _size - pos;
	}
	String sub;
	for (size_t i = pos; i < len; i++)//循环尾插
	{
		sub += _str[pos + i];
	}
	return sub;
}

//compare
bool operator<(const String& l, const String& r)
{
	return strcmp(l.c_str(), r.c_str()) < 0;//调用strcmp比较
}
bool operator>(const String& l, const String& r)
{
	return !(l < r);
}
bool operator<=(const String& l, const String& r)
{
	return (l == r || l < r);
}
bool operator>=(const String& l, const String& r)
{
	return (l == r || l > r);
}
bool operator==(const String& l, const String& r)
{
	return strcmp(l.c_str(), r.c_str()) == 0;
}
bool operator!=(const String& l, const String& r)
{
	return !(l == r);
}

//输入输出
ostream& operator<<(ostream& out, const String& str)
{
	for (auto ch : str)//遍历打印每一个字符
	{
		out << ch;
	}
	return out;//支持连续输出
}
istream& operator>>(istream& in, String& str)
{
	str.clear();//先清除原字符串
	char ch = in.get();//使用get函数读取单个字符
	while (ch != ' ' && ch != '\n')//读取到空格或换行就停止
	{
		str += ch;
		ch = in.get();
	}
	return in;//支持连续输入
}
istream& getline(istream& in, String& str, char delim)
{
	str.clear();
	char ch = in.get();
	while (ch != delim)
	{
		str += ch;
		ch = in.get();
	}
	return in;
}

总结

        今天,我们在学会使用string类的基础上模拟实现了string类的常用功能,这对于我们学习数据结构和string类有很大帮助。之后博主会和大家一起进入下一个容器——vector的学习。如果你觉得博主讲的还不错,就请留下一个小小的赞在走哦,感谢大家的支持❤❤❤

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

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

相关文章

人工智能进程;算子加速的具体计算部分;大模型GPT5:参数18万亿;大模型面临问题

目录 人工智能进程 算子加速的简单理解,举例说明 一、简单理解 二、举例说明 一、算子加速的具体计算部分 二、举例说明 三、算子加速是否仅针对GPU 大模型GPT5:参数18万亿 大模型面临问题 算力集群设计框架 人工智能进程

【c++高级篇】--多任务编程/多线程(Thread)

目录 1.进程和线程的概念&#xff1a; 1.1 进程&#xff08;Process&#xff09;&#xff1a; 1.2线程&#xff08;Thread&#xff09;&#xff1a; 1.3 对比总结&#xff1a; 2.多线程编程&#xff1a; 2.1 基于线程的多任务处理&#xff08;Thread&#xff09;&#xf…

jenkins ssh 免密报错Host key verification failed.

jenkins 发布项目&#xff0c;ssh连接远程服务器时报错&#xff1a;Host key verification failed. 解决&#xff1a; 原因是生成的sshkey不是用的jenkins用户&#xff0c;所以切换用户到&#xff1a;jenkins重新生成sshkey su jenkins ssh-keygen -t rsa ssh-copy-id -i ~/…

Linux: Shell编程入门

Shell 编程入门 1 ) Shell 概念 shell 是 在英语中 壳, 外壳的意思可以把它想象成嵌入在linux这样的操作系统里面的一个微型的编程语言不像C语言, C 或 Java 等编程语言那么完整&#xff0c;它可以帮我们完成很多自动化任务例如保存数据监测系统的负载等等&#xff0c;我们同样…

MATLAB生物细胞瞬态滞后随机建模定量分析

&#x1f3af;要点 基于随机动态行为受化学主方程控制&#xff0c;定量分析单细胞瞬态效应。确定性常微分方程描述双稳态和滞后现象。通过随机性偏微分方程描述出暂时性滞后会逐渐达到平稳状态&#xff0c;并利用熵方法或截断方法计算平衡收敛速度的估计值。随机定量分析模型使…

什么是字节序、大小端、高低字节、高低地址?

目录 1. 什么是字节序&#xff08;Endianness&#xff09;&#xff1f; 2. 什么是大小端&#xff08;Big-Endians and Little-Endian&#xff09;&#xff1f; 3. 什么时候需要用到大小端的概念&#xff1f; 4. 如何确认系统的大小端模式&#xff1f; 5. 什么是大小端定义…

[LVGL] MessageBox

该例子用lvgl9 来测试&#xff0c;对话框从底部升上来。当点击关闭或者确认按键时&#xff0c;会向绑定对象发送按键事件&#xff0c;参数 100/101. /*** file lv_demo_test.c**/#include "stdio.h" #include "stdlib.h" #include "lvgl.h"#ifde…

985研一,转嵌入式好还是后端开发好?

有个老铁问&#xff0c;985研一&#xff0c;转嵌入式好还是后端开发好&#xff1f; 我认为&#xff0c;这学历&#xff0c;两个随便挑&#xff0c;我说的&#xff0c;从趋势来看&#xff0c;更建议嵌入式&#xff0c;走供应链上游&#xff0c;芯片原厂、新能源车企、军工或者搞…

IDEA自动生成时序图插件-SequenceDiagram

目录 前言介绍安装在线安装离线安装 使用基本使用使用技巧 知识扩展为什么要画时序图&#xff1f;为什么要使用SequenceDiagram插件&#xff1f; 前言 工欲善其事&#xff0c;必先利其器&#xff0c;用对工具&#xff0c;事半功倍。我向大家介绍一款卓越的插件——Sequence Dia…

衡石分析平台系统分析人员手册-展示类控件创建富文本攻略

富文本​ 富文本控件是一种常见的控件&#xff0c;可用来展示文本信息、用户属性信息&#xff0c;在数据分析中起到辅助分析的功能。 富文本常见的使用场景有&#xff1a; 仅展示纯文本信息。在富文本中展示数据集字段、指标、参数等信息。使用富文本展示用户属性相关信息。在…

H3m-Blog

H3m-Blog 一、项目介绍 1.1 项目介绍 一个基于SpringBoot和Vue3的博客系统&#xff0c;博客名称来源于陈奕迅于2009年发布的粤语专辑《H3M》 1.2 技术架构 主要技术栈&#xff1a; SpringBoot2 Vue3 MySQL8.0 1.3 主要功能 内容丰富&#xff0c;尽情体验~ 二、快速开始…

Win11安装基于WSL2的Ubuntu

1. 概述 趁着还没有完全忘记&#xff0c;详细记录一下在Win11下安装基于WSL2的Ubuntu的详细过程。不得不说WSL2现在被微软开发的比较强大了&#xff0c;还是很值得安装和使用的&#xff0c;笔者就通过WSL2安装的Ubuntu成功搭建了ROS环境。 2. 详论 2.1 子系统安装 在Win11搜…

【HarmonyOS NEXT】使用 Navigation 对折叠屏设备页面进行分栏展示,优化 UI 交互

关键词&#xff1a;折叠屏、navigation、router、路由、分栏、UI 随着科技的发展&#xff0c;手机设备形态也由一面屏向多面屏进行发展&#xff0c;那么软件的UI适配也面临着问题&#xff0c;本篇文章主要解决大屏设备的页面 UI 适配问题&#xff0c;如折叠屏&#xff0c;平板&…

MySql数据库中数据类型

本篇将介绍在 MySql 中的所有数据类型&#xff0c;其中主要分为四类&#xff1a;数值类型、文本和二进制类型、时间日期、String 类型。如下&#xff08;图片来源&#xff1a;MySQL数据库&#xff09;&#xff1a; 目录如下&#xff1a; 目录 数值类型 1. 整数类型 2. …

[MoeCTF 2022]endian

查看发现是64位文件&#xff0c;且看到了amd64-64-little&#xff08;这里是小端序&#xff09; 所以我们要反向输入 对于整数 0x12345678&#xff0c;在小端序存储下的内存布局为&#xff1a; 地址 内容 低地址 0x78 0x56 0x34 高地址 0x12 查看main函数&#xff0c…

Python4

4. 更多控制流工具 除了刚介绍的 while 语句&#xff0c;Python 还用了一些别的。我们将在本章中遇到它们。 4.1. if 语句 if elif else if x<0: x 0 print(Negative changed to zero) elif x0: print( zero) else: print(More) 4.2. for 语句 Pyth…

游戏服务器被攻击有办法防护吗

游戏服务器受到攻击时比较常见的。就算是刚上线的游戏&#xff0c;都会有被攻击的时候。游戏服务器受到攻击的原因以及解决方案有哪些呢&#xff1f; 游戏服务器被攻击的原因有哪些呢&#xff1f; 1、常见的攻击&#xff0c;大部分来自于同行之间的恶意竞争&#xff0c;你的游…

Rust 力扣 - 3. 无重复字符的最长子串

文章目录 题目描述题解思路题解代码题目链接 题目描述 题解思路 我们需要记录当前子串的开始下标&#xff0c;一个哈希表记录字符和遍历过程中最后一次出现的索引 遍历字符串&#xff0c;如果在当前字符在哈希表中有记录&#xff0c;并且索引下标大于当前子串的开始下标&…

Lesson12---queue

Lesson12—queue 本篇博客介绍了cqueue的介绍使用以及模拟实现 文章目录 Lesson12---queue前言一、queue的成员函数1 queue2.empty3.size4.front5.back6.push7.pop 二、相关题目三、模拟实现完整代码 四、deque&#xff08;双端队列&#xff09;总结 前言 queue的文档:https:…

go高并发之路——本地缓存

一、使用场景 试想一个场景&#xff0c;有一个配置服务系统&#xff0c;里面存储着各种各样的配置&#xff0c;比如直播间的直播信息、点赞、签到、红包、带货等等。这些配置信息有两个特点&#xff1a; 1、并发量可能会特别特别大&#xff0c;试想一下&#xff0c;一个几十万…