C++中的字符串实现

news2024/12/25 1:35:13

短字符串优化(SSO)

实现1

在这里插入图片描述

实现2

在这里插入图片描述

写时复制

在这里插入图片描述

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstring>
using std::cout;
using std::endl;

// 引用计数存放的位置
// 1. 存放在栈上 --- 不行
// 2. 存放在全局静态区 --- 不行
// 3. 只能放在堆上
class String
{
public:
	String();
	String(const char* pstr);
	String(const String& rhs);
	String& operator=(const String& rhs);
	~String();
	const char* c_str() const;
	int getRefCount()const;
	friend std::ostream& operator<<(std::ostream& os, const String& rhs);
	char& operator[](size_t index);
private:
	size_t size()const;
private:
	char* _pstr;
};
String::String() :_pstr(new char[5] + 4) 
{// 5: 引用计数+‘\0’  4:让其直接指向数据的位置
	//cout << "String()" << endl;
	*(int*)(_pstr - 4) = 1;
}
String::String(const char* pstr):_pstr(new char[strlen(pstr) + 5]()+4)/*_pstr(new char(strlen(pstr)+1))*/
{
	//cout << "String::String(const char* pstr)" << endl;
	strcpy(_pstr,pstr);
	(*(int*)(_pstr - 4)) = 1;
}
// String s2(s1)
String::String(const String& rhs):_pstr(rhs._pstr) // 浅拷贝
{
	//cout << "String::String(const String& rhs)" << endl;
	(*(int*)(_pstr - 4))++;
}
String& String::operator=(const String& rhs)
{
	if (&rhs != this)   //1. 自赋值
	{
		// 2. 释放左操作
		(*(int*)(_pstr - 4))--;
		if (0 == (*(int*)(_pstr - 4)))
		{
			delete[](_pstr - 4);
		}
		// 3. 深拷贝(浅拷贝)
		_pstr = rhs._pstr;
		(*(int*)(_pstr - 4))++;
	}
	return *this;
}
String::~String()
{
	cout << "~String()" << endl;
	(*(int*)(_pstr - 4))--;
	if (0 == (*(int*)(_pstr - 4)))
	{
		delete[](_pstr - 4);
	}
}
const char* String::c_str() const
{
	return _pstr;
}
int String::getRefCount() const
{
	return *(int*)(_pstr - 4);
}
std::ostream& operator<<(std::ostream& os, const String& rhs)
{
	if (rhs._pstr != NULL) {
		os << rhs._pstr << endl;
	}
	return os;
}
size_t String::size()const
{
	return strlen(_pstr);
}
char& String::operator[](size_t index)
{
	if (index > size()) {
		static char charnull = '\0';
		return charnull;
	}
	else {
		if (getRefCount() > 1) { // 考虑是不是共享的
			// 执行深拷贝
			int len = size();
			char* tmp = new char[len+5]()+4;
			strcpy(tmp,_pstr);

			(*(int*)(_pstr - 4))--;  // 注意这里不需要看是否等于0,因为getRefCount() > 1
			_pstr = tmp;
			(*(int*)(_pstr - 4)) = 1;
			/*return _pstr[index];*/ // 这个要写到括号之外,可能这个字符串没有被共享
		}
		return _pstr[index];
	}
}

//std::ostream& operator<<(std::ostream& os, char ch)
//{
//	os << "std::ostream& operator<<(std::ostream& os, char ch)" << endl;
//	os << ch;
//	return os;
//}
int main(void)
{
	String s1("hello");
	String s2(s1);
	cout << "s1 = " << s1 << endl;
	cout << "s2 = " << s2 << endl;
	cout << "s2 RefCount= " << s2.getRefCount() << endl;
	cout << "s2 RefCount= " << s2.getRefCount() << endl;

	printf("s1`address = %p\n",s1.c_str());
	printf("s2`address = %p\n",s2.c_str());

	String s3("world");
	cout << "s3 = " << s3 << endl;
	cout << "s3 RefCount= " << s3.getRefCount() << endl;

	cout << endl << "使用s3对s1进行赋值操作" << endl;
	s3 = s1;
	cout << "s1 = " << s1 << endl;
	cout << "s2 = " << s2 << endl;
	cout << "s3 = " << s3 << endl;
	cout << "s1 RefCount= " << s1.getRefCount() << endl;
	cout << "s2 RefCount= " << s2.getRefCount() << endl;
	cout << "s3 RefCount= " << s3.getRefCount() << endl;

	printf("s1`address = %p\n", s1.c_str());
	printf("s2`address = %p\n", s2.c_str());
	printf("s3`address = %p\n", s3.c_str());

	cout << endl << endl << "对s3[0]执行写操作" << endl;
	s3[0] = 'H';
	cout << "s1 = " << s1 << endl;
	cout << "s2 = " << s2 << endl;
	cout << "s3 = " << s3 << endl;
	cout << "s1 RefCount= " << s1.getRefCount() << endl;
	cout << "s2 RefCount= " << s2.getRefCount() << endl;
	cout << "s3 RefCount= " << s3.getRefCount() << endl;
	printf("s1`address = %p\n", s1.c_str());
	printf("s2`address = %p\n", s2.c_str());
	printf("s3`address = %p\n", s3.c_str());

	/*如何区分是对[] 读还是写
		cout<<s[1]  是进行的读操作;  可以对[]进行重载
		s[1] = '1'  是进行的写操作;  可以对=进行重载
	*/
	// 读操作
	//cout << s1[1] << endl;
	return 0;
}
  • 如何区分什么什么时候读,什么时候写

    • 通过一个中间类型CharProxy,并且重载他的operator=和operator<<函数
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstring>
using std::cout;
using std::endl;

// 引用计数存放的位置
// 1. 存放在栈上 --- 不行
// 2. 存放在全局静态区 --- 不行
// 3. 只能放在堆上
class String
{
public:
	String();
	String(const char* pstr);
	String(const String& rhs);
	String& operator=(const String& rhs);
	~String();
	const char* c_str() const;
	int getRefCount()const;
	friend std::ostream& operator<<(std::ostream& os, const String& rhs);
private:
	class CharProxy {
	public:
		CharProxy( String& self, size_t idx) :_self(self), _idx(idx) {}
		char& operator = (const char& ch); // 写操作
		/*{
			_self[_idx] = ch;
			return _self[_idx];
		}*/
		friend std::ostream& operator<<(std::ostream& os, const CharProxy& ch);
	private:
		String &_self; // 此时String是不完整类型
		size_t _idx;
	};
public:
	friend std::ostream& operator<<(std::ostream& os, const CharProxy& ch);
	//char& operator[](size_t index);
	CharProxy  operator[](size_t idx)
	{
		return CharProxy(*this,idx);
	}
private:
	void initRefCount();
	void increaseRefCount();
	void decreaseRefCount();
	void release();
	size_t size()const;
private:
	char* _pstr;
};
String::String() :_pstr(new char[5] + 4) 
{// 5: 引用计数+‘\0’  4:让其直接指向数据的位置
	//cout << "String()" << endl;
	initRefCount();
}
String::String(const char* pstr):_pstr(new char[strlen(pstr) + 5]()+4)/*_pstr(new char(strlen(pstr)+1))*/
{
	//cout << "String::String(const char* pstr)" << endl;
	strcpy(_pstr,pstr);
	//(*(int*)(_pstr - 4))--;
	initRefCount();
}
// String s2(s1)
String::String(const String& rhs):_pstr(rhs._pstr) // 浅拷贝
{
	//cout << "String::String(const String& rhs)" << endl;
	//(*(int*)(_pstr - 4))++;
	increaseRefCount();
}
String& String::operator=(const String& rhs)
{
	if (&rhs != this)   //1. 自赋值
	{
		// 2. 释放左操作
		//(*(int*)(_pstr - 4))--;
		/*decreaseRefCount();
		if (0 == (*(int*)(_pstr - 4)))
		{
			delete[](_pstr - 4);
		}*/
		release();
		// 3. 深拷贝(浅拷贝)
		_pstr = rhs._pstr;
		//(*(int*)(_pstr - 4))++;
		increaseRefCount();
	}
	return *this;
}
String::~String()
{
	//cout << "~String()" << endl;
	//(*(int*)(_pstr - 4))--;
	//decreaseRefCount();
	//if (0 == (*(int*)(_pstr - 4)))
	//{
	//	delete[](_pstr - 4);
	//}
	release();
}
void String::initRefCount()
{
	*(int*)(_pstr - 4) = 1;
}
void String::increaseRefCount()
{
	(*(int*)(_pstr - 4))++;
}
void String::decreaseRefCount()
{
	(*(int*)(_pstr - 4))--;
}
void String::release()
{	
	decreaseRefCount();
	if (0 == (*(int*)(_pstr - 4)))
	{
		delete[](_pstr - 4);
	}
}
const char* String::c_str() const
{
	return _pstr;
}
int String::getRefCount() const
{
	return *(int*)(_pstr - 4);
}
std::ostream& operator<<(std::ostream& os, const String& rhs)
{
	if (rhs._pstr != NULL) {
		os << rhs._pstr << endl;
	}
	return os;
}
size_t String::size()const
{
	return strlen(_pstr);
}
char& String::CharProxy::operator = (const char& ch) // 写操作
{
	if (_idx > _self.size()) 
	{
		static char charnull = '\0';
		return charnull;
	}
	else 
	{
		if (_self.getRefCount() > 1) { // 考虑是不是共享的
			// 执行深拷贝
			int len = _self.size();
			char* tmp = new char[len+5]()+4;
			strcpy(tmp, _self._pstr);
			//(*(int*)(_pstr - 4))--;  // 注意这里不需要看是否等于0,因为getRefCount() > 1
			_self.decreaseRefCount();
			_self._pstr = tmp;
			_self.initRefCount();
			/*return _pstr[index];*/ // 这个要写到括号之外,可能这个字符串没有被共享
		}
		_self._pstr[_idx] = ch;
		return _self._pstr[_idx];
	}
}
//双友元的设置
std::ostream& operator<<(std::ostream& os, const String::CharProxy& rhs)
// 注意这个函数既要左CharProxy的友元也要作为String的友元(不然上面的代码const String::CharProxy& ch处会报错)
{
	os << rhs._self._pstr[rhs._idx];
	return os;
}
//char& String::operator[](size_t index)
//{
//	if (index > size()) {
//		static char charnull = '\0';
//		return charnull;
//	}
//	else {
//		if (getRefCount() > 1) { // 考虑是不是共享的
//			// 执行深拷贝
//			int len = size();
//			char* tmp = new char[len+5]()+4;
//			strcpy(tmp,_pstr);
//
//			//(*(int*)(_pstr - 4))--;  // 注意这里不需要看是否等于0,因为getRefCount() > 1
//			decreaseRefCount();
//			_pstr = tmp;
//			//(*(int*)(_pstr - 4)) = 1;
//			initRefCount();
//			/*return _pstr[index];*/ // 这个要写到括号之外,可能这个字符串没有被共享
//		}
//		return _pstr[index];
//	}
//}

//std::ostream& operator<<(std::ostream& os, char ch)
//{
//	os << "std::ostream& operator<<(std::ostream& os, char ch)" << endl;
//	os << ch;
//	return os;
//}
int main(void)
{
	String s1("hello");
	String s2(s1);
	cout << "s1 = " << s1 << endl;
	cout << "s2 = " << s2 << endl;
	cout << "s2 RefCount= " << s2.getRefCount() << endl;
	cout << "s2 RefCount= " << s2.getRefCount() << endl;

	printf("s1`address = %p\n",s1.c_str());
	printf("s2`address = %p\n",s2.c_str());

	String s3("world");
	cout << "s3 = " << s3 << endl;
	cout << "s3 RefCount= " << s3.getRefCount() << endl;

	cout << endl << "使用s3对s1进行赋值操作" << endl;
	s3 = s1;
	cout << "s1 = " << s1 << endl;
	cout << "s2 = " << s2 << endl;
	cout << "s3 = " << s3 << endl;
	cout << "s1 RefCount= " << s1.getRefCount() << endl;
	cout << "s2 RefCount= " << s2.getRefCount() << endl;
	cout << "s3 RefCount= " << s3.getRefCount() << endl;

	printf("s1`address = %p\n", s1.c_str());
	printf("s2`address = %p\n", s2.c_str());
	printf("s3`address = %p\n", s3.c_str());

	cout << endl << endl << "对s3[0]执行写操作" << endl;
	s3[0] = 'H';
	// 首先会调用s1.operator[](1)返回一个CharProxy对象,然后调用CharProxy的operatpr=()函数
	cout << "s1 = " << s1 << endl;
	cout << "s2 = " << s2 << endl;
	cout << "s3 = " << s3 << endl;
	cout << "s1 RefCount= " << s1.getRefCount() << endl;
	cout << "s2 RefCount= " << s2.getRefCount() << endl;
	cout << "s3 RefCount= " << s3.getRefCount() << endl;
	printf("s1`address = %p\n", s1.c_str());
	printf("s2`address = %p\n", s2.c_str());
	printf("s3`address = %p\n", s3.c_str());

	/*如何区分是对[] 读还是写
		cout<<s[1]  是进行的读操作;  可以对[]进行重载
		s[1] = '1'  是进行的写操作;  可以对=进行重载
		s[1] = '1'   .  
		char = char 两个char是不能被重载的,因为 运算符重载的第一条规则,参数必须要有一个自定义类型或者enum
		CharProxy = char就可以重载了	
		
		可以先重载一下[],让他返回一个CharProxy对象,然后重载CharProxy的operator=和operator<<函数
		CharProxy operator[](size idx);
		CharProxy = char;
	*/
	// 读操作
	cout << endl << endl << "对s1[1]执行读操作操作" << endl;
	cout << s1[1] << endl; 
	// 首先会调用s1.operator[](1)返回一个CharProxy对象,然后调用CharProxy的operator<<()函数
	cout << "s1 = " << s1 << endl;
	cout << "s2 = " << s2 << endl;
	cout << "s3 = " << s3 << endl;
	cout << "s1 RefCount= " << s1.getRefCount() << endl;
	cout << "s2 RefCount= " << s2.getRefCount() << endl;
	cout << "s3 RefCount= " << s3.getRefCount() << endl;
	printf("s1`address = %p\n", s1.c_str());
	printf("s2`address = %p\n", s2.c_str());
	printf("s3`address = %p\n", s3.c_str());
	return 0;
}


//https://www.bilibili.com/video/BV1VM4m12754?t=1190.2&p=58

上面会出现,双友元声明,可以只重载operator=,不重载<< 和一个类型转换函数

  • //双友元的设置
    std::ostream& operator<<(std::ostream& os, const String::CharProxy& rhs)
    // 注意这个函数既要左CharProxy的友元也要作为String的友元(不然上面的代码const String::CharProxy& ch处会报错)
    {
    	os << rhs._self._pstr[rhs._idx];
    	return os;
    }
    
    // cout<<s1[1]  CharProxy-> char
    
  • 改为

    • String::CharProxy::operator char()  // 注意类型转换函数在类外的定义的形式operator char都放在最后
      {
      	return _self._pstr[_idx];
      }
      

string的实现

#include<iostream>
#include<cstring>
#include<vector>
using std::cout;
using std::endl;

class String 
{
	friend bool operator==(const String& lhs, const String& rhs);
	friend bool operator!=(const String& lhs, const String& rhs);
	friend bool operator>(const String& lhs, const String& rhs);
	friend bool operator<(const String& lhs, const String& rhs);
	friend bool operator<=(const String& lhs, const String& rhs);
	friend bool operator>=(const String& lhs, const String& rhs);
	friend std::ostream& operator<<(std::ostream& os, const String& s);
	friend std::istream& operator>>(std::istream& is, String& s);
public:
	String();
	String(const char* pstr);
	String(const String& rhs);
	String& operator=(const String& rhs);
	String& operator=(const char* pstr);
	String& operator+=(const String& rhs);
	String& operator+=(const char* pstr);
	char& operator[](std::size_t index); // 非const调用
	char& operator[](std::size_t index) const;// const对象调用
	std::size_t size()const;
	const char* c_str()const;
private:
	char* _pstr;
	
};
std::size_t String::size()const
{
	return strlen(this->_pstr);
}
const char* String::c_str()const
{
	return this->_pstr;
}
String::String() :_pstr(nullptr)  // 后面的操作需要判空
{
	cout << "String()" << endl;
}
String::String(const char* pstr) :_pstr(new char[strlen(pstr)+1]())
{
	strcpy(this->_pstr,pstr);
}

String::String(const String& rhs) :_pstr(new char[strlen(rhs._pstr) + 1])
{
	cout << "String(const String& rhs)" << endl;
	strcpy(this->_pstr, rhs._pstr);
}

String& String::operator=(const String& rhs)
{
	if (&rhs != this)
	{
		delete[] this->_pstr;
		this->_pstr = nullptr;
		this->_pstr = new char[strlen(rhs.c_str() + 1)]();
		strcpy(this->_pstr, rhs.c_str());
	}
	
	return *this;
}
String& String::operator=(const char* pstr)
{
	String tmp(pstr);
	*this = tmp;  // 调用String的operator=()函数。
	return tmp;
}
String& String::operator+=(const String& rhs)
{
	int len = this->size() + rhs.size() + 1;
	char* p = new char[len + 1] ();
	strcpy(p,this->c_str());
	strcat(p,rhs.c_str());
	delete[] this->_pstr;
	this->_pstr = p;
	return *this;
}
String& String::operator+=(const char* pstr)
{
	//char* p = new char[this->size()+strlen(pstr) + 1]();
	//strcpy(p, this->c_str());
	//strcat(p, pstr);
	//delete[] this->_pstr;
	//this->_pstr = p;
	String tmp(pstr);
	*this += tmp; // 调用operator+=(const String& rhs)
	return *this;
}
char& String::operator[](std::size_t index)
{
	if (index < this->size())
	{
		return this->_pstr[index];
	}
	else {
		static char nullchar = '\0';
		return nullchar;
	}
}
char& String::operator[](std::size_t index) const
{
	if (index < this->size())
	{
		return this->_pstr[index];
	}
	else {
		static char nullchar = '\0';
		return nullchar;
	}
}






bool operator==(const String& lhs, const String& rhs)
{
	return !strcmp(lhs.c_str(),rhs.c_str());
}
bool operator!=(const String& lhs, const String& rhs)
{
	return strcmp(lhs.c_str(), rhs.c_str());
}

bool operator>(const String& lhs, const String& rhs)
{
	return strcmp(lhs.c_str(),rhs.c_str());
}
bool operator<(const String& lhs, const String& rhs)
{
	return strcmp(lhs.c_str(), rhs.c_str());
}
bool operator<=(const String& lhs, const String& rhs)
{
	return strcmp(lhs.c_str(), rhs.c_str());
}
bool operator>=(const String& lhs, const String& rhs) 
{
	return strcmp(lhs.c_str(), rhs.c_str());
}

std::ostream& operator<<(std::ostream& os, const String& s)
{
	os << s.c_str();
}
std::istream& operator>>(std::istream& is, String& s)
{
	if (s._pstr)
	{
		delete s._pstr;
		s._pstr = nullptr;
	}
	std::vector<char> buffer;
	char ch;
	while ((ch = is.get()) != '\n')
	{
		buffer.push_back(ch);
	}
	s._pstr = new char[buffer.size() + 1]();
	strncpy(s._pstr,&buffer[0],buffer.size());
	return is;
}

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

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

相关文章

clickhouse解决suspiciously many的异常

1. 问题背景 clickhouse安装在虚拟机上&#xff0c;持续写入日志时&#xff0c;突然关机&#xff0c;然后重启&#xff0c;会出现clickhouse可以正常启动&#xff0c;但是查询sql语句&#xff0c;提示suspiciously many异常&#xff0c;如图所示 2. 问题修复 touch /data/cl…

JVM系列(十三) -常用调优工具介绍

最近对 JVM 技术知识进行了重新整理&#xff0c;再次献上 JVM系列文章合集索引&#xff0c;感兴趣的小伙伴可以直接点击如下地址快速阅读。 JVM系列(一) -什么是虚拟机JVM系列(二) -类的加载过程JVM系列(三) -内存布局详解JVM系列(四) -对象的创建过程JVM系列(五) -对象的内存分…

Y3编辑器教程7:界面编辑器

文章目录 一、简介1.1 导航栏1.2 画板1.3 场景界面1.4 控件1.4.1 空节点1.4.2 按钮1.4.3 图片1.4.4 模型1.4.5 文本1.4.6 输入框1.4.7 进度条1.4.8 列表 1.5 元件1.5.1 简介1.5.2 差异说明1.5.3 元件实例的覆盖、还原与禁止操作1.5.4 迷雾控件 1.6 属性1.7 事件&#xff08;动画…

重温设计模式--状态模式

文章目录 状态模式&#xff08;State Pattern&#xff09;概述状态模式UML图作用&#xff1a;状态模式的结构环境&#xff08;Context&#xff09;类&#xff1a;抽象状态&#xff08;State&#xff09;类&#xff1a;具体状态&#xff08;Concrete State&#xff09;类&#x…

MySQL的分析查询语句

【图书推荐】《MySQL 9从入门到性能优化&#xff08;视频教学版&#xff09;》-CSDN博客 《MySQL 9从入门到性能优化&#xff08;视频教学版&#xff09;&#xff08;数据库技术丛书&#xff09;》(王英英)【摘要 书评 试读】- 京东图书 (jd.com) MySQL9数据库技术_夏天又到了…

题解 洛谷 Luogu P1135 奇怪的电梯 广度优先搜索 BFS C/C++

题目传送门&#xff1a; P1135 奇怪的电梯 - 洛谷 | 计算机科学教育新生态https://www.luogu.com.cn/problem/P1135思路&#xff1a; 一道比较裸的 BFS&#xff0c;就是把走迷宫每次搜周围相邻四格&#xff0c;改成了楼层每次搜上下方向的某层而已 感觉这个题难度只有普及- …

git 删除鉴权缓存及账号信息

在Windows系统下 清除凭证管理器中的Git凭据 按下Win R键&#xff0c;打开“运行”对话框&#xff0c;输入control&#xff0c;然后回车&#xff0c;打开控制面板。在控制面板中找到“用户账户”&#xff0c;然后点击“凭据管理器”。在凭据管理器中&#xff0c;找到“Windows…

2024 高频 Java 面试合集整理 (1000 道附答案解析)

2024 年马上就快要过去了&#xff0c;总结了上半年各类 Java 面试题&#xff0c;初中级和中高级都有&#xff0c;包括 Java 基础&#xff0c;JVM 知识面试题库&#xff0c;开源框架面试题库&#xff0c;操作系统面试题库&#xff0c;多线程面试题库&#xff0c;Tcp 面试题库&am…

【图书介绍】】几本Linux C\C++编程图书

Linux C\C编程&#xff0c;是IT领域比较稳定的职业发展方向&#xff0c;本文介绍几本Linux开发方面的图书。 《Linux C与C一线开发实践&#xff08;第2版&#xff09;》 《Linux C与C一线开发实践&#xff08;第2版&#xff09;&#xff08;Linux技术丛书&#xff09;》(朱文…

git命令恢复/还原某个文件、删除远程仓库中的文件

有时刚创建的远程仓库&#xff0c;可能无意中把一些没用的文件上传到仓库&#xff0c;本文介绍一下怎么删除这些文件。 一、git命令恢复某个文件 第一步&#xff1a;拉取最新代码 git pull 第二步&#xff1a; 查看git 修改的文件状态 git status 第三步&#xff1a;查看…

shell脚本定义特殊字符导致执行mysql文件错误的问题

记得有一次版本发布过程中有提供一个sh脚本用于一键执行sql文件&#xff0c;遇到一个shell脚本定义特殊字符的问题&#xff0c;sh脚本的内容类似以下内容&#xff1a; # 数据库ip地址 ip"127.0.0.1" # 数据库密码 cmdbcmdb!#$! smsm!#$!# 执行脚本文件&#xff08;参…

redis数据转移

可能有时候因为硬件的原因我们我们需要更换服务器&#xff0c;如果更换服务器的话&#xff0c;那我们redis的数据该怎样转移呢&#xff0c;按照一下步骤即可完成redis数据的转移 1.进入redis客户端 2.使用 bgsave命令进行数据的备份&#xff0c;此命令完成后会在你的redis安装目…

stm32引脚模式GPIO

问题引入 stm32f103定时器的引脚GPIO_MODE_OUTPUT_PP和GPIO_MODE_AF_PP有什么区别&#xff1f; 在STM32F103微控制器中&#xff0c;使用定时器时涉及到的GPIO配置主要有两种模式&#xff1a;GPIO_MODE_OUTPUT_PP和GPIO_MODE_AF_PP。这两种模式的主要区别在于它们的用途和工作…

Krita安装krita-ai-diffusion工具搭建comfyui报错没有ComfyUI_IPAdapter_plus解决办法

我们在使用Kirta安装krita-ai-diffusion工具之后搭建comfyui环境需要安装很多扩展文件。 一般正常安装都可以使用了。 但是有一个插件很特别,无论你安装多少遍都会显示缺失,是什么插件这么难搞定呢? 没错,就是我们的ComfyUI_IPAdapter_plus插件。 就像下图一样: 那么怎…

WordPress源码解析-数据库表结构

WordPress是一个功能强大的内容管理系统&#xff0c;它使用MySQL数据库来存储和管理网站的内容、用户和配置信息。作为WordPress开发者&#xff0c;了解WordPress数据库的结构和各表的作用至关重要&#xff0c;因为这将帮助您更好地开发插件和主题&#xff0c;以及执行高级数据…

【项目实践】SpringBoot Nacos配置管理 map数据

本文是继续上一篇博客SpringBoot使用Nacos进行application.yml配置管理_nacos application配置-CSDN博客 目录 一、踩坑记录 二、亲测成功 方法一 方法二 一、踩坑记录 参考网上的一些案例&#xff0c;看到网上有人建议 Nacos 可以直接配置成 Map 类型&#xff0c;后台使用…

XILINX平台LINUX下高速ADC08060驱动

前置调研 原理图 AXI-FULL时序 由于项目需要实时性高&#xff0c;采用AXI-FULL接口ADC IP作为master端写入DDR中 引用&#xff1a; AXI_02 AXI4总线简介&#xff08;协议、时序&#xff09;_axi4总线时序-CSDN博客 AXI总线的访问 在ARM架构中&#xff0c;访问I/O地址通常通…

【C语言】指针数组、数组指针、函数指针、指针函数、函数指针数组、回调函数

【C语言】函数指针与指针函数 文章目录 [TOC](文章目录) 前言一、指针数组二、数组指针三、函数指针四、指针函数五、函数指针数组六、回调函数七、参考资料总结 前言 使用工具&#xff1a; 1.DEVC 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、…

华为IPD流程6大阶段370个流程活动详解_第二阶段:计划阶段 — 86个活动

华为IPD流程涵盖了产品从概念到上市的完整过程,各阶段活动明确且相互衔接。在概念启动阶段,产品经理和项目经理分析可行性,PAC评审后成立PDT。概念阶段则包括产品描述、市场定位、投资期望等内容的确定,同时组建PDT核心组并准备项目环境。团队培训涵盖团队建设、流程、业务…

Adversarial Machine Learning(对抗机器学习)

之前把机器学习&#xff08;Machine Learning&#xff09;的安全问题简单记录了一下&#xff0c;这里有深入研究了一些具体的概念&#xff0c;这里记录一下方便以后查阅。 Adversarial Machine Learning&#xff08;对抗机器学习&#xff09; Adversarial Examples 相关内容Eva…