string的应用和模拟实现(上)

news2024/9/25 9:35:23

目录

string的应用

insert插入元素

 erase删除元素

 assign赋值:

replace代替函数的一部分

 find:从string对象中找元素

c_str:得到c类型的字符串的指针

substr:取部分元素构建成新的string对象

 rfind

 find_first_of:从string查找元素

 string算法题目:

题目1:字符串中的第一个唯一字符:

题目2:字符串最后一个单词的长度

 string的模拟实现(上)

构造函数:

c_str

 _size

[]

实现迭代器:

 迭代器和范围for的关系:

实现尾插:

reserve:修改容量

继续实现尾插:


string的应用

insert插入元素

 insert函数的作用是插入字符到string对象中。

其中,第一个函数是比较常用的,我们进行实验:

第一个函数是插入string对象(字符或字符串)到string对象中,例如:

#define _CRT_SECURE_NO_WARNINGS 1
using namespace std;
#include<iostream>
#include<string>
void test1()
{
	string s1 = "hello world";
	s1.insert(0, "bit");
	cout << s1 << endl;
	s1.insert(9, "bit");
	cout << s1 << endl;
}
int main()
{
	test1();
	return 0;
}

创建string对象s1,string对象的内容是字符串hello world,我们调用insert插入函数,表示分别从下标为0和下标为9的位置插入字符串bit,我们进行输出打印:

 1:insert函数表示插入元素到string对象中,insert函数并不常用,原因是对于头部或者中间位置的插入需要挪动数据,挪动数据会导致效率偏低

 erase删除元素

 erase函数的作用是从字符串中删除元素。

其中,第一个函数比较常用,我们对第一个函数进行分析:

 第一个参数pos表示我们要删除元素的下标,缺省值为0,如果我们不输出第一个参数的话,默认从string对象的首元素的下标开始删除,第二个参数是要删除元素的个数,缺省值是npos。

 npos对应的是无符号整型的最大值,大约在42亿左右。

我们进行实验:

void test1()
{
	string s1 = "hello world";
	s1.insert(0, "bit");
	cout << s1 << endl;
	s1.insert(9, "bit");
	cout << s1 << endl;
	s1.erase(9, 3);
	cout << s1 << endl;
	s1.erase(0, 3);
	cout << s1 << endl;
}
int main()
{
	test1();
	return 0;
}

注意:假如我们调用函数的第二个参数比我们pos位置之后的元素个数大时,我们会删除之后的全部元素,例如:

void test1()
{
	string s1 = "hello world";
	/*s1.insert(0, "bit");
	cout << s1 << endl;
	s1.insert(9, "bit");
	cout << s1 << endl;
	s1.erase(9, 3);
	cout << s1 << endl;
	s1.erase(0, 3);
	cout << s1 << endl;*/
	s1.erase(6, 200);
}
int main()
{
	test1();
	return 0;
}

我们pos位置之后只有5个元素,我们却要删除200个元素,因为200大于5,所以我们会删除pos位置之后的全部元素,具体的原理如图:

 所以假如我们想要更轻松的达到以上的效果,我们可以不传第二个参数,因为第二个参数的缺省值是npos,对应的数是42亿,远大于我们剩余元素的个数。

void test1()
{
	string s1 = "hello world";
	/*s1.insert(0, "bit");
	cout << s1 << endl;
	s1.insert(9, "bit");
	cout << s1 << endl;
	s1.erase(9, 3);
	cout << s1 << endl;
	s1.erase(0, 3);
	cout << s1 << endl;*/
	s1.erase(6);
	cout << s1 << endl;
}
int main()
{
	test1();
	return 0;
}

 1:erase函数表示删除元素。

 assign赋值:

assign相当于把先把原来的string对象清空,然后赋值。

 例如:

void test1()
{
	string s1 = "hello world";
	s1.assign("bit");
	cout << s1 << endl;
	///*s1.insert(0, "bit");
	//cout << s1 << endl;
	//s1.insert(9, "bit");
	//cout << s1 << endl;
	//s1.erase(9, 3);
	//cout << s1 << endl;
	//s1.erase(0, 3);
	//cout << s1 << endl;*/
	//s1.erase(6);
	//cout << s1 << endl;
}
int main()
{
	test1();
	return 0;
}

assign函数把string对象原来的hello world替换成了bit。

replace代替函数的一部分

 我们使用第一个函数:

 第一个参数表示从pos位置开始,第二个参数len表示代替len个,第三个参数str表示用str来代替这部分。

我们举一个例子:

void test1()
{
	string s1 = "hello world";
	s1.replace(6, 5, "china");
	cout << s1 << endl;
	/*s1.assign("bit");
	cout << s1 << endl;*/
	///*s1.insert(0, "bit");
	//cout << s1 << endl;
	//s1.insert(9, "bit");
	//cout << s1 << endl;
	//s1.erase(9, 3);
	//cout << s1 << endl;
	//s1.erase(0, 3);
	//cout << s1 << endl;*/
	//s1.erase(6);
	//cout << s1 << endl;
}
int main()
{
	test1();
	return 0;
}

这里我们用字符串"china"代替从第六个元素开始的五个元素。

 find:从string对象中找元素

 

 返回值是如果我们找到了匹配的元素,我们会返回对应匹配元素的第一个元素的下标,如果没有元素匹配,我们返回npos

我们主要理解第一个函数

 第一个参数表示我们要寻找匹配的字符串,第二个参数pos表示我们要从string对象的pos下标开始寻找,pos的缺省值为0,表示如果我们不传参的话,默认从首元素的下标开始查找。

我们可以用一个题目进行练习:

题目1:把字符串中空格全部替换成为"%20"

例如:string s1="hello world hello"

替换之后的结果为"hello%20world%20hello"

我们可以这样写:

void test1()
{
	string s1 = "hello world hello";
	size_t pos = s1.find(' ');
	while (pos != string::npos)
	{
		s1.replace(pos, 1, "%20");
		pos = s1.find(' ', pos + 3);
	}
	cout << s1 << endl;
	/*s1.replace(6, 5, "china");
	cout << s1 << endl;*/
	/*s1.assign("bit");
	cout << s1 << endl;*/
	///*s1.insert(0, "bit");
	//cout << s1 << endl;
	//s1.insert(9, "bit");
	//cout << s1 << endl;
	//s1.erase(9, 3);
	//cout << s1 << endl;
	//s1.erase(0, 3);
	//cout << s1 << endl;*/
	//s1.erase(6);
	//cout << s1 << endl;
}
int main()
{
	test1();
	return 0;
}

我们进行运行:

 我们完成了需求。

方法2:

void test1()
{
	string s1 = "hello world hello";
	string ret;
	for (auto ch : s1)
	{
		if (ch!=' ')
		{
			ret += ch;
		}
		else
		{
			ret += "%20";
		}
	}
	cout << ret << endl;
	/*size_t pos = s1.find(' ');
	while (pos != string::npos)
	{
		s1.replace(pos, 1, "%20");
		pos = s1.find(' ', pos + 3);
	}
	cout << s1 << endl;*/
	

	/*s1.replace(6, 5, "china");
	cout << s1 << endl;*/
	/*s1.assign("bit");
	cout << s1 << endl;*/
	///*s1.insert(0, "bit");
	//cout << s1 << endl;
	//s1.insert(9, "bit");
	//cout << s1 << endl;
	//s1.erase(9, 3);
	//cout << s1 << endl;
	//s1.erase(0, 3);
	//cout << s1 << endl;*/
	//s1.erase(6);
	//cout << s1 << endl;
}
int main()
{
	test1();
	return 0;
}

我们创建一个空对象ret,使用auto for循环,对s1的每一个元素进行判断,假如元素为' ',我们让ret+="%20",假如元素不为' ' ,我们就+=该元素即可。

我们进行运行:

 优化:

这里会涉及到容量不够的问题,我们可以实现开辟足够的容量,降低扩容的成本。

void test1()
{
	string s1 = "hello world hello";
	string ret;
	ret.reserve(s1.size());
	for (auto ch : s1)
	{
		if (ch!=' ')
		{
			ret += ch;
		}
		else
		{
			ret += "%20";
		}
	}
	cout << ret << endl;
	/*size_t pos = s1.find(' ');
	while (pos != string::npos)
	{
		s1.replace(pos, 1, "%20");
		pos = s1.find(' ', pos + 3);
	}
	cout << s1 << endl;*/
	

	/*s1.replace(6, 5, "china");
	cout << s1 << endl;*/
	/*s1.assign("bit");
	cout << s1 << endl;*/
	///*s1.insert(0, "bit");
	//cout << s1 << endl;
	//s1.insert(9, "bit");
	//cout << s1 << endl;
	//s1.erase(9, 3);
	//cout << s1 << endl;
	//s1.erase(0, 3);
	//cout << s1 << endl;*/
	//s1.erase(6);
	//cout << s1 << endl;
}
int main()
{
	test1();
	return 0;
}

c_str:得到c类型的字符串的指针

我们知道,string的底层实现的大致格式如下

class string
{
public://成员函数

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

这里的c_str就是这里的_str。

substr:取部分元素构建成新的string对象

 

 这个函数的作用如下:我们从string对象的pos位置开始,取len个元素产生一个新的string对象并返回。pos的缺省值为0,len的缺省值为npos,表示假如我们不传参数时,我们会构建从0位置开始到string对象最后一个元素的所有元素构成一个新的string对象。

我们如何应用呢?我们可以使用它去取文件的后缀。

例如:

void test2()
{
	string s1;
	cin >> s1;
	size_t pos = s1.find('.');
	if (pos != string::npos)
	{
		string suffix = s1.substr(pos, s1.size() - pos);
		cout << suffix << endl;
	}
}
int main()
{
	test2();
	return 0;
}

我们进行运行:

 rfind

rfind就是倒着的find

假如我们的文件名如下:test.cpp.txt.apk,我们知道,文件的后缀一定是最后一个.后面的元素。

我们使用rfind解决问题:

 

 find_first_of:从string查找元素

 我们解释第一个函数:

 表示我们从pos位置开始找,直到找到与str字符串内部任意相等的元素,并返回第一个相等的元素的下标

void test3()
{
	string str("Please, replace the vowels in this sentence by asterisks.");
	size_t found = str.find_first_of("aeiou");
	while (found != string::npos)
	{
		str[found] = '*';
		found = str.find_first_of("aeiou", found + 1);
	}

	cout << str << '\n';
}
int main()
{
	test3();
	return 0;
}

我们进行运行:

我们把string对象中的全部的"aeiou"全部替换成了"*".

 string算法题目:

题目1:字符串中的第一个唯一字符:

 387. 字符串中的第一个唯一字符 - 力扣(Leetcode)

class Solution {
public:
    int firstUniqChar(string s) {
    int count[26]={0};
    for(auto ch:s)
    {
        count[ch-'a']++;
    }
    for(size_t i=0;i<s.length();i++)
    {
        if(count[s[i]-'a']==1)
        {
            return i;
        }
    }
    return -1;
    }
};

 我们画图进行讲解:

这道题目的要求是要我们找到第一个只出现一次的字符,我们的思路如下:

我们可以创建一个数组,该数组有26个元素,从第1个元素到第26个元素对应的是a到z元素在s

中出现的次数。

 数组中每一个元素的初始值都为0。

然后我们使用范围for来统计每一个小写字母出现的次数

class Solution {
public:
	int firstUniqChar(string s) {
		int count[26] = { 0 };
		for (auto ch : s)
		{
			count[ch - 'a']++;
		}
	}
};

ch对应的是小写字母,ch-'a'就是该小写字母相对于字符'a'的距离,我们可以把'a'的位置设为0,其他的元素按照顺序往后排列。

 这个时候,我们的数组记录了每一个元素出现的次数。

接下来,我们遍历s,把s的元素放到count数组中找,直到找到第一个对应元素值为1的,这就是我们第一个只出现一次的字符,我们返回该元素的下标即可

class Solution {
public:
	int firstUniqChar(string s) {
		int count[26] = { 0 };
		for (auto ch : s)
		{
			count[ch - 'a']++;
		}
		for (size_t i = 0; i < s.size(); i++)
		{
			if (count[s[i] - 'a'] == 1)
			{
				return i;
			}
		}
		return -1;
	}
};

题目2:字符串最后一个单词的长度

 我们可以使用rfind函数,找到最后一个空格,最后一个空格之后的元素个数就是我们要求的长度。

我们可以这样写:

int main()
{
	string str;
	cin >> str;
	size_t pos = str.rfind(' ');
	cout << str.size() - pos - 1 << endl;
	return 0;
}

但是这里有一个问题,cin的输入本身就是以空格和换行为多组输入的间隔,我们可以把空格设置为一个普通的字符,让换行成为多组输入的间隔,我们可以使用getline函数:

 表示从流中取出字符串的一行,默认以换行为间隔,我们可以这样写:

int main()
{
	string str;
	getline(cin, str);
	size_t pos = str.rfind(' ');
	cout << str.size() - pos - 1 << endl;
	return 0;
}

我们进行实验:

 string的模拟实现(上)

#pragma once
namespace bit
{
	class string
	{
	public:
		
	private:
		char*_str;
		size_t _size;
		size_t _capacity;
	};
}

我们先写出框架:

我们用命名空间bit对string类进行封装:

我们一共有三个成员变量,分别是str,_size,_capacity,和顺序表非常相似。

str表示指向string空间的指针,_size表示string的有效元素个数,_capacity表示string的容量。

构造函数:

我们首先要实现构造函数:

string(const char*str)
		{
			_size = strlen(str);
			_capacity = _size;
			_str = new char(_capacity + 1);
			strcpy(_str, str);
		}

我们对构造函数进行分析:

我们构造函数的参数用const修饰,因为我们要用该参数构建string对象,不能通过该参数修改string对象。

我们首先使用strlen计算字符串str的有效元素的个数,并赋给_size

然后把_size赋给_capacity,接下来,我们来申请空间。

申请_capacity+1个空间,原因是我们需要给\0留一个空间,所有的成员变量都已经处理完毕,接下来,我们要实现数据的拷贝,我们使用strcpy把字符串str的内容拷贝到_str.

c_str

c_str就是c类型的指针,我们可以把他当成字符串类型首元素的地址,本质上就是_str

const char*c_str()
		{
			return _str;
		}

const修饰的意思就是我们只能访问c_str,不能对c_str进行修改。

我们进行实验:

void test_string1()
	{
		string s1("hello world");
		cout << s1.c_str() << endl;
	}

我们进行运行:

 _size

size_t size()
		{
			return _size;
		}

[]

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

我们要对[]进行重载,我们首先要进行断言,为了防止pos大于有效元素的个数而导致的越界问题,然后我们返回_str[pos],并用引用来接收,原因是[]的元素既可以访问,又可以修改。

我们对[]进行实验:

void test_string1()
	{
		string s1("hello world");
		cout << s1.c_str()<<endl;
		for (size_t i = 0;i<s1.size();i++)
		{
			s1[i]++;
		}
		cout << s1.c_str() << endl;
	}

我们进行运行:

 证明我们的[]是有效的。

实现迭代器:

迭代器是像指针的一样的,在目前阶段,我们可以把迭代器理解成一个指针。

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

我们对迭代器进行实验:

void test_string1()
	{
		string s1("hello world");
		string::iterator it1 = s1.begin();
		while (it1 != s1.end())
		{
			(*it1)++;
			it1++;
		}
		cout << s1.c_str() << endl;
	}

我们进行运行:

我们成功实现了迭代器。

 迭代器和范围for的关系:

void test_string1()
	{
		string s1("hello world");
		for (auto ch : s1)
		{
			cout << ch << ' ';
		}
		cout << endl;
	}

我们进行编译:

 我们什么都没有实现,我们却可以调用范围for,原因是什么呢?

范围for的本质是替换成了迭代器,当我们实现了迭代器之后,范围for就可以使用了。

当我们注释掉了迭代器之后

 

 范围for就无法使用:

 当我们恢复迭代器并把迭代器的begin换成Begin时:

 

 依旧会报错,原因是范围for发生了盲目的简单的替换,只会识别begin而无法识别大写的Begin

实现尾插:

void push_back(char ch)
{

}
void append(const char*str)
{

}

前者是尾插一个字符,后者是尾插一个字符串。

实现尾插我们首先要实现+=,不仅要实现+=一个字符,也要实现+=一个字符串。

我们模仿+=实现一个+=字符串和+=字符。

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

我们要实现+=的话,首先要想到的就是扩容,要解决扩容的问题,我们就要实现reserve函数:

reserve:修改容量

void rerserve(size_t n)
		{
			char*tmp = new char[n + 1];
			strcpy(tmp, _str);
			delete[] _str;
			_str = tmp;
			_capacity = n;
		}

我们的思路如下:我们与其修改容量,我们不如创建一个更大的空间,然后把原空间的内容拷贝到这个新空间里面,然后释放掉原空间,并且把原空间的容量修改为我们要修改的n

之所以修改为n+1,是因为我们需要多开辟一个空间给\0。

继续实现尾插:

void push_back(char ch)
		{
			if (_size == _capacity)
			{
				reserve(_capacity * 2);
			}
			_str[_size] = ch;
			_size++;
			_str[_size] = '\0';
		}

如果我们的容量和我们的有效元素相等时,表示我们的容量已满,我们需要扩容,这里我们可以直接采用扩容二倍的方法,然后把要插入的元素放到string的_size位置处,让_size++,然后把\0放到string的_size位置处。

下节课我们完成append函数。

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

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

相关文章

JVM【类的加载过程(类的生命周期)详解】

概述 在 Java 中数据类型分为基本数据类型和引用数据类型。基本数据类型由虚拟机预先定义&#xff0c;引用数据类型则需要进行类的加载。 按照 Java 虚拟机规范&#xff0c;从 class 文件到加载到内存中的类&#xff0c;到类卸载出内存为止&#xff0c;它的整个生命周期包括如…

软件测试之python学习

1、pycharm的常用配置 1.1修改主题配置 1、点击菜单file,选择settings选项2、选择editor&#xff0c;点击color scheme配色方案3、在右侧选择对应的主题配置1.2修改背景颜色 1、点击菜单file,选择settings选项2、选择appearance&#xff0c;点击Theme 1.3调整字体大小 1、点…

基于K8S+eureka的java应用快速上下线的WEB平台

刚进公司时&#xff0c;由于历史原因&#xff0c;应用发布通过&#xff1a;发布新版&#xff08;新老并存&#xff09;->下线老版->删除老版的方式&#xff0c;每次通过手工处理&#xff0c;蛋疼&#xff08;不方便且高风险&#xff09;。于是马上写了比较直观的脚本方案…

关于java移位运算的一点讨论

框架乱飞的年代&#xff0c;时常还得往框架源码里看&#xff0c;对内在原理没点理解&#xff0c;人家就会认为你不太行。平时开发你可能没咋用过位移运算&#xff0c;但往源码里一看&#xff0c;就时常能看到它。我也是看着看着&#xff0c;突然仔细一琢磨&#xff0c;又不由得…

C++缺省参数与函数重载

目录 一.缺省参数 1. 基本概念 2.多参函数中使用缺省参数的情形分类 二.函数重载 (1)形参类型不同构成的重载 (2)形参个数不同构成的重载 (3)形参类型顺序不同构成的重载 函数重载的注意事项&#xff1a; 三.C支持函数重载的底层原理--函数名修饰 编译器生成可执行程序…

选购自主可控全国产交换机时, IP防护等级多少比较合适?

本期武汉海翎光电的小编要为大家介绍的是《选购自主可控全国产交换机时IP防护等级多少比较合适&#xff1f;》首先我们要了解自主可控全国产交换机的工作场景&#xff0c;加固交换机会比工业交换机的IP等级更高一些&#xff0c;而工业交换机又会比普通交换机的IP等级要求高一些…

Unity 工具 之 Jenkins 打包自动化工具的下载/安装/基本操作/任务创建执行/Unity打包自动化简单搭建的相关整理

Unity 工具 之 Jenkins 打包自动化工具的下载/安装/基本操作/任务创建执行/Unity打包自动化简单搭建的相关整理 目录 Unity 工具 之 Jenkins 打包自动化工具的下载/安装/基本操作/任务创建执行/Unity打包自动化简单搭建的相关整理 一、简单介绍 二、Jenkins 的下载 三、Jenk…

代码随想录--链表相关题目整理

代码随想录–链表相关题目整理 1. LeetCode203 移除链表中指定元素 给你一个链表的头节点 head 和一个整数 val &#xff0c;请你删除链表中所有满足 Node.val val 的节点&#xff0c;并返回 新的头节点 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,6,3,4,5,6], val…

如何免费创建PDF文档?创建PDF文档的9个工具

PDF 创建器是一种程序、应用程序或软件&#xff0c;旨在制作或创建 PDF 文档。自可移植文档格式 ( PDF ) 出现以来&#xff0c;文档共享和存储变得更加容易。PDF 还使文件交换更加安全。由于 PDF 格式的众多优点&#xff0c;PDF 文档被全球范围内的人们广泛使用。因此&#xff…

Java数据结构(List介绍和顺序表)

1、List的介绍 在集合框架中&#xff0c;List是一个接口&#xff0c;继承自Collection&#xff08;也是一个接口&#xff09;。 Collection也是一个接口&#xff0c;该接口中规范了后序容器中常用的一些方法&#xff0c;Iterable也是一个接口&#xff0c;表示实现该接口的类是可…

第一天总结 之 用户管理界面的实现 之 修改操作 的实现

修改操作 首先 明确 修改操作的前提是 先在页面显示修改前的数据 然后对其进行修改 之后点击提交在页面显示修改前的数据 方法一&#xff1a; 带着数据直接跳转 到添加页面 即在跳转的url后 直接通过&#xff1f;携带数据跳转 缺点&#xff1a; &#xff01;&#xff01;…

6、Ubuntu20的JDKMySQLtomcatRedis安装

安装JDK 这里以安装版本8为例 进入存放jdk目录创建目录 cd /usr/local mkdir jdk cd jdk 把下好的jdk8压缩包拖拽到Ubuntu连接用户下 移动jdk包文件 mv /home/starfish/jdk-8u351-linux-x64.tar.gz . 解压jdk tar -zxvf jdk-8u351-linux-x64.tar.gz cd jdk1.8.0_351/ p…

【C#】C#Process调用外部程序

前言 使用C#调用外部程序&#xff0c;一种是通过Process类&#xff0c;一种是通过命令行&#xff0c;本文主要说一下使用C#中的Process类调用外部程序的方式。 过程&#xff1a; 创建Process对象配置启动选项&#xff08;输入、输出等&#xff09;切换工作目录设置外部程序名…

Java——全排列

题目链接 leetcode在线oj题——全排列 题目描述 给定一个不含重复数字的数组 nums &#xff0c;返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。 题目示例 输入&#xff1a;nums [1,2,3] 输出&#xff1a;[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] …

Linux防火墙状态查看 | 端口关闭 | 端口开启 | 修改命令

目录 firewall防火墙 【1】查看firewall状态 【2】开启、重启、关闭firewalld服务 【3】查看防火墙规则 【3】 添加指定需要开放的端口&#xff08;开启8088&#xff09; 【4】重载入添加的端口 【5】查询指定端口是否开启成功 firewall防火墙 【1】查看firewall状态 s…

Java日志系统log4j2的使用配置和异步日志使用

目录1. log4j21.1 log4j2介绍1.2 Log4j2入门1.2.1 log4j2(日志门面 日志框架)使用1.2.2 slf4j log4j2使用1.3 Log4j2配置1.4 Log4j2异步日志1.4.1 全局异步AsyncLogger1.4.2 混合异步AsyncLogger1.4.3 AsyncAppender1. log4j2 1.1 log4j2介绍 Apache Log4j2是Log4j的升级版…

IB地理学什么?适合什么人学习?

IB精选&#xff1a;IB地理学什么&#xff1f;快速搞懂自己适不适合修读地理&#xff01; 核心目的IB地理科是一个很特别的科目&#xff0c;目的是要帮助同学掌握一些认识和了解现实世界的技能。这个现实世界包括了两大部分。 第一个部分是自然环境&#xff0c;当中包括生态系统…

你还在恐惧指针吗?点进来,带你全方位深入了解指针

指针是什么&#xff1f; 指针也就是内存地址&#xff0c;指针变量是用来存放内存地址的变量。(存放在指针中的值都被当做地址处理) 对于32位的机器&#xff0c;假设有32根地址线&#xff0c;那么假设每根地址线在寻找地址的时候会产生高电平&#xff08;高电压&#xff09;和…

[ZJCTF 2019]NiZhuanSiWei

目录 信息收集 代码审计 第一层 第二层 第三层 信息收集 打开页面又是代码审计 代码如下 <?php $text $_GET["text"]; $file $_GET["file"]; $password $_GET["password"]; if(isset($text)&&(file_get_contents($text,…

React + TS + TDD 扫雷游戏学习心得

React TS TDD 扫雷游戏学习心得 试玩地址&#xff1a;https://goldenaarcher.com/minesweeper&#xff0c;stroybook 的地址&#xff1a;https://www.chromatic.com/build?appId63b2530f575ed5942cf105be&number1 测试主要使用 jest&#xff08;mock&#xff09; RTL …