string类的理解以及模拟实现

news2024/10/1 9:44:49

string类的理解

  • 为什么需要学习string类
  • 标准库中的string类
    • string类简单了解
    • string类常见接口
  • string模拟实现
  • 深浅拷贝问题
  • 标准库下的string
    • VS环境下
    • g++环境下

为什么需要学习string类

在C语言中,字符串和字符串相关的函数是分开的,不太符合面向对象的思想,封装性太低,同时需要用户自己管理底层空间,稍不留神可能就会越界访问;
为此,C++在这些问题的基础上提出了string类;

标准库中的string类

string类简单了解

string类文档介绍;

  1. string类也是利用模板实例化出来的一个具体类,string本身并不是模板;
    在这里插入图片描述
  2. 字符串是表示字符序列的类
  3. 标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作
    单字节字符字符串的设计特性。
  4. string类是使用char(即作为它的字符类型,使用它的默认char_traits和分配器类型(关于模板的更多信
    息,请参阅basic_string)。
  5. string类是basic_string模板类的一个实例,它使用char来实例化basic_string模板类,并用char_traits
    和allocator作为basic_string的默认参数(根于更多的模板信息请参考basic_string)。
  6. 注意,这个类独立于所使用的编码来处理字节:如果用来处理多字节或变长字符(如UTF-8)的序列,这个
    类的所有成员(如长度或大小)以及它的迭代器,将仍然按照字节(而不是实际编码的字符)来操作。
    总结:
    1.string是表示字符串的字符串类
    2.该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。
    3.string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator>string;
    4.不能操作多字节或者变长字符的序列。

string类常见接口

(constructor)
1、string();//无参构造函数,利用空串构造string类对象
在这里插入图片描述
2、string(const char*str);//利用C语言字符串构造string类对象;
在这里插入图片描述
3、string(const string &s);//拷贝构造函数
在这里插入图片描述
4、string(size_t n,char c);//利用n个c字符来构造string类对象
在这里插入图片描述

(destructor)
~string();//析构函数

operator=
1、string&operator=(const string&s);//string对象赋值给string对象;
在这里插入图片描述
2、string&operator=(const char*str);//C语言字符串赋值给string对象;
在这里插入图片描述
3、string&operator=(char ch);//C字符给string赋值;
在这里插入图片描述

Capacity:
1、size();//返回字符串长度;
2、length();//返回字符串长度
size与length在底层实现上是一样的,早期的string是不在容器之内的,求字符长度是用的length,多出来一个size主要是为了与后面string加入容器队伍过后名称的统一!
在这里插入图片描述
3、capacity();//返回当前的字符数组的容量;
在这里插入图片描述、4、empty();//判断string对象是否为空串;是:true;不是:false;
在这里插入图片描述
5、clear();//清除有效字符,size置0,容量不变
在这里插入图片描述
6、reserev(size_t n=0);//请求更改容量,若n大于当前已开辟的容量,那么就开辟n个空间(或则更多,vs环境下就是这样,Linux:g++下就是要多少开多少);若n小于等于当前已开辟的容量,编译器不做处理;此操作不会更改size的大小;
在这里插入图片描述
7、resize(size_t n,char c=‘\0’)//重新调整字符串有效字符的大小;如果n大于当前size的大小,那么当前对象可能会发生扩容,size=n,新出来的空间利用指定字符c来填充;若n小于当前的大小,那么size=n,将当前对象的有效字符缩短置n个,capacity不变;
在这里插入图片描述

iterator:
begin();//返回开头的指针
end();//返回最后一个元素的下一个位置的指针;
在这里插入图片描述
我们可以利用迭代器完成字符串遍历的操作;
rbegin();//反向迭代器,返回最后一个元素的指针;
rend();//反向迭代器,返回第一个元素的前一个位置的指针;
反向迭代器也就是反着遍历:
在这里插入图片描述
begin/end有重载,rbegin/rend也有重载
主要是重载的const对象;非const对象调用begin/end,rbegin/rebd;const对象调用begin()const/end()const,rbegin()const/rend()const;
在这里插入图片描述
当然在C++11中const对象,调用正向迭代器,可以用cbegin/cend;
反向迭代调用crbegin/crend;
按照非const对象与const对象来分:
非const对象调用begin/end,rbegin/rend;
const对象调用cbegin/cend,crbegin。crend;
按照正向迭代器、反向迭代器来分:
begin/end可以被非const对象和const对象调用;
rbegin/rend可以被非const对象和const对象调用;
注意:要注意调用的迭代器类型要匹配;
迭代器总的来说有四种:iterator(非const对象正向迭代器)、const_iterator(const对象正向迭代器);
reverse_iterator(非const对象反向迭代器)、const_reverse_iterator(const对象反向迭代器);

Element access:
operator[](size_t pos);//重载运算符[]//这是利用assert的方式来检查越界
在这里插入图片描述
at(size_t pos);//使用at成员函数访问//这是利用抛出异常的方式来检查越界
在这里插入图片描述

Modifiers:
operator+=(const string&s);//追加一个string对象
operator+=(const char*s);//追加一个C字符串
operator+=(char ch);//追加一个字符;
在这里插入图片描述
append(const string&s);
append(const char*s);
append(size_t n.char c);
在这里插入图片描述
push_back(char c);//插入一个字符
在这里插入图片描述
pop_back();//删除最后一个字符
insert(size_t pos,const string&s);//pos位置插入一个string对象
insert(size_t pos,const char*s);//pos位置插入C字符串
insert(size_t pos,size_t n ,char c);//pos位置插入n个指定字符;
erase (size_t pos = 0, size_t len = npos)//从pos位置开始删除len个字符;
swap (string& str);//此swap是在string类内部实现的,与模板swap不一样;
我们可思考一下,为什么要在string内部实现实现一个swap呢?模板swap不够我们用吗?
当然不是,两个swap都能用,但是在使用上存在效率问题,string内部的swap效率要高于模板swap;
下面我们通过画图来解释:
在这里插入图片描述
String operations:
const char* c_str() const;//将我们的string转换为C字符串
size_t find(const string &str,size_t pos=0);//从pos位置开始寻找str字符串,找到了,返回其所在位置,找不到返回npos;
size_t find(const char *s,size_t pos=0);
size_t find( char ch,size_t pos=0);

Non-member function overloads:
relational operators:
在这里插入图片描述
getline();
在这里插入图片描述
从istream流获取字符串,以指定字符delim作为分隔符;delim默认是’\n’;

string模拟实现

string.h

#pragma once
#pragma warning(disable:4996)
#include<iostream>
#include<assert.h>
#include<string.h>
namespace MySpace
{
	class string//字符串末尾需要存储'\0',但是'\0'不算做_capacity的大小
	{
	//	friend std::ostream& operator<<(std::ostream& cout, const string& str);
		friend std::istream& operator>>(std::istream& cin, string& str);
	public:

		//string()///无参构造//根据STL的表现,string对象会被初始化为一个空串
		//:_size(0),_capacity(0)
		//{
		//	_str = new char[1];//这里为什么不用new char?答:虽然语法支持这样做,但是考虑到析构函数的统一写法:delete[],建议用new[]开空间
		//	_str[0] = '\0';
		//}
		
		//对于无参构造函数和有参构造函数,我们可以考虑利用缺省值
		string(const char* str = "")
			:_size(strlen(str))
		{
			_capacity = _size + 4;//这里可以考虑多开一点,保持capacity不为0;//不一定要多少开开多少
			//可以多开4个空间
			_str = new char[_capacity + 1];//这里需要将'\0'的空间考虑进来,但是_capacity不记录'\0'的空间
			strcpy(_str, str);
		}

		//拷贝构造//使用编译器默认的会发生浅拷贝问题
		string(const string& str)
			:_size(str._size), _capacity(str._capacity)
		{
			_str = new char[_capacity + 1];
			strcpy(_str, str._str);
		}

		//析构
		~string()
		{
			delete[] _str;
			_size = _capacity = 0;
		}

		//赋值运算符,不能用编译器提供的,编译器提供的是浅拷贝
		string& operator=(const string& str)
		{
			if (this != &str)
			{
				char* tmp = new char[str._capacity + 1];//开辟失败,抛出异常,不会丢失原数据
		//开辟成功
				strcpy(tmp, str._str);
				_size = str._size;
				_capacity = str._capacity;
				delete[]_str;
				_str = tmp;
			}
			return *this;
		}

		string& operator=(const char* str)
		{
			if (str != _str)
			{
				resize(0);
				*this += str;
			}
			return *this;
		}

		string& operator=(char ch)
		{
			resize(0);
			*this += ch;
			return *this;
		}

		//字符串长度
		size_t size()const
		{
			return _size;
		}

		//清理有效字符
		void clear() {
			_size = 0;
			_str[_size] = '\0';
		}

		//判断是否是空串
		bool empty()const
		{
			return _size == 0;
		}

		//获取当前的有效存储容量
		size_t capacity()const
		{
			return _capacity;
		}

		//[]访问
		//1、非const对象
		char& operator[](size_t pos)
		{
			assert(pos < _size);
			return _str[pos];
		}

		//2、const对象
		const char& operator[](size_t pos)const//返回值是没必要加const的,因为const对象的const是修饰_str的不是修饰*_str的
		{
			assert(pos < _size);
			return _str[pos];
		}

		//比较运算符
		bool operator>(const string& str)const
		{
			size_t l1 = 0;
			size_t l2 = 0;
			while (l1 <= _size && l2 <= str._size)
			{
				if (_str[l1] == str._str[l2])
				{
					l1++;
					l2++;
				}
				else if (_str[l1] > str._str[l2])
					return true;
				else
					return false;
			}
			return false;
		}

		bool operator==(const string& str)const
		{
			if (_size != str._size)
				return false;
			else
			{
				size_t l1 = 0;
				size_t l2 = 0;
				while (l1 <= _size && l2 <= str._size)
				{
					if (_str[l1] == str._str[l2])
					{
						l1++;
						l2++;
					}
					else
						return false;
				}
				return true;
			}
		}

		bool operator<(const string& str)const
		{
			return !(*this > str || *this == str);
		}

		bool operator>=(const string& str)const
		{
			return !(*this < str);
		}

		bool operator<=(const string& str)const
		{
			return !(*this > str);
		}

		bool operator!=(const string& str)const
		{
			return !(*this == str);
		}

		//string转换成C字符串
		const char* c_str() const
		{
			return _str;
		}

		//迭代器
		//1、非const对象
		typedef char* iterator;
		iterator begin()
		{
			return _str;
		}

		iterator end()
		{
			return _str + _size;
		}

		//2、const对象
		typedef const char* const_iterator;
		const_iterator begin()const
		{
			return _str;
		}
		const_iterator end()const
		{
			return _str + _size;
		}

		//重设size的大小
		void resize(size_t n, char ch = '\0')
		{
	
		  if (n > _size && n <= _capacity)
			{
				memset(_str+_size,ch,(n-_size)*sizeof(char));
			}
			else if(n>_capacity)
			{
				reserve(n);
				memset(_str+_size,ch,(n-_size)*sizeof(char));
			}
			_str[n] = '\0';
			_size = n;
		}

		//pos位置插入字符
		string& insert(size_t pos, char ch)
		{
			assert(pos<=_size);
			if (_size + 1 > _capacity)
			{
				reserve(_capacity * 2);
			}
      size_t end=_size+1;
      while(end>=pos+1)
      {
        _str[end]=_str[end-1];
        end--;
      }
      _str[pos]=ch;
      _size++;
			return *this;
		}

		//pos位置插入字符串
		string& insert(size_t pos, const char* str)
		{
			assert(pos <= _size);
			size_t len = strlen(str);
			if (len + _size > _capacity)
			{
				reserve(len + _size);
			}
      size_t end=_size+len;
      while(end>=pos+len)
      {
        _str[end]=_str[end-len];
        end--;
      }
      strncpy(_str+pos,str,len);
      _size+=len;
			return *this;
		}

		//从pos位置开始删除len个字符
		string& erase(size_t pos = 0, size_t len = npos)
		{
			assert(empty() != true && pos < _size);
			int left = pos;
			int right = pos+len-1;
      if(len==npos||pos+len>=_size)
        right=_size-1;
			int end = right + 1;
			while (end <= _size)
			{
				_str[end - (right - left + 1)] = _str[end];
				end++;
			}
			_size -= (right - left + 1);
			return *this;
		}

		//交换
		void swap(string& str)
		{
			std::swap(_str,str._str);
			std::swap(_size,str._size);
			std::swap(_capacity,str._capacity);
		}

		//扩容,重新设置有效容量
		void reserve(size_t n = 0)
		{
			//开空间,拷数据
			if (n > _capacity)
			{
				char* tmp = new char[n + 1];
				strcpy(tmp, _str);
				delete[]_str;
				_str = tmp;
				_capacity = n;
			}
		}

		//插入字符
		void push_back(char ch)
		{
			//检查容量
			if (_size +1> _capacity)//容量已满,扩二倍
			{
				reserve(_capacity*2);
			}
			_str[_size] = ch;
			_str[_size + 1] = '\0';//'\0'需要我们自己手动插入
			_size++;
		}

		//追加字符串
		string& append(const char* str)
		{
			size_t len = strlen(str);
			const char* sour = str;
			char* dest = _str;//先记录一下原来的指针
			if (_size + len > _capacity)//需要扩容
			{
				reserve(_size+len);
			}
			//strcpy(_str+_size,str);//这里不建议用strcat,因为strcat会去找_str的\0,这是O(N)的消耗,没必要
			//这里用strcpy的话会造成自己给自己追加不能完成
			//可是如果考虑到自己给自己追加的话,strcpy也会不妥,会发生无穷拷贝
			if (dest == sour)//dest==str,说明是自己给自己追加,由于已经开好了空间,之前的空间也就被释放了,sour也就是个也
				sour = _str;//野指针,我们需要更换sour的指向
			memmove(_str+_size,sour,len*sizeof(char));
			_size += len;
			_str[_size] = '\0';
			return *this;
		}

		//重载+=运算符
		string& operator+=(char ch)
		{
			push_back(ch);
			return *this;
		}

		string& operator+=(const char* str)
		{
			append(str);
			return *this;
		}

		string& operator+=(const string& str)
		{
			append(str._str);
			return *this;
		}

    //从pos位置开始查找字符c第一次出现的位置
    size_t find(char c,size_t pos=0)const
    {
        size_t i=pos;
        for(i=pos;i<_size;i++)
        {
          if(_str[i]==c)
            return i;
        }
        return npos;
    }
    int *GetNext(const char*p)const 
    {
      int len=strlen(p);
      int *next=new int[len];
      next[0]=-1;
      int j=0;
      int k=-1;
        while(j<len)
        {
          if(-1==k||p[k]==p[j])
          {
            next[j+1]=k+1;
            k=next[j+1];
            j++;
          }
          else{
            k=next[k];
          }
        }
        return next;
    }
    //从pos位置开始查找字串第一次出现的位置
    size_t find(const char*str,size_t pos=0)const
    {
      
      int i=pos;
      int j=0;
      int len1=_size;
      int len2=strlen(str);
        if(len2==0)
          return 0;
        int *next=GetNext(str);//根据字串获的next数组
      while(i<len1&&j<len2)
      {
        while(j==-1||(i<len1&&j<len2&&_str[i]==str[j]))
        {
          i++;
          j++;
        }
        if(j>=len2)
        {
          return i-j;
        }
        else
        {
          j=next[j];
        }
      }
      return npos;
    }
	private:
		char* _str;
		size_t _size;//记录当前已经存储的字符个数
		size_t _capacity;//记录当前能存储的字符的空间,不包含最后以'\0'结尾字符串的空间,但是实际我们需要将这个空间开出来;
	public:
		static const size_t npos;//与STL保持一致
	};

	size_t const string::npos = -1;

	//重载<<运算符
	std::ostream& operator<<(std::ostream& cout, const string& str)
	{
		//cout << str._str << std::endl;
    string::const_iterator it=str.begin();
    while(it!=str.end())
    {
    std::cout<<*it;
    it++;
    }
		return cout;
	}

	std::istream& operator>>(std::istream& cin, string& str)
	{
		//char tmp[1000];
		//std::cin >> tmp;
		//str = tmp;
		//return cin;
   // char ch;
   //ch= cin.get();
   //while(ch!=' '&&ch!=9&&ch!='\n')
   //{
   //  str+=ch;
   // ch=cin.get();
   srvar cin>>ch;
   //}
   char buff[128];
   int i=0;
   char ch;
  ch= cin.get();
   while(ch!=' '&&ch!=9&&ch!='\n')
   {
     buff[i++]=ch;
     if(i==127)
     {
       buff[127]='\0';
       str+=buff;
       i=0;
     }
     ch=cin.get();
   }
   if(i)
   {
     buff[i]='\0';
     str+=buff;
   }
   return cin;
	}
void test7()
{
  string str1="Hello World";
  size_t index=str1.find("ld",10);
  std::cout<<index<<std::endl;

}
	void test4()
	{
		//string str1;
		//str1 += '*';
		//str1 += "hahaha";
		//string str2 = "999";
		//str1 += str2;
	/*	str1.reserve(100);
		str1.reserve(10);
		str1.push_back('a');
		str1.append("hahahaha");*/
		string str1 = "Hello World";
		std::cin >> str1;
		string str2 = "jkjj";
		str1.swap(str2);
		//str1.erase();
		//str1.insert(5,"YY");
		//str1.resize(5);
		str1.resize(20,'*');
	}
	
	//Void test3()
	//{
	//	string str1 = "Hello World!";
	//	//string::iterators it = str1.begin();
	//	for (auto it : str1)
	//	{
	//	std::cout << it << std::endl;
	//	}
	//}
	void test1()
	{
		string str1;
		string str2("Hahaha");
		/*std::cout << str1 << std::endl;
		std::cout << str2 << std::endl;*/
		std::cout << str1.c_str() << std::endl;//cout打印char*时不会及那个char*当作地址来打印;
		//而是将char*当作字符串首元素地址来打印,也就是将char*指针当作字符串来打印,遇到\0停止;
		std::cout << str2.c_str() << std::endl;
		//std::cout << (char*)nullptr << std::endl;
		
		string str3 = str2;
		std::cout << str3 << std::endl;

		string str4="dfdhbvdawovhewvabkjawbvjewbovnoeiwhvbejw clcKJFHEBWKLVNWEAUOGBUB";
		str4 = str3;
		std::cout << str4 << std::endl;
		std::cout << str4.size() << std::endl;

	}
	void test2()
	{
		//const string str1 = "Hello World!";
		//for (size_t i = 0; i < str1.size(); i++)
		//{
		//	//str1[i]++;
		//	std::cout << str1[i] << " ";
		//}
		//std::cout << std::endl;
		string str1 = "abccd";
		string str2 = "fgwaghbre ewAR";
		std::cout << str1.capacity() << std::endl;
		std::cout << str1.size() << std::endl;

		
	/*	if (str1 == str2)
		{
			std::cout << "str1==str2" << std::endl;
		}
		else
		{
			std::cout << "str1!=str2" << std::endl;
		}*/
	}
	void test5()
	{
		string str1;
		string str2 = "Hello World!";
		string str3 = str2;
		std::cout << (str1+=str2+=str2 )<< std::endl;
		std::cout << "--------------" << std::endl;
		std::cout << str2.capacity() << std::endl;
		std::cout << "--------------" << std::endl;
		std::cout << str3.capacity() << std::endl;
		std::cout << "--------------" << std::endl;

	}
void test6()
{
 // string str1="xxxxxxxxx";
   // str1.erase(1,4);
   // std:: cout<<str1<<std::endl;
   // std::cout<<str1;
   
  string str="";
//  str+='\0';
 // str+='\0';
  //srvtr+='\0';
//  str+='\0';
//tmpstr+="yyyyyyyyy";
  std::cin>>str;
std::cout<<str.size()<<std::endl;
  std::cout<<str<<std::endl;
}
}

深浅拷贝问题

请移步至博客:C++深浅拷贝

标准库下的string

VS环境下

为了演示方便,以下演示都是在64位平台下验证:
VS平台下,string总共占28个字节,内部结构稍微复杂 ,先是有一个联合体,联合体用来定义string中字符串的存储空间:
当字符串长度小于等于15时,使用内部固定的字符数组来存放
当字符串长度大于15时,从堆上开辟空间;

union _Bxty
{ // storage for small buffer or pointer to larger one
value_type _Buf[_BUF_SIZE];
pointer _Ptr;
char _Alias[_BUF_SIZE]; // to permit aliasing
} _Bx;

这种设计也是有一定道理的,大多数情况下字符串的长度都小于16,那string对象创建好之后,内
部已经有了16个字符数组的固定空间,不需要通过堆创建,效率高。
其次:还有一个size_t字段保存字符串长度,一个size_t字段保存从堆上开辟空间总的容量
最后:还有一个指针做一些其他事情。
故总共占16+4+4+4=28个字节。
在这里插入图片描述

g++环境下

g++环境下string的总共占4/8字节,g++环境下的string是通过指针来实现的,该指针指向一块空间:
在这里插入图片描述

lenth:字符串长度;
capacity:字符串长度;
refcount:引用计数;
像这样实现string,在拷贝和赋值方面效率会很高,为什么这么说呢?
在这里插入图片描述
那么g++为什么要这么做?
因为如果每次都老老实实的完成拷贝和赋值的话(也就是向VS环境下的string)是需要代价的,我们需要完成从另一个空间将数据拷贝到其他空间,这是一个不小的时间消耗,但是g++向这样设计的话,就不需要进行数据的拷贝,只需要完成string内部指针的赋值即可,更加高效!
那么如果是多个对象指向同一块空间的话,万一我想对某个对象进行修改或删除操作呢?
在这里插入图片描述

那么这时候我们就不能在str1与str2共享的空间上直接进行修改,因为如果我们对其直接进行修改,str1对象上的数据也会跟着被修改!这不是我们所希望的,遇到这种情况,g++提供了“写时拷贝的技术” 就是当我们需要对str2对象进行修改数据的操作时,OS会在内存重新开辟一块空间给str2,然后让str2在新空间上进行修改操作,这样就避免了修改str2的数据时误修改了str1的数据!
在这里插入图片描述
g++的这种作法其实就是一种赌徒思想,它在赌用户不会进行修改数据的操作,赌对了它就赚了,赌错了,就老老实实开辟空间,然后拷贝数据!

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

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

相关文章

在线视频加密播放与防下载该如何考虑?

在线视频加密播放与防下载该如何考虑&#xff1f; ▲ 图 / 防录屏随机水印 1. 视频加密&#xff08;分片加密&#xff09;VRM加密&#xff1a; 将视频进行切片、对碎片逐一进行混淆式加密&#xff0c;包括AES128加密、XOR加密、关键帧错序等。 2. 防录屏&#xff08;用名信息I…

IM即时通讯开发如何解决大量离线消息导致客户端卡顿的

大部分做后端开发的朋友&#xff0c;都在开发接口。客户端或浏览器h5通过HTTP请求到我们后端的Controller接口&#xff0c;后端查数据库等返回JSON给客户端。大家都知道&#xff0c;HTTP协议有短连接、无状态、三次握手四次挥手等特点。而像游戏、实时通信等业务反而很不适合用…

一个Laravel+vue免费开源的基于RABC控制的博客系统

项目介绍 CCENOTE 是一个使用 Vue3 Laravel8 开发的前后端分离的基于RABC权限控制管理的内容管理系统&#xff0c;由于作者本人比较喜欢写作的原因&#xff0c;因此开发了这个项目&#xff0c;后端使用的PHP的Laravel框架&#xff0c;并且整理了数据层与业务层&#xff0c;相…

node环境搭建以及接口的封装

node环境搭建 文章目录node环境搭建1.在cmd中输入命令安装express&#xff08;全局&#xff09;2.在自己的项目下安装serve3.测试接口4.连接mysql4.1 创建数据表4.2 在serve目录下建db下的sql.js4.3 sql.js4.4 在serve路径下安装mysql4.5 在routes 中引入并发送请求4.6 请求到数…

一文3000字从0到1教你用python+selenium搭建UI自动化测试环境以及使用

一、什么是Selenium &#xff1f; Selenium 是一个浏览器自动化测试框架&#xff0c;它主要用于web应用程序的自动化测试&#xff0c;其主要特点如下&#xff1a;开源、免费&#xff1b;多平台、浏览器、多语言支持&#xff1b;对web页面有良好的支持&#xff1b;API简单灵活易…

STM32CubeMX串口USART中断发送接收数据

本文代码使用 HAL 库。 文章目录前言一、中断控制二、USART中断使用1. 中断优先级设置 &#xff1a;2. 使能中断3. 使能UART的发送、接收中断4. 中断收发函数5. 中断处理函数6. 中断收发回调函数三、串口中断实验串口中断发送数据点亮 led&#xff1a;实验现象&#xff1a;总结…

excel图表制作:旋风图让数据对比更直观

旋风图是我们工作中最常用的数据对比图表。旋风图中两组图表背靠背&#xff0c;纵坐标同向&#xff0c;横坐标反向。今天我们就跟大家分享两种制作旋风图的方式。如下表所示&#xff0c;我们以某平台各主要城市的男女粉丝数据为例&#xff0c;制作旋风图来对比男女用户情况。一…

中级嵌入式系统设计师2016下半年下午应用设计试题

中级嵌入式系统设计师2016下半年下午试题 试题一 阅读以下说明,回答问题1至问题3。 【说明】 某综合化智能空气净化器设计以微处理器为核心,包含各种传感器和控制器,具有检测环境空气参数(包含温湿度、可燃气体、细颗粒物等),空气净化、加湿、除湿、加热和杀菌等功能…

7、算法MATLAB ---(运算符)(语句)

运算符&语句1.关系运算符2.逻辑运算符3. if...else 控制语句4. for循环5. While循环6.控制循环退出的关键字6.1 Break6.2 Continue6.3 Return1.关系运算符 ">"大于 ">"大于等于 "<"小于 "<"小于等于 ""等于…

【第六章 JdbcTemplate概念和准备,JdbcTemplate操作数据库(添加)】

第六章 JdbcTemplate概念和准备&#xff0c;JdbcTemplate操作数据库&#xff08;添加&#xff09; 1.JdbcTemplate&#xff08;概念和准备&#xff09; &#xff08;1&#xff09;spring框架对jdbc进行封装&#xff0c;使用JdbcTemplate方便实现对数据库操作。 &#xff08;2&…

数睿通2.0数据服务功能模块发布

文章目录引言API 目录API 权限API 日志结语引言 数睿通 2.0 之前基本完成了数据集成和数据开发两大模块&#xff0c;也因此得到了一些朋友的帮助和支持&#xff0c;在此由衷的表示感谢&#xff0c;你们的支持便是我们更新的最大动力&#xff01; 目前&#xff0c;数据服务模块…

10种聚类算法的完整python操作示例

大家好&#xff0c;聚类或聚类分析是无监督学习问题。它通常被用作数据分析技术&#xff0c;用于发现数据中的有趣模式&#xff0c;例如基于其行为的客户群。有许多聚类算法可供选择&#xff0c;对于所有情况&#xff0c;没有单一的最佳聚类算法。相反&#xff0c;最好探索一系…

【内网安全】——Linux权限维持

作者名&#xff1a;白昼安全主页面链接&#xff1a; 主页传送门创作初心&#xff1a; 以后赚大钱座右铭&#xff1a; 不要让时代的悲哀成为你的悲哀专研方向&#xff1a; web安全&#xff0c;后渗透技术每日鸡汤&#xff1a; 钱至少对于现在的我来说&#xff0c;的确是万能的在…

04 方法与函数

Scala 中的也有方法和函数的概念。 Scala中的 方法 是类的一部分。 Scala中的 函数 是一个对象&#xff0c;可以赋值给变量。 在类中定义的函数就是方法 4.1 方法 Scala 中方法 与 Java 中类似&#xff0c;是组成类的一部分 4.1.1 语法结构 格式&#xff1a; def 方法名([参…

程序员是否要加入创业公司?

我从1月份入职到2月份离职&#xff0c;历时一个半月。短暂的体验了一段创业生活&#xff0c;更准确的说是一段“待在”创业团队的生活&#xff0c;因为我发现创业本身跟我关系不大。一个半月的就业经历&#xff0c;对任何人来说都不是一个好选择&#xff0c;当然也不是我所期望…

[oeasy]python0094_视频游戏_双人网球_pong_atari_mos_6502_雅达利_米洛华

编码进化 回忆上次内容 上次 我们回顾了 微软之前的 比尔盖茨和保罗艾伦 mits 迎来的 是帮手还是隐患&#xff1f; intel-8080 遇到了 mos-6502 底层硬件 驱动 游戏行业进化 不光是扑克牌和柏青哥了出现了双人网球 不过 目前的游戏 PDP-1 上的《太空大战》Donner Model 30 上…

K8S---pod基础概念

目录 一、资源限制 二、Pod 的两种使用方式 三、Pod 资源共享 四、底层容器Pause 1、Pause共享资源 1.1 网络 1.2 存储 1.3 小结 2、Pause主要功能 3、Pod 与 Pause 结构的设计初衷 五、Pod容器的分类 1、基础容器&#xff08;infrastructure container&#xff09;…

C++ Primer Plus 第6版 读书笔记(2)第2章 开始学习 C++

C是在 C 语言基础上开发的一种集面向对象编程、泛型编程和过程化编程于一体的编程语言&#xff0c;是C语言的超集。本书是根据2003年的ISO/ANSI C标准编写的&#xff0c;通过大量短小精悍的程序详细而全面地阐述了 C的基本概念和技术&#xff0c;并专辟一章介绍了C11新增的功能…

企业网站自动生成系统的设计和实现

技术&#xff1a;Java、JSP等摘要&#xff1a;随着Internet技术的发展&#xff0c;人们的日常生活已经离不开网络。未来社会人们的生活和工作将越来越依赖于数字技术的发展&#xff0c;越来越数字化、网络化、电子化、虚拟化。Internet的发展历程以及目前的应用状况和发展趋势&…

BigGAN

1、BIGGAN 解读1.1、作者 Andrew Brock、Jeff Donahue、Karen Simonyan 1.2、摘要 尽管最近在生成图像建模方面取得了进展&#xff0c;但从 ImageNet 等复杂数据集中 成功生成高分辨率、多样化的样本仍然是一个难以实现的目标。为此&#xff0c;我们以迄 今为止最大的规模训练生…