c++初阶篇----string的底层模拟

news/2024/5/15 22:45:41/文章来源:https://blog.csdn.net/qq_69261838/article/details/137150333

string类的模拟

目录

  • string类的模拟
    • 功能介绍
    • 各功能的实现
      • 类的构造函数,拷贝构造函数,析构函数
      • 迭代器的实现
      • string的内部容量访问成员函数
      • string的修改成员函数
      • string类的相关联函数
      • string类的输入输出友元
    • 汇总
      • string功能的实现汇总
      • 测试代码

功能介绍

namespace bit
{class string{private:char* _str;size_t _capacity;size_t _size;public:typedef char* iterator;typedef const char*const_iterator;static const int npos;public:string(){}~string(){}//各个成员函数的实现}
}
const int bit::string::npos = -1;

实现思路:

  • 迭代器的实现{typefef 一个指针,模拟指针,这里用的指针}
  • 先实现构造函数{注意初始化的动态内存开辟}
    这里还要注意如何实现缺省值(赋予nullptr? 赋予空)
    ,拷贝构造函数{开辟字符串长度大小的空间,strcpy内存复制}
  • c_str(){返回数组内容}
  • 运算符重载 拷贝 {设计现代写法,利用临时变量在此函数调用其他已经实现的构造函数,与临时变量交换,实现拷贝。第二种普遍想法,开同等大小的空间(注意实际多出1来存‘/0’),内存复制函数}
  • 析构函数{释放空间,成员变量归零}
  • 扩容函数reserve{若空间不足,new,思想和栈,链表开空间一样,临时变量来做缓冲}
  • 调整字符串大小函数resize{若参数的长度小于当前string的长度,便将[n]位置的数据赋予‘/0’

修改:

  • 尾插push_pack{判断扩容,尾部插入字符}
  • append{扩容,复制拷贝字符串}
  • 插入函数insert(字符串,字符){扩容,对pos位置的字符往后排,在pos位置插入字符,或者插入字符串(strncpy,不会复制空字符)}
  • 擦除erase{在pos位置删除之后的字符串}
  • 交换swap{交换函数模板,各成员变量都交换}
  • 查找find(字符,字符串)
    判断pos位置的合理性,循环比较相等
    字符串需要用到库函数strstr
  • 获取从某个位置开始的字符串substr{临时变量string来储存pos之后的字符串,注意的是,谨慎判断pos与_size和npos之间的关系大小
  • 清除clear{_str[0]置空字符,大小变为零}
  • [ ]

各功能的实现

类的构造函数,拷贝构造函数,析构函数

其中涉及到库函数,内存拷贝的4个库函数

  1. void * memcpy ( void * destination, const void * source, size_t num ); 返回值是destination,此函数是复制内存块,将字节数的值从源指向的位置直接复制到目标指向的内存块,不检查是否source是否有终止符号

  2. void * memmove ( void * destination, const void * source, size_t num ); 返回值是destination,此函数是移动内存块。将 num 字节的值从指向的位置复制到目标指向的内存块。复制就像使用中间缓冲区一样进行,从而允许目标重叠

  3. char * strcpy ( char * destination, const char * source ); 返回值是destination,此函数的复制包括结尾的‘/0’

  4. char * strncpy ( char * destination, const char * source, size_t num ); 返回值是 destination,此函数是复制字符串,复制n个字符至des

  5. 构造,拷贝构造

//不建议的写法
string():_str(new char[1]),_capacity(0),_size(0)
{_str[0] = '/0';
}
string(const char* s):_capacity(strlen(s))
{_str = new char[_capacity + 1];_size = _capacity;strcpy(_str, s);
}
string(const char* str = ""):_capacity(strlen(str))//缺省值不给字符,也就是空字符串
{_str = new char[_capacity + 1];//多开一个空间给‘/0’_size = _capacity;strcpy(_str, str);
}string(const string& s)
{_str = new char[s._capacity + 1];_capacity = s._capacity;_size = s._size;strcpy(_str, s._str);
}string& operator=(const string& s)
{_str = new char[s._capacity + 1];_capacity = s._capacity;_size = s._size;strcpy(_str, s._str);return *this;
}
  1. 现代写法:在实现了一个构造函数,在此基础上调用构造函数来实现另一个构造函数
string& operator=(const string& s)
{//_str = new char[s._capacity + 1];//_capacity = s._capacity;//_size = s._size;//strcpy(_str, s._str);//return *this;string tem(s);swap(tem);//这里交换要注意一开始给成员变量初始值return *this;
}

注意:交换后临时开辟的变量若对成员变量不初始化,临时变量就会变成野指针,在析构可能出现异常

在这里插入图片描述

  1. 析构函数
~string()
{delete[] _str;_str = nullptr;            _size = _capacity = 0;
}

迭代器的实现

string的迭代器这里用的是指针实现,迭代器用指针是比较容易实现的,只需返回指针即可

// iterator
typedef char* iterator;
typedef const char*const_iterator;
const_iterator begin()const
{return _str;
}
const_iterator end()const
{return _str + _size;
}
iterator begin()
{return _str;
}
iterator end()
{return _str + _size;
}

实现了迭代器,那么for循环也可以使用,其实for循环的本质也是用的迭代器的原理,表面是任意变量的拷贝,底层是迭代器的转换

	void test(){bit::string s2("abcdefg");bit::string::iterator begin = s2.begin();while (begin != s2.end()){(*begin)++;cout << *begin;begin++;}cout << endl;for (auto ch : s2){cout << ch;}}

在这里插入图片描述

string的内部容量访问成员函数

  1. string大小容量的实现
// capacity
size_t size()const
{return _size;
}size_t capacity()const
{return _capacity;
}bool empty()const
{return _size == 0;
}
  1. 在实现resize和reserve成员函数,应该充分了解他们的底层逻辑,resize只增容不缩容,若参数大于当前容量,扩容并且以参数char c来填充扩容后的内容。
    reserve是用来为其他成员函数扩容的,不缩容,所有扩容函数构造函数都会多开一个空间来留个‘\0’这个空字符
void resize(size_t n, char c = '\0')
{if (n <= _capacity){_str[n] = '\0';_size = n;}else {reserve(n);for (size_t i = _size;i < n; i++)   {_str[i] = c;}_str[n] = '\0';_size = n;}
}void reserve(size_t n)
{if (n > _capacity){char* tem = new char[n + 1];//多开一个空间strcpy(tem, _str);delete[] _str;_str = tem;_capacity = n;}
}

string的修改成员函数

string的修改成员函数

   modify//void push_back(char c);//string& operator+=(char c);//void append(const char* str);//string& operator+=(const char* str);//void clear();//void swap(string& s);//const char* c_str()const
  1. 实现插入,增加的原理是一样的:
    先判断是否扩容,然后插入字符,或者strcpy插入字符串
 void push_back(char c){if (_size == _capacity){reserve(_capacity == 0 ? 4 : 2 * _capacity);}_str[_size++] = c;_str[_size] = '\0';}string& operator+=(char c){push_back(c);return *this;}void append(const char* str){int len = strlen(str);if(_size+len>_capacity||_size >_capacity - len)//最大值溢出问题{reserve(_size + len);}strcpy(_str + _size, str);_size = _size + len;}string& operator+=(const char* str){append(str);return *this;}string& operator+=(const string s){reserve(_size + s._size);strcpy(_str + _size, s._str);_size = _size + s._size;return *this;}
  1. 清除函数和交换函数也是很容易实现的
    清除成员函数不需要考虑释放空间,只需要将第一个有效字符设置为’\0’即可
    而交换函数可以利用库函数的模板来实现,将各个成员变量进行交换
 void clear(){_str[0] = '\0';_size = 0;}void swap(string& s){std::swap(_str, s._str);std::swap(_capacity, s._capacity);std::swap(_size, s._size);}

在swap中应用中还是不太普遍,需要s1.swap(s2) ,看起来还是不够使用,也不像库函数那样,所以,可以再重载一个全局函数,这里注意是在类域外,全局处重载函数

void swap(string& s1, string& s2)
{s1.swap(s2);
}
  1. 返回字符串函数,返回成员变量_str(开辟空间的地址)即可
const char* c_str()const{return _str;}

string类的相关联函数

  1. find成员函数的实现
// 返回c在string中第一次出现的位置
size_t find(char c, size_t pos = 0) const
{//pos合理assert(pos < _size);for (size_t i = pos;i < _size;i++){if (_str[i] == c)return i;}return npos;
}

find的字符串的实现

这里用到了库函数strstr
const char * strstr ( const char * str1, const char * str2 ); 此函数查找子字符串,若匹配了,返回指针,不匹配返回空指针

// 返回子串s在string中第一次出现的位置
size_t find(const char* s, size_t pos = 0) const
{assert(pos < _size);char* ch = strstr(_str, s);if (ch){//返回俩个指针的差值,就是当前的位置return ch - _str;}else{return npos;}
}

insert插入函数的实现

 在pos位置上插入字符c/字符串str,并返回该字符的位置
string& insert(size_t pos, char c)
{assert(pos <= _size);if (_size == _capacity){reserve(_capacity == 0 ? 4 : 2 * _capacity);}for (size_t i = _size+1;i > pos;i--){_str[i] = _str[i-1];}_str[pos] = c;_size++;return* this;
}string& insert(size_t pos, const char* str)
{assert(pos <= _size);//和append实现思路一样size_t len = strlen(str);if (_size > _capacity - len || _size+len >_capacity )//最大值溢出的问题{reserve(_size + len);}for (size_t i = _size  + len;i > pos + len -1 ;i--){_str[i] = _str[i - len]; }strncpy(_str + pos, str,len);_size = _size + len;return *this;
}

erase擦除函数,将pos位置及后面的总计len个字符擦除

// 删除pos位置上的元素,并返回该元素的下一个位置
string& erase(size_t pos, size_t len=npos)
{assert(pos < _size);//不用取等,最后一个_size位置是不能擦除的,'\0'if (len == npos || len > _size - pos-1){_str[pos] = '\0';_size = pos;}else{strcpy(_str + pos, _str + pos + len);_size -= len;}return *this;
}

string比较函数的实现可以借助库函数的strcmp
int strcmp ( const char * str1, const char * str2 ); 此函数将俩个字符串进行比较,相等返回0 ,str1小于str2返回一个负数(反之)

string类的输入输出友元

注意:此类函数的实现都是基于类域的外面,也就是全局

输出流cout

输入流cin

输入流的实现思路有很多种优化。
很普遍的实现是将输入的字符一个一个塞进string的实例中,一边塞一边扩容 ,获取输入字符可以借助库函数get()

    //istream& operator>>(istream& _cin, bit::string& s)//{//    //清空当前的内容//    s.clear();//    char ch;//    ch = _cin.get();//    s.reserve(64);//    while (ch != ' ' && ch != '\n')//当遇到enter和空格就停止获取//    {//        s.push_back(ch);//        ch = _cin.get();//    }//    return _cin;//}

优化:用一个大小固定的临时数组来存储当前输入字符,当输入结束或者数组已满,就赋值给string,将数组的有效字符重置,继续下一轮(如果还有输入字符)

istream& operator>>(istream& _cin, bit::string& s)
{s.clear();char ch;ch = _cin.get();char c[128];size_t i = 0;while (ch != ' ' && ch != '\n'){c[i++] = ch;//当数组满时if (i == 127){c[127] = '\0';//注意在这个位置赋予结束字符,否则会出现随机字符s += c;i = 0;}ch = _cin.get();}if (i > 0){c[i] = '\0';//注意先给结束字符,否则会将上一次循环的已无效字符或随机字符,+=进入strings += c;}return _cin;
}

汇总

string功能的实现汇总

#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<assert.h>
using namespace std;namespace bit
{class string{private:char* _str = nullptr;size_t _capacity = 0;size_t _size = 0;friend ostream& operator<<(ostream& _cout, const bit::string& s);friend istream& operator>>(istream& _cin, bit::string& s);public:typedef char* iterator;typedef const char*const_iterator;static const int npos;public://string() :_str(new char[1]), _capacity(0), _size(0)//{//    _str[0] = '/0';//}//string(const char* s) :_capacity(strlen(s))//{//    _str = new char[_capacity + 1];//    _size = _capacity;//    strcpy(_str, s);//}string(const char* str = ""):_capacity(strlen(str))//缺省值不给字符,也就是空字符串{_str = new char[_capacity + 1];//多开一个空间给‘/0’_size = _capacity;strcpy(_str, str);}string(const string& s){_str = new char[s._capacity + 1];_capacity = s._capacity;_size = s._size;strcpy(_str, s._str);}string& operator=(const string& s){//_str = new char[s._capacity + 1];//_capacity = s._capacity;//_size = s._size;//strcpy(_str, s._str);//return *this;string tem(s);swap(tem);//这里交换要注意一开始给成员变量初始值return *this;}//俩种现代写法,在完成其他成员函数的基础上~string(){delete[] _str;_str = nullptr;            _size = _capacity = 0;}// iteratorconst_iterator begin()const{return _str;}const_iterator end()const{return _str + _size;}iterator begin(){return _str;}iterator end(){return _str + _size;}// modifyvoid push_back(char c){if (_size == _capacity){reserve(_capacity == 0 ? 4 : 2 * _capacity);}_str[_size++] = c;_str[_size] = '\0';}string& operator+=(char c){push_back(c);return *this;}void append(const char* str){size_t len = strlen(str);if (_size >_capacity - len||_size+len>_capacity)//最大值溢出问题{reserve(_size + len);}strcpy(_str + _size, str);_size = _size + len;}string& operator+=(const char* str){append(str);return *this;}string& operator+=(const string s){reserve(_size + s._size);strcpy(_str + _size, s._str);_size = _size + s._size;return *this;}void clear(){_str[0] = '\0';_size = 0;}void swap(string& s){std::swap(_str, s._str);std::swap(_capacity, s._capacity);std::swap(_size, s._size);}const char* c_str()const{return _str;}// capacitysize_t size()const{return _size;}size_t capacity()const{return _capacity;}bool empty()const{return _size == 0;}void resize(size_t n, char c = '\0'){if (n <= _capacity){_str[n] = '\0';_size = n;}else {reserve(n);for (size_t i = _size;i < n; i++)   {_str[i] = c;}_str[n] = '\0';_size = n;}}void reserve(size_t n){if (n > _capacity){char* tem = new char[n + 1];strcpy(tem, _str);delete[] _str;_str = tem;_capacity = n;}} accesschar& operator[](size_t index){return *(_str + index);}const char& operator[](size_t index)const{return *(_str + index);}//relational operatorsbool operator<(const string& s){if (strcmp(c_str(), s.c_str()) < 0){return true;}return false;}bool operator<=(const string& s){return *this == s || *this < s;}bool operator>(const string& s){return !(*this <= s);}bool operator>=(const string& s){return !(*this < s);}bool operator==(const string& s){if (strcmp(c_str(), s.c_str()) == 0)return true;return false;}bool operator!=(const string& s){return !(*this == s);}// 返回c在string中第一次出现的位置size_t find(char c, size_t pos = 0) const{//pos合理assert(pos < _size);for (size_t i = pos;i < _size;i++){if (_str[i] == c)return i;}return npos;}// 返回子串s在string中第一次出现的位置size_t find(const char* s, size_t pos = 0) const{assert(pos < _size);char* ch = strstr(_str, s);if (ch){//返回俩个指针的差值,就是当前的位置return ch - _str;}else{return npos;}} 在pos位置上插入字符c/字符串str,并返回该字符的位置string& insert(size_t pos, char c){assert(pos <= _size);if (_size == _capacity){reserve(_capacity == 0 ? 4 : 2 * _capacity);}for (size_t i = _size+1;i > pos;i--){_str[i] = _str[i-1];}_str[pos] = c;_size++;return* this;}string& insert(size_t pos, const char* str){assert(pos <= _size);//和append实现思路一样size_t len = strlen(str);if (_size > _capacity - len || _size+len >_capacity )//最大值溢出的问题{reserve(_size + len);}for (size_t i = _size  + len;i > pos + len -1 ;i--){_str[i] = _str[i - len]; }strncpy(_str + pos, str,len);_size = _size + len;return *this;}// 删除pos位置上的元素,并返回该元素的下一个位置string& erase(size_t pos, size_t len=npos){assert(pos < _size);//不用最后一个pos是不能擦除的,‘\0'if (len == npos || len > _size - pos-1){_str[pos] = '\0';_size = pos;}else{strcpy(_str + pos, _str + pos + len);_size -= len;}return *this;}};const int bit::string::npos = -1;void swap(string& s1, string& s2){s1.swap(s2);}ostream& operator<<(ostream& _cout, const bit::string& s){for (auto ch : s){_cout << ch;}return _cout;}//istream& operator>>(istream& _cin, bit::string& s)//{//    //清空当前的内容//    s.clear();//    char ch;//    ch = _cin.get();//    s.reserve(64);//    while (ch != ' ' && ch != '\n')//当遇到enter和空格就停止获取//    {//        s.push_back(ch);//        ch = _cin.get();//    }//    return _cin;//}istream& operator>>(istream& _cin, bit::string& s){s.clear();char ch;ch = _cin.get();char c[128];size_t i = 0;while (ch != ' ' && ch != '\n'){c[i++] = ch;//当数组满时if (i == 127){c[127] = '\0';//注意在这个位置赋予结束字符,否则会出现随机字符s += c;i = 0;}ch = _cin.get();}if (i > 0){c[i] = '\0';//注意先给结束字符,否则会将上一次循环的已无效字符或随机字符,+=进入strings += c;}return _cin;}
}

测试代码

#include"imitate_string.h"void test1()
{//bit::string s1;bit::string s2("abcdefg");bit::string::iterator begin = s2.begin();while (begin != s2.end()){(*begin)++;cout << *begin;begin++;}cout << endl;for (auto ch : s2){cout << ch;}//cout << s2[2] << endl;
}void test2()
{bit::string s1("abcdefg");//s1.resize(4);//s1.resize(5, 'i');//cout << s1.c_str() << endl;//bit::string s2;//s2.resize(10, '1');//s1.push_back('2');s1.append("you donot know .... ,just nothing");cout << s1.c_str() << endl;
}void test3()
{bit::string s1;bit::string s2("ijustlovebutitwillbenothing");bit::string s3 = s2;s3 += s2;s2 += "itjustnothing OK";cout << s3.c_str() << endl;cout << s2.c_str() << endl;s1.swap(s2);cout << s1.c_str() << endl;s1.clear();cout << s1.c_str() << endl;
}void test4()
{bit::string s1("abadfadf");//bit::string s2("it just be ok,i think i only gave up");//swap(s1, s2);//bit::string s3 = s2;//cout << s1.c_str() << endl << s2.c_str() <<s3.c_str()<< endl;
}void test5()
{bit::string s1("abadfadf");bit::string s2("ac");size_t i = s1.find('b');//s1.insert(i, '5');//s2.insert(0,"12345");s1.erase(i, 2);s2.erase(0);s2 = s1;s2.erase(3);cout << s1.c_str() << endl;cout << s2.c_str();
}void test6()
{bit::string s1("qwert");bit::string s2("qwert");bit::string s3("qert");//cout << (s1 < s2) << endl;//0//cout << (s1 >= s2) << endl;//1//cout << (s3 <= s2) << endl;//1cout << s1 << endl;cin >> s2;cout << s2;bit::swap(s2, s1);cout << s1;}int main()
{//using namespace bit;//bit::string s1();//bit::string s2("abcdefg");//test5();//test6();bit::string s1("abka");cin >> s1;//s1.append("ab");cout << s1;return 0;
}

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

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

相关文章

151 shell编程,正则表达式,在C语言中如何使用正则表达式

零&#xff0c;坑点记录&#xff1a;bash 和 dash 的区别&#xff0c;导致的坑点 查看当前用的shell 是啥&#xff0c;用的是/bin/bash hunandedehunandede-virtual-machine:~$ echo $SHELL /bin/bash 当shell 脚本运行的时候&#xff08;后面会学到方法&#xff0c;这里是最…

Django屏蔽Server响应头信息

一、背景 最近我们被安全部门的漏洞扫描工具扫出了一个服务端口的漏洞。这个服务本身是一个Django启动的web服务&#xff0c;并且除了登录页面&#xff0c;其它页面或者接口都需要进行登录授权才能进行访问。 漏洞扫描信息和提示修复信息如下: 自然这些漏洞如何修复&#xff0c…

图论- 最小生成树

一、最小生成树-prim算法 1.1 最小生成树概念 一幅图可以有很多不同的生成树&#xff0c;比如下面这幅图&#xff0c;红色的边就组成了两棵不同的生成树&#xff1a; 对于加权图&#xff0c;每条边都有权重&#xff08;用最小生成树算法的现实场景中&#xff0c;图的边权重…

每天五分钟深度学习:使用神经网络完成人脸的特征点检测

本文重点 我们上一节课程中学习了如何利用神经网络对图片中的对象进行定位,也就是通过输出四个参数值bx、by、bℎ和bw给出图片中对象的边界框。 本节课程我们学习特征点的检测,神经网络可以通过输出图片中对象的特征点的(x,y)坐标来实现对目标特征的识别,我们看几个例子。…

java 溯本求源之基础(八)之 jar(下篇)

上篇中我们介绍了 Java 类加载顺序、JAR 命令的使用以及 MANIFEST.MF 文件的作用。Java 类加载顺序包括 Bootstrap classes、Extension classes 和 Class Path。JAR 命令是一个归档和压缩工具&#xff0c;用于打包 Java 应用程序。MANIFEST.MF 文件存储打包文件的元信息&#x…

Midjourney AI绘图工具介绍及使用

介绍 Midjourney是一款目前被誉为最强的AI绘图工具。只要输入想到的文字&#xff0c;就能通过人工智能产出相对应的图片。 官网只是宣传和登录入口&#xff0c;提供个人主页、订阅管理等功能&#xff0c;Midjourney实际的绘画功能&#xff0c;是在另外一个叫discord的产品中实…

关于未来自我的发展和一些学习方法(嵌入式方向)

我是一名大二的学生&#xff0c;考研还是就业&#xff0c;到底是重视专业课还是重视数学英语&#xff0c;这些问题一直困扰了我很久&#xff0c;但如今已经有了一些浅显的认识&#xff0c;所以才会想写这样一篇文章来记录一下自己的状态和未来的规划 下面的看法都是个人的看法&…

Day26 手撕各种集合底层源码(一)

Day26 手撕各种集合底层源码&#xff08;一&#xff09; 一、手撕ArrayList底层源码 1、概念&#xff1a; ArrayList的底层实现是基于数组的动态扩容结构。 2、思路&#xff1a; 1.研究继承关系 2.研究属性 3.理解创建集合的过程 – 构造方法的底层原理 4.研究添加元素的过程…

wpf 自定义命令

自定义命令 MyCommand.cs public class MyCommand : ICommand {private readonly Action<Object> execAction;private readonly Func<Object,bool> changedFunc;public event EventHandler? CanExecuteChanged;public MyCommand(Action<object> execAction…

离线linux服务器安装mysql8

本文的服务器环境&#xff1a;openEuler毛坯版的&#xff0c;很多常用的指令都没有预装&#xff0c;比如rpm、tar等等&#xff0c;没有网络坏境&#xff0c;需要自己手动配置本地yum仓库&#xff0c;安装相关指令 1、检查服务器是否已经安装了MySQL 1.1、查询mysql以安装的相关…

NRF52832修改OTA升级时的bootloader蓝牙MAC

NRF52832在OTA升级时&#xff0c;修改了APP的蓝牙MAC会导致无法升级&#xff0c;原因是OTA程序的蓝牙MAC没有被修改所以手机扫描蓝牙时无法连接 解决办法 在bootloader的程序里面加入修改蓝牙mac地址的代码实现原理&#xff1a; 在bootloader蓝牙广播开启之前修改蓝牙mac 通…

无人车网关案例:记SV900无人清扫车网关的现场应用

​随着无人驾驶技术的不断发展,无人车辆已经开始广泛应用于物流配送、环境保洁、巡逻监控等众多领域,助力城市运营更加高效智能。而要实现无人车辆的安全可靠运行,关键在于选择一款性能卓越的车载网络通信系统.在这一背景下,星创易联推出了SV900无人车网关系列产品。它集5G/4G网…

算法打卡day17

今日任务&#xff1a; 1&#xff09;654.最大二叉树 2&#xff09;617.合并二叉树 3&#xff09;700.二叉搜索树中的搜索 4&#xff09;98.证二叉搜索树 654.最大二叉树 题目链接&#xff1a;654. 最大二叉树 - 力扣&#xff08;LeetCode&#xff09; 给定一个不含重复元素的整…

计算机网络数据链路层知识总结

物理层知识总结传送门 计算机网络物理层知识点总结-CSDN博客 功能 功能概述 一些基本概念 结点:主机、路由器链路﹔网络中两个结点之间的物理通道&#xff0c;链路的传输介质主要有双绞线、光纤和微波。分为有线链路、无线链路。数据链路︰网络中两个结点之间的逻辑通道&a…

HarmonyOS实战开发-如何实现一个自定义抽奖圆形转盘

介绍 本篇Codelab是基于画布组件、显式动画&#xff0c;实现的一个自定义抽奖圆形转盘。包含如下功能&#xff1a; 通过画布组件Canvas&#xff0c;画出抽奖圆形转盘。通过显式动画启动抽奖功能。通过自定义弹窗弹出抽中的奖品。 相关概念 Stack组件&#xff1a;堆叠容器&am…

STM32第十节(中级篇):EXTI(第一节)——EXTI功能框图及初始化结构体讲解(包括STM32中断应用总结)

目录 前言 STM32第十节&#xff08;中级篇&#xff09;&#xff1a;EXTI&#xff08;第一节&#xff09;——EXTI功能框图及初始化结构体讲解&#xff08;包括STM32中断应用总结&#xff09; EXTI功能框图 EXTI初始化结构体讲解 STM32中断应用总结 NVIC介绍 优先级 优先…

后端常问面经之并发

volatile 关键字 volatile关键字是如何保证内存可见性的&#xff1f;底层是怎么实现的&#xff1f; "观察加入volatile关键字和没有加入volatile关键字时所生成的汇编代码发现&#xff0c;加入volatile关键字时&#xff0c;会多出一个lock前缀指令”lock前缀指令实际上相…

Radash一款JavaScript最新的实用工具库,Lodash的平替!

文章目录 Lodash 的痛点进入正题--Radash特点 举例几个常用的api 一说lodash应该大部分前端同学都知道吧&#xff0c;陪伴我们好多年的JavaScript工具库&#xff0c;但是自从 ES6 出现后就慢慢退出前端人的视线&#xff0c;能ES6写的代码绝对不会用Lodash&#xff0c;也不是完全…

C#预处理器指令(巨细版)

文章目录 一、预处理器指令的基本概念二、预处理器指令的基本规则三、C# 预处理器指令详解3.1 #define 和 #undef3.2 #if、#else、#elif 和 #endif3.3 #line3.4 #error 和 #warning3.5 #region 和 #endregion 四、高级应用&#xff1a;预处理器指令的最佳实践4.1 条件编译的最佳…

PS从入门到精通视频各类教程整理全集,包含素材、作业等复发(2)

PS从入门到精通视频各类教程整理全集&#xff0c;包含素材、作业等 最新PS以及插件合集&#xff0c;可在我以往文章中找到 由于阿里云盘有分享次受限制和文件大小限制&#xff0c;今天先分享到这里&#xff0c;后续持续更新 初级教程素材 等文件 https://www.alipan.com/s/fC…