【C++】-8.2- string〔string类模拟实现〕

news2024/9/28 19:19:53

文章目录

  • //模拟实现string类,并完成测试
  • • string类的基本结构
  • • Destructor
  • • Construct
    • 〔构造函数〕
      • ‹ 无参构造 ›
      • ‹ 单参数构造 ›
      • ‹ 全缺省参数构造 ›
    • 〔拷贝构造〕
  • • operator= 赋值重载
  • • Element access(operator[])
  • 补充:const 变量的场景
  • • Iterator
  • • Relational Operators(比较大小)
  • • Capacity
    • 〔size〕&〔capacity〕&〔empty〕
    • 〔reserve〕
    • 〔resize〕
  • • Modify
    • 〔push_back〕&〔append〕
    • 〔+=〕
    • 〔swap〕
    • 〔clear〕& 〔c_str〕
  • • 「insert」在 pos 位置插入字符或字符串
  • • 「erase」从 pos 位置删除 len 个字符
  • 注:npos的声明和定义
  • • 「find」
  • • 「流插入<<、流提取>>」
    • ‹ 流插入 ›
    • ‹ 流提取 ›

//模拟实现string类,并完成测试

头文件声明:

//anolog_string.h
#pragma once

#include <iostream>
namespace RoundBottle//与库里面的string类做区分
{
	class string
	{
	public:
		typedef char* iterator;
		typedef const char* const_iterator;

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

		//拷贝构造
		string(const string& s);

		//赋值重载是关于两个已经存在的对象
		string& operator=(const string& s);
		~string();

		/

		// access
		char& operator[](size_t index);
		const char& operator[](size_t index)const;

		//

		// iterator
		iterator begin();
		iterator end();
		const_iterator begin()const;
		const_iterator end()const;

		/

		// 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;

		/

		// capacity
		size_t size()const;
		size_t capacity()const;
		bool empty()const;
		void resize(size_t n, char c = '\0');
		void reserve(size_t n);

		/

		//relational operators
		bool operator<(const string& s);
		bool operator<=(const string& s);
		bool operator>(const string& s);
		bool operator>=(const string& s);
		bool operator==(const string& s);
		bool operator!=(const string& s);

		// 返回c在string中第一次出现的位置
		size_t find(char c, size_t pos = 0) const;

		// 返回子串s在string中第一次出现的位置
		size_t find(const char* s, size_t pos = 0) const;

		// 在pos位置上插入字符c/字符串str,并返回该字符的位置
		string& insert(size_t pos, char c);
		string& insert(size_t pos, const char* str);

		// 删除pos位置上的元素,并返回该元素的下一个位置
		string& erase(size_t pos, size_t len = npos);

	private:
		char* _str;
		size_t _capacity;
		size_t _size;
		static size_t npos;
	};

	size_t string::npos = -1;

	std::ostream& operator<<(std::ostream& _cout, const RoundBottle::string& s);
	std::istream& operator>>(std::istream& _cin, RoundBottle::string& s);

	//测试
	void string_test()
	{}
};

//test.cpp
#include "analog_string.h"

int main()
{
	try
	{
		RoundBottle::string_test();
	}
	catch (const std::exception& e)
	{
		cout << e.what() << endl;
	}

	return 0;
}

• string类的基本结构

private:
	char* _str;
	size_t _capacity;
	size_t _size;

在这里插入图片描述

• Destructor

~string()
{
	delete[]_str;
	_str = nullptr;
	_size = _capacity = 0;
}

• Construct

〔构造函数〕

‹ 无参构造 ›

  • 方式一_str 置空指针
//全缺省构造
string()
	: _str(nullptr)
	, _size(0)
	, _capacity(0)
	{}

//cout 自动识别类型,对于指针,会进行解引用!

cout << s.c_str() <<endl; 👉 s.c_str()成员函数 会返回对象 s 中的成员变量 _str,若 _str==nullptr 则 cout 会对空指针进行解引用!

  • 方式二:给_str所指向的空间开新空间——new char
//全缺省构造
string()
	: _str(new char)
	, _size(0)
	, _capacity(0)
	{}

//析构函数执行的语句是:delete [] _str; 与 new 不匹配(关于new和delete详见文章C++ -5- 内存管理)

  • 方式三_str = new char [1]
//全缺省构造
string()
	: _str(new char [1])
	, _size(0)
	, _capacity(0)
	{
		_str[size] = '\0';
	}

‹ 单参数构造 ›

  • 方式一:直接赋值
//全缺省构造
string(const char* str)
	: _str(str)//权限放大
	, _size(strlen(str))
	, _capacity(strlen(str))
	{}

将 str 中的地址赋值给 _str 很明显是权限放大,如下图:
在这里插入图片描述

  • 方式二:
//全缺省构造
string(const char* str)
	: _size(strlen(str))
{
	_capacity = _size;
	_str = new char[_capacity + 1];
	strcpy(_str, str);
}
  • 关于 capacity:
    • 能存多少个有效数据(不包括’\0’)→ 所以开空间要多开一个留给’\0’
    • 尽量不要再初始化列表里面用 size 来初始化 capacity,因为初始化的顺序是变量声明的顺序,变量声明顺序更改会影响初始化的结果。

‹ 全缺省参数构造 ›

无参构造和单参数构造可以合成一个全缺省参数构造

//全缺省构造
string(const char* str = "")
	: _size(strlen(str))
{
	_capacity = _size;
	_str = new char[_capacity + 1];
	strcpy(_str, str);
}
  • "" 常量字符串默认以 ‘\0’ 结束 (字符串为不为空都是如此)
  • const char* str = '\0' 不可以! ‘\0’ 只是一个 char 类型的字符,将 char 类型的数据赋值给 const char* 类型不匹配,会发生类型转换,char 类型转化为 int 类型,类型仍然不匹配,则 str 会被赋值为空指针
  • “\0”才是一个常量字符串
  • const char* str = "" == const char* str = "\0"

〔拷贝构造〕

开新空间 + 拷贝数据

string(const string& s)
{
	_str = new char[s._capacity + 1];
	_size = s._size;
	_capacity = s._capacity;
	strcpy(_str, s._str);
}

• operator= 赋值重载

  • delete 原空间
    • 自己赋值给自己( s = s )?自己给自己 delete 之后就什么都没了~,所以如果是自己给自己赋值,这里需要判断一下,什么也不执行就行了。
  • new 新空间
    • 为了防止开空间失败,这个可以先创建临时变量
  • strcpy 内容到新空间
  • 修改 sizecapacity

❗❗赋值重载是关于两个已经存在的对象

string& operator=(const string& s)
{
	if (this != &s)
	{
		//防止开空间失败
		char* tmp = new char[s._capacity + 1];
		strcpy(tmp, s._str);
		//

		delete[]_str;//销毁原空间
		_str = tmp;//拷贝数据
		_size = s._size;
		_capacity = s._capacity;

	}
	return *this;
}

• Element access(operator[])

char& operator[](size_t index)
{
	assert(index < _size);
	return _str[index];
}
const char& operator[](size_t index)const
{
	assert(index < _size);
	return _str[index];
}

补充:const 变量的场景

const 变量不可以调用非 const 成员函数,不修改成员函数变量数据的函数最好都加 const
例如如下代码:参数为 const string 类型

const char& operator[](size_t index)const
{
	assert(index < _size);
	return _str[index];
}
size_t size()const
{
	return _size;
}
void Print(const string& s)
{
	for (int i = 0; i < s.size(); ++i)//size()
	{
		cout << s[i] << " ";//[]操作符重载
	}
	cout << endl;
}

• Iterator

这里用指针来模拟实现迭代器,但库中的迭代器不一定都是用指针实现的。

另外,begin() 和 end() 遵循 “左闭右开” 的原则。
在这里插入图片描述

typedef char* iterator;
typedef const char* const_iterator;

// iterator
iterator begin()
{
	return _str;
}
iterator end()
{
	return (_str + _size);
}

const_iterator begin()const
{
	return _str;
}
const_iterator end()const
{
	return (_str + _size);
}

实现了迭代器之后,范围for也可以用。

应用示例:

void string_test6()
{
	string s("abcdefghijklm");

	string::iterator it = s.begin();	
	while (it != s.end())
	{	
		cout << *it << " ";
		++it;
	}
	cout << endl;
	
	for(auto e : s)
	{
		cout << e << " ";
	}
	cout << endl;
}

• Relational Operators(比较大小)

比较ASCII码值的大小,复用 strcmp

bool operator<(const string& s)
{
	return strcmp(_str, s._str) < 0;
}
bool operator<=(const string& s)
{
	return strcmp(_str, s._str) <= 0;
}
bool operator>(const string& s)
{
	return !(*this <= s);
}
bool operator>=(const string& s)
{
	return !(*this < s);
}
bool operator==(const string& s)
{
	return strcmp(_str, s._str) == 0;
}
bool operator!=(const string& s)
{
	return !(*this == s);
}

• Capacity

〔size〕&〔capacity〕&〔empty〕

size_t size()const
{
	return _size;
}
size_t capacity()const
{
	return _capacity;
}
bool empty()const
{
	return _size == 0;
}

〔reserve〕

  • reverse 是管理容量的函数
  • 不缩容原则,如果要更改的容量大小小于原容量大小则不执行操作
  • 实现思路:
    • 按指定容量大小开新空间(永远多开一个位置给’\0’)
    • 拷贝数据到新空间
    • 释放原空间
    • 新空间地址赋值给 _str
    • 修改容量大小
void reserve(size_t n)
{
	if (n > _capacity)
	{
		char* tmp = new char[n + 1];
		strcpy(tmp, _str);
		delete[]_str;
		_str = tmp;
		_capacity = n;
	}
}

〔resize〕

  • resize :开空间(size_t n) + 初始化(char c)
  • if (n <= _size) 容量不缩,只保留前 n 个数据
  • if (n > _capacity) 扩容
  • ps. size >= capacity
void resize(size_t n, char c = '\0')
{
	if (n <= _size)
	{
		_str[n] = '\0';
		_size = n;
	}
	else
	{
		if (n > _capacity)
		{
			reserve(n);
		}
		int cout = n - _size;
		while (cout--)
		{
			_str[_size] = c;
			++_size;
		}
		_str[_size] = '\0';
	}
}

• Modify

〔push_back〕&〔append〕

插入数据就要考虑容量问题,势必要进行扩容,由于原本的空间就是 new 出来的,扩容肯定不能用 realloc 直接扩容,所以最终我们选择手动扩容 → 调用 reserve 函数
在这里插入图片描述
整体思路:

  • 检查容量
  • 插入数据
  • 修改 _size
void push_back(char c)
{
	if (_size == _capacity)
	{
		//扩容
		int new_capacity = _capacity == 0 ? 7 : _capacity * 2;
		reserve(new_capacity);
	}
	_str[_size] = c;
	++_size;
	_str[_size] = '\0';
}

void append(const char* str)
{
	int len = strlen(str);
	if (_size + len > _capacity)
	{
		reserve(_size + len);
	}
	strcpy(_str + _size, str);
	_size += len;
}

〔+=〕

复用 push_back 和 append

string& operator+=(char c)
{
	push_back(c);
	return *this;
}


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

〔swap〕

可以复用 std 库里的 swap 函数

void swap(string& s)
{
	/*char* ptmp = _str;
	_str = s._str;
	s._str = _str;

	char size = _size;
	_size = s._size;
	s._size = _size;

	char capacity = _capacity;
	_capacity = s._capacity;
	s._capacity = _capacity;*/

	std::swap(_str, s._str);
	std::swap(_size, s._size);
	std::swap(_capacity, s._capacity);
}

〔clear〕& 〔c_str〕

clear :清空数据

void clear()
{
	_size = 0;
	_str[_size] = '\0';
}

const char* c_str()const
{
	return _str;
}

• 「insert」在 pos 位置插入字符或字符串

插入字符就相当于插入只有单个字符的字符串,所以这里详细分析插入字符串时的情况,插入字符的思路与之类似。

基本思路:①检查容量;②挪动数据(strncpy);③插入数据。(注意检查 pos 位置的有效性)

如下图,在 pos 位置插入长度为 len 的字符串:则我们需要把 红色区域 的数据往后挪动 len个单位长度,再将要插入的数据从原 pos 位置往后依此插入。
在这里插入图片描述

  • 可以选择 index 从 size 位置开始吗??👉不可以
    当 pos == 0 时情况如下:
    在这里插入图片描述
    size_t index无符号整型,-1即为整型的最大值,以 index >= pos 作为循环判断条件,在 pos ==0 的情况下将进入死循环;
    ②若声明 int index,仍以 index >= pos 作为循环判断条件,>=操作符左右变量的类型不同,会发生类型转换,通常是比较范围小的向范围大的转换,即在该情况下,有符号整型一般转化为无符号整型。

简单说明一下strnpy函数:strncpy( dst-copy到哪 , scr-从哪copy , 从scr copy 多少个)

string& insert(size_t pos, char c)
{
	assert((pos <= _size) && (pos >= 0));
	if (_size + 1 > _capacity)//扩容
	{
		int new_capacity = _capacity == 0 ? 7 : _capacity * 2;
		reserve(new_capacity);
	}

	for (size_t index = _size + 1; index >= pos + 1; --index)//挪动数据
	{
		_str[index] = _str[index - 1];
	}
	_str[pos] = c;
	++_size;
	return *this;
}

string& insert(size_t pos, const char* str)
{
	assert((pos <= _size) && (pos >= 0));
	int len = strlen(str);
	if (_size + len > _capacity)//扩容
	{
		int new_capacity = _size + len;
		reserve(new_capacity);
	}

	for (int index = _size + len; index >= pos + len; --index)//挪动数据
	{
		_str[index] = _str[index - len];
	}

	strncpy(_str + pos, str, len);
	_size += len;

	return *this;
}

• 「erase」从 pos 位置删除 len 个字符

  1. pos 往后还有 >= len 个长度的字符:挪动覆盖数据(memmove/strcpy)
  2. pos 往后不足 len 个长度的字符:从 pos 往后全部删除(包括 pos 位置的数据)

注意:pos + len >= _size || len == npos len == npos必须单独判断,npos 已经是整型的最大值。

string& erase(size_t pos, size_t len = npos)
{
	assert((pos < _size) && pos);
	if (pos + len >= _size || len == npos)
	{
		_size = pos;
		_str[_size] = '\0';
	}
	else
	{
		for (size_t index = pos + len; index <= _size; ++index)
		{
			_str[index - len] = _str[index];
		}
		//strcpy(_str+pos,_str+pos+len)
		_size -= len;
	}
	return *this;
}

注:npos的声明和定义

class string
{
	private:
		char* _str;
		size_t _capacity;
		size_t _size;
		static size_t npos;
		//static size_t npos = -1;error
};
size_t string::npos = -1;

静态成员变量:类内声明,类外定义。
(ps. C++中加 const 的静态成员变量可以给缺省值,但是只针对整型变量)(最好不要这么用)

class string
{
	private:
		const static size_t npos = -1;
};

• 「find」

  • 找字符:找到就返回该字符下标,没有找到返回 npos
  • 找字串:strstr函数暴力匹配
    • 为 nullptr 即没找到
    • 找到返回子串第一个字符的地址 👉 地址 - 地址 = 下标
//找字符c
size_t find(char c, size_t pos = 0) const
{
	for (int i = 0; i < _size; ++i)
	{
		if (_str[i] == c)
			return i;
	}
	return npos;
}

//找字串s
size_t find(const char* s, size_t pos = 0) const
{
	char* p = strstr(_str, s);
	if (p == nullptr)
	{
		return npos;
	}
	else
	{
		return (p - _str);
	}
}

• 「流插入<<、流提取>>」

肯定不能写成成员函数,可以用友元函数,但也不一定要写成友元函数。

‹ 流插入 ›

  • 流插入重载要根据 size 打印,即使字符串间夹杂着 ‘\0’
  • 不能直接访问成员变量,可以通过成员函数间接访问。( iterator / 范围for / [下标] )
std::ostream& operator<<(std::ostream& _cout, const RoundBottle::string& s)
{
	for (auto e : s)
	{
		_cout << e;
	}
	return _cout;
}

‹ 流提取 ›

cin 和 scanf 对于空格和换行不识别 👉 采用 istream 的成员函数 get()

优化:不断输入字符会导致频繁的扩容,这里可以用一个临时数组储存输入字符,临时数组每满一次就追加到 string s 中,知道输入结束后,将临时数组里剩下的字符再追加到 string s 中。

std::istream& operator>>(std::istream& _cin, RoundBottle::string& s)
{
	s.clear();
	char tmp[128];
	char c = _cin.get();
	int index = 0;
	while (c != '\0' && c != '\n')
	{
		tmp[index] = c;
		++index;

		if (index == 127)
		{
			tmp[index] = '\0';
			s += tmp;
			index = 0;
		}
		c = _cin.get();
	}
	if (index != 0)
	{
		tmp[index] = '\0';
		s += tmp;
	}
	return _cin;
}

END

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

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

相关文章

Android强大的原生调试工具adb的常用命令

文章目录 ADB简介常用命令列出链接的设备进入设备的shell环境设备日志安装应用程序卸载应用程序将本地文件复制到调试设备上将设备上的文件拉取到本地启动程序强制停止程序运行截图屏幕录制列出调试设备所有的应用的报名 结语 ADB简介 ADB&#xff08;Android Debug Bridge&am…

BioXFinder生物数据库

BioXFinder是目前国内第一个也是国内唯一一个生物信息数据库&#xff0c;由享融智云公司精心研发&#xff0c;主要针对生物科研工作者的综合性生物数据检索及分析平台&#xff0c;汇集了核酸、蛋白、蛋白结构、代谢通路和信号通路信息&#xff0c;解决海外数据访问难、访问慢的…

Adobe Creative Cloud 摄影计划 - 当图像与想象力相遇。 PS+LRc套餐 国际版 1年订阅/398

这里重点介绍国际版摄影计划套餐详情&#xff1a; 国际版包括&#xff1a;Photoshop、Lightroom Classic、Photoshop Express、Lightroom Mobile、Lightroom、云服务。中国版包括&#xff1a;Photoshop、Lightroom Classic、Photoshop Express、Lightroom Mobile 桌面应用程序…

SpringBoot集成Kafka的简单实现案列

1&#xff0c;首先搭建一个Springboot项目准备一个测试服务器 2&#xff0c;引入pom <dependency><groupId>org.springframework.kafka</groupId><artifactId>spring-kafka</artifactId></dependency>他其中包括一些其他的包如果有冲突可…

有哪些值得注意的隔断小技巧可以让酒店更美观实用

以下是一些可以使酒店隔断更美观实用的小技巧&#xff1a; 1. 选择透明或半透明材料&#xff1a;使用透明或半透明的材料&#xff0c;如玻璃、亚克力或薄钢板&#xff0c;可以增加空间的亮度和通透感。 2. 考虑隔断的尺寸和布局&#xff1a;确保隔断的尺寸和布局适应空间大小和…

AI血洗时尚圈!就连这些线上店家都开始用AI生成爆款商品了

量子位 | 公众号 QbitAI 家人们谁懂啊&#xff0c;刷屏的《哈利波特》AI时装秀&#xff0c;看几次都不够&#xff01; 这些时装造型火遍全网&#xff0c;视频播放量破千万&#xff0c;还只是技术给时尚圈带来的一点小小震撼。 现在&#xff0c;用AI辅助设计服装、食品包装、装…

Elasticsearch初探

ElasticSearch Elasticsearch 是一个分布式、高扩展、高实时的搜索与数据分析引擎。 Elasticsearch结合Kibana、Logstash、Beats&#xff0c;也就是elastic stack(ELK)。被广泛应用在日志分析、实时监控&#xff08;CPU、Memory、Program&#xff09;等领域。 elasticsearch是…

【​区块链】相关专业名词术语

区块链 区块链是一个共享数据库&#xff0c;存储于其中的数据或信息&#xff0c;具有不可伪造、全程留痕、可以追溯、公开透明和集体维护等特征。可以把区块链理解为一个共享的、不可更改的电子账本&#xff0c;能够在网络中记录交易和跟踪资产。这里的资产可以是有形的&#…

mount.nfs: access denied by server while mounting

问题及截图&#xff1f; 客户端挂载nfs共享目录的时候提示如下&#xff1a; mount.nfs: access denied by server while mounting 192.168.44.10:/xxx 问题原因有两方面&#xff1a; 一&#xff1a;权限问题 服务器端的共享目录没有给足相应的权限导致&#xff0c;导致挂载失…

如何做mysql调优?绝命7招,让慢SQL调优100倍

前言&#xff1a; 在40岁老架构师尼恩的读者社区&#xff08;50&#xff09;中&#xff0c;很多小伙伴拿不到offer&#xff0c;或者拿不到好的offer。 尼恩经常给大家 优化项目&#xff0c;优化简历&#xff0c;挖掘技术亮点。在指导简历的过程中&#xff0c; Java 调优是一项…

HiveSQL在使用聚合类函数的时候性能分析和优化详解

文章目录 概述1.仅在Reduce阶段聚合的SQL执行逻辑2.在map和reduce阶段聚合的SQL逻辑 概述 前文我们写过简单SQL的性能分析和解读&#xff0c;简单SQL被归类为select-from-where型SQL语句&#xff0c;其主要特点是只有map阶段的数据处理&#xff0c;相当于直接从hive中取数出来…

C++模板和模板的特化,模板的扩展和智能指针------(14)

模板 概念 模板的作用是实现类型通用&#xff0c;降低代码的冗余度 模板可以为一种算法定义不同类型的版本 实现机制&#xff1a; 复制代码使用类型参数突破类型的限制&#xff0c;丧失一定的类型安全 模板需要实例化才能使用&#xff0c;实例化由编译器完成 模板的分类 …

对比学习初认识

这篇文章我们通过SimCLR模型来对对比学习技术有一个认知。 1.什么是对比学习系统 根据上面这个图&#xff0c;来介绍下怎么做一个抽象的对比学习系统。以一个图像为例子&#xff0c;通过自动构造正例或负例&#xff0c;形成图片的两个view&#xff0c;通过encoder把它们编码&a…

第十届中医药健康文化节:御医传人龚洪海强调心血管疾病中医治疗"治未病"的重要性

在第十届中医药健康文化节上&#xff0c;备受瞩目的中医世家龚洪海医生强调了中医对心血管疾病的有效治疗&#xff0c;并提出了更为重要的概念——"治未病"。这一观念的传达不仅对预防常见病和多发病有益&#xff0c;同时在重大疑难疾病的防治中发挥着关键作用&#…

Java - OkHttp

使用方法&#xff1a; 在postman中请求接口通过&#xff0c;可复制右侧代码使用&#xff0c;可用于webservice等接口

MySQL数据库事务和存储引擎

MySQL数据库事务和存储引擎 一、mysql事务1、事务的概念2、事务的ACID特点2.1 原子性2.2 一致性2.3 隔离性2.4 持久性 3、两个事务之间的影响3.1 脏读&#xff08;读取未提交数据&#xff09;3.2 不可重复度&#xff08;前后多次读取&#xff0c;数据内容不一致&#xff09;3.3…

【Vue+Django】Training Management Platform分页功能 - 20230621

需求描述 分页显示数据&#xff0c;避免造成服务器宕机。 Django&#xff1a;根据pageNum返回数据切片 Views.py写入业务逻辑 # 数据接口&#xff1a;暴露trs_training_and_test_record数据 def api_trs_training_and_test_record(request,myDateS,myDateE,mySystem,catego…

SpringCloud Alibaba入门4之nacos注册中心管理

我们在上一章的基础上进行学习。https://blog.csdn.net/qinxun2008081/article/details/131330451 什么是注册中心?它记录了服务和服务地址的映射关系。在分布式架构中&#xff0c;服务会注册到这里&#xff0c;当服务需要调用其它服务时&#xff0c;就到这里找到服务的地址&…

Java开发必看,Spring增强性能与现代应用支持

出品 | CSDN 云计算 开发界经典话题之一&#xff0c;就是语言之争。除了每月的开发语言排行榜上几大王牌语言在榜单前列上上下下&#xff0c;在 CSDN 连续几年的年度开发者调研《中国开发者调查报告》中&#xff0c;Java 一直被评为开发者使用占比最高的语言&#xff0c;而 Spr…

银河麒麟V10 wireguard 编译

系统信息 操作系统信息&#xff1a; 我这里使用的操作系统是 银河麒麟V10&#xff0c;CPU为飞腾 ARM64 根据wireguard 的编译指南&#xff1a;https://www.wireguard.com/compilation/ 安装 编译安装内核 注意&#xff1a;5.6 以上内核不需要编译安装&#xff0c;已经集…