【C++】vector 不踩坑指南:用法、底层实现与迭代器失效解析

🫧个人主页:小年糕是糕手

💫个人专栏:《C++》《Linux》《数据结构》《Blender 修行笔记》

🎨你不能左右天气,但你可以改变心情;你不能改变过去,但你可以决定未来!


目录

一、定义和初始化

二、vector的常用成员函数

【容量相关】常用函数

[1. size() → 获取元素个数](#1. size() → 获取元素个数)

[2. empty() → 判断是否为空](#2. empty() → 判断是否为空)

[3. resize(n) → 重新调整大小](#3. resize(n) → 重新调整大小)

[4. capacity() → 查看容量(预分配空间)](#4. capacity() → 查看容量(预分配空间))

[5. reserve(n) → 预分配空间(提升效率)](#5. reserve(n) → 预分配空间(提升效率))

【添加元素】常用函数

[1. push_back(x) → 尾部添加元素(最常用)](#1. push_back(x) → 尾部添加元素(最常用))

[2. emplace_back(x) → 尾部高效添加(C++11)](#2. emplace_back(x) → 尾部高效添加(C++11))

[3. insert(pos, x) → 指定位置插入](#3. insert(pos, x) → 指定位置插入)

【删除元素】常用函数

[1. pop_back() → 删除最后一个元素](#1. pop_back() → 删除最后一个元素)

[2. erase(pos) → 删除指定位置元素](#2. erase(pos) → 删除指定位置元素)

[3. erase(begin, end) → 删除区间](#3. erase(begin, end) → 删除区间)

[4. clear() → 清空所有元素](#4. clear() → 清空所有元素)

【访问元素】常用函数

[1. [] → 下标访问(最快,不检查越界)](#1. [] → 下标访问(最快,不检查越界))

[2. at(i) → 下标访问(会检查越界,更安全)](#2. at(i) → 下标访问(会检查越界,更安全))

[3. front() → 访问第一个元素](#3. front() → 访问第一个元素)

[4. back() → 访问最后一个元素](#4. back() → 访问最后一个元素)

[【遍历 / 迭代器】常用函数](#【遍历 / 迭代器】常用函数)

[1. begin() → 指向第一个元素的迭代器](#1. begin() → 指向第一个元素的迭代器)

[2. end() → 指向最后一个元素下一位的迭代器](#2. end() → 指向最后一个元素下一位的迭代器)

【其他实用函数】

[1. swap(vec) → 交换两个 vector](#1. swap(vec) → 交换两个 vector)

[2. assign(n, val) → 赋值(覆盖原有数据)](#2. assign(n, val) → 赋值(覆盖原有数据))

高频使用速记表

三、尝试手搓vector

vector.h

test.c

四、迭代器失效

一、简单记忆规则

二、严格来说的正确规则

[erase 的精确失效规则:](#erase 的精确失效规则:)

[insert 的精确失效规则:](#insert 的精确失效规则:)

三、erase的返回值

四、典型用法(安全删除)

[五、erase 返回值的精确行为](#五、erase 返回值的精确行为)

[六、insert 也有返回值(C++11 起)](#六、insert 也有返回值(C++11 起))


一、定义和初始化

vector和string不同的是vector是没有重载流插入和提取的,我们需要自己来写一个打印函数,我们依旧有三种方式:

cpp 复制代码
//vector是没有重载流插入和提取的,我们需要自己来写一个打印函数
void Print(const vector<int>& v)
{
	//方式一
	for (size_t i = 0; i < v.size(); i++)
	{
		cout << v[i] << " ";
	}
	cout << endl;

	//方式二
	//这里auto实际上是vector<int>::const_iterator
	for (auto it = v.begin(); it != v.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;

	//方式三
	for (auto x : v)
	{
		cout << x << " ";
	}
	cout << endl;
}

下面我们来尝试给他定义和初始化一下:

cpp 复制代码
//定义和初始化
void test_vector1()
{
	vector<int>v1;
	vector<int>v2(10, 1);//创建了十个空间,空间里都是1
	vector<int>v3(v2.begin(), v2.end());//v2和v3完全一致
	string s1("hello world");
	vector<int>v4(s1.begin(), s1.end());//把字符串里的每个字符,按ASCII码存入v4中
	vector<int>v5(v4);//拷贝构造
	vector<int>v6({ 1,2,3,4,5,6,7 });
	vector<int>v7 = { 1,2,3,4,5,6,7 };

	
	//vector没用支持流的插入和提取(string支持)
	Print(v2);
	Print(v3);
	Print(v4);
	Print(v5);
}

其实vector大家就给他当成一个动态数组,我们之前学过的变长数组,但是我们说过有些编译器是不支持变长数组的,但是vector大家一般都是支持的


二、vector的常用成员函数

【容量相关】常用函数

1. size() → 获取元素个数

cpp 复制代码
v.size(); // 返回元素数量(无符号整数)

2. empty() → 判断是否为空

cpp 复制代码
v.empty(); // 空返回true,非空返回false

3. resize(n) → 重新调整大小

cpp 复制代码
v.resize(10); // 变成10个元素,多删少补

resize(n):扩大时自动填 0 / 空值
resize(n, 66):扩大时新元素全部填 66

4. capacity() → 查看容量(预分配空间)

cpp 复制代码
v.capacity(); // 实际分配的内存大小 >= size

5. reserve(n) → 预分配空间(提升效率)

cpp 复制代码
v.reserve(100); // 提前分配100个元素空间,避免频繁扩容

下面我们在函数中来看看vector的和容量相关的函数(大家重点看注释):

cpp 复制代码
//扩容机制 -- 我们通常不会去缩容
//我们不难发现VS2022下依旧保持1.5倍左右的扩容机制
void test_vector2()
{
	vector<int> v1;
	//如果我们知道我们需要开多少空间可以直接使用reverse
	int x;
	cin >> x;
	v1.reserve(x);

	size_t old = v1.capacity();
	cout << old << endl;
	for (size_t i = 0; i < 100; i++)
	{
		v1.push_back(i);
		if (old != v1.capacity())
		{
			cout << v1.capacity() << endl;
			old = v1.capacity();
		}
	}
}

//clock()统计的是CPU运行时间,不是墙钟时间,不受系统调度影响
void test_vector3()
{
	vector<int> v1;
	const size_t N = 10000000; // 合理测试次数:1000万次
	size_t begin = clock();
	for (long long i = 0; i < N; i++)
	{
		v1.push_back(i);
	}
	size_t end = clock();
	cout << end - begin << endl;
}

//reserve = 开空间,不造元素
//resize = 开空间 + 造元素,直接能用
void test_vector4()
{
	vector<int> v1;
	v1.reserve(10);
	// size=0,不能访问 v[0]
	// 只是预留空间

	vector<int> v2;
	v2.resize(10);
	// size=10,可以直接用 v[0]~v[9]
	// 有10个元素
	
	vector<int> v3;
	v3.resize(100, 1);
	//把v的大小改成100个元素,所有元素的值都是 1。
}

【添加元素】常用函数

1. push_back(x) → 尾部添加元素(最常用)

cpp 复制代码
v.push_back(10); // 尾部加10
v.push_back(20);

2. emplace_back(x) → 尾部高效添加(C++11)

push_back 更快,用法一样:

cpp 复制代码
v.emplace_back(30);

3. insert(pos, x) → 指定位置插入

cpp 复制代码
// 在第2个位置(下标1)插入 100
v.insert(v.begin() + 1, 100);

这里我们需要对比一下push_back和emplace_back:

cpp 复制代码
//emplace
struct AA {
	int _a1 = 1;
	int _a2 = 2;
	AA(int a1 = 1, int a2 = 1)
		:_a1(a1)
		,_a2(a2)
	{ }
};
void test_vector6()
{
	AA aa1 = { 0,0 };
	vector<AA> v = { aa1,{1,1},{2,2},{3,3} };
	auto it = v.begin();
	while (it != v.end())
	{
		//结构体不属于内置类型不能直接解引用打印
		//cout << *it << endl;
		cout << it->_a1 << ":" << it->_a2 << endl;
		it++;
	}
	//push_back只可以传AA对象
	//emplace可以传AA对象也可以传构造AA对象的参数
	//emplace更高效

	v.push_back(aa1);
	v.emplace_back(aa1);

	v.push_back({ 1,1 });
	v.emplace_back(1, 1);
}

【删除元素】常用函数

1. pop_back() → 删除最后一个元素

cpp 复制代码
v.pop_back(); // 无返回值,直接删尾部

2. erase(pos) → 删除指定位置元素

cpp 复制代码
// 删除第3个元素(下标2)
v.erase(v.begin() + 2);

3. erase(begin, end) → 删除区间

cpp 复制代码
// 删除前3个元素
v.erase(v.begin(), v.begin() + 3);

4. clear() → 清空所有元素

cpp 复制代码
v.clear(); // size=0,capacity不变

这里依旧给出一段解释代码:

cpp 复制代码
void test_vector5()
{
	//insert -- 头插
	//erase -- 删除

	vector<int>v1 = { 1,2,3,4,5,6,7 };
	v1.push_back(8);
	v1.insert(v1.begin(), 0);//传进去的是迭代器
	Print(v1);
	//insert迭代器是支持加的,如果我们想在下标为3的地方插入数字x
	//下标是从0开始的
	int x;
	cin >> x;
	v1.insert(v1.begin() + 3, x);

	v1.erase(v1.begin());//头删
	v1.erase(v1.begin() + 3);//删除下标为3的数字
}

【访问元素】常用函数

1. [] → 下标访问(最快,不检查越界)

cpp 复制代码
int x = v[0]; // 访问第一个元素
v[0] = 100;   // 修改第一个元素

2. at(i) → 下标访问(会检查越界,更安全)

cpp 复制代码
int x = v.at(0);

3. front() → 访问第一个元素

cpp 复制代码
v.front(); // 等价 v[0]

4. back() → 访问最后一个元素

cpp 复制代码
v.back(); // 等价 v[v.size()-1]

【遍历 / 迭代器】常用函数

1. begin() → 指向第一个元素的迭代器

2. end() → 指向最后一个元素下一位的迭代器

遍历示例(最常用):

cpp 复制代码
// 方式1:for循环(推荐)
for(int i=0; i<v.size(); i++) cout << v[i];

// 方式2:范围for(C++11,最简单)
for(auto x : v) cout << x;

// 方式3:迭代器
for(auto it = v.begin(); it != v.end(); it++)
    cout << *it;

【其他实用函数】

1. swap(vec) → 交换两个 vector

cpp 复制代码
vector<int> a, b;
a.swap(b);

2. assign(n, val) → 赋值(覆盖原有数据)

cpp 复制代码
v.assign(5, 10); // 5个10
cpp 复制代码
#include<iostream>
#include<vector>
using namespace std;
void Print(const vector<int>& v)
{
	for (auto x : v)
	{
		cout << x << " ";
	}
	cout << endl;
}
int main()
{
	vector<int> a, b;
	a.emplace_back(1);
	a.emplace_back(3);
	a.emplace_back(5);
	a.emplace_back(7);
	b.emplace_back(2);
	b.emplace_back(4);
	b.emplace_back(6);
	b.emplace_back(8);

	//将a和b的全部元素进行交换
	a.swap(b);
	Print(a);
	Print(b);

	//assign替换机制是会先清空数组然后再覆盖
	a.assign(3, 7);//将a里面的元素替换成3个7
	Print(a);
	a.swap(b);
	Print(a);
	Print(b);

	return 0;
}

高频使用速记表

功能 函数
尾部添加 push_back(x)
尾部删除 pop_back()
获取大小 size()
判断为空 empty()
访问第 i 个元素 v[i] / v.at(i)
访问首尾 front() / back()
指定位置插入 insert(pos, x)
删除指定位置 erase(pos)
清空所有元素 clear()

三、尝试手搓vector

大家这部分尝试去看注释理解,如果理解不了其实也不影响我们后面去做一些项目

vector.h

cpp 复制代码
#pragma once
#include<iostream>
#include<assert.h>
using namespace std;

namespace Byte
{
	template<class T>
	class vector
	{
	public:
		//typedef T* iterator;
		using iterator = T*;
		using const_iterator = const T*;
		using value_type = T;  // 添加 value_type 类型别名

		vector()
			//:_start(nullptr)
			//, _finish(nullptr)
			//, _end_of_storage(nullptr)
		{
		}

		//这是vector的构造函数:初始化列表构造
		//就是让你能直接用花括号给vector批量初始化
		//initializer_list<value_type>
		//initializer_list: C++专门用来接收大括号{ ... }列表的标准类型
		//value_type:就是vector存的元素类型,比如int、string
		//参数名il就是随便起的,写成lst也一样
		vector(initializer_list<value_type> il)
		{
			reserve(il.size());
			for (const auto& e : il)
			{
				push_back(e);
			}
		}

		//析构
		~vector()
		{
			if (_start != nullptr)  // 修改:0 改为 nullptr
			{
				delete[] _start;
				_start = _finish = _end_of_storage = nullptr;
			}
		}

		//传统写法
		//拷贝构造v2(v1)
		//这里看似只有一个参数但是有一个隐含的this指针
		//vector(const vector<T>& v)
		//{
		//	reserve(v.capacity());
		//	for (const auto& e : v)
		//	{
		//		push_back(e);
		//	}
		//}

		//v1 = v3
		//vector<T>& operator=(const vector<T>& v)
		//{
		//	if (this != &v)
		//	{
		//		clear();
		//		reserve(v.capacity());
		//		for (const auto& e : v)
		//		{
		//			push_back(e);
		//		}
		//	}
		//	return *this;
		//}

		//n个val的初始化
		vector(size_t n, const T& val = T())
		{
			reserve(n);
			for (size_t i = 0; i < n; i++)
			{
				push_back(val);
			}

		}

		//函数模板,迭代器不一定是vector迭代器,也可以是其他容器的迭代器
		template<class InputIterator>
		vector(InputIterator first, InputIterator last)
		{
			//reserve(last - first);
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}

		//现代写法
		vector(const vector<T>& v)
		{
			vector<T> tmp(v.begin(), v.end());
			swap(tmp);
		}

		//v1 = v3
		vector<T>& operator=(vector<T> tmp)
		{
			swap(tmp);
			return *this;
		}

		void swap(vector<T>& v)
		{
			std::swap(_start, v._start);
			std::swap(_finish, v._finish);
			std::swap(_end_of_storage, v._end_of_storage);  // 修改:_end_of_start 改为 _end_of_storage
		}

		void clear()
		{
			_finish = _start;
		}

		//判空
		bool empty() const
		{
			return (_start == _finish);
		}

		//初始位置的迭代器
		iterator begin()
		{
			return _start;
		}

		//结束位置的迭代器
		iterator end()
		{
			return _finish;
		}

		//重载一个const版本防止权限放大
		//初始位置的迭代器
		const_iterator begin() const
		{
			return _start;
		}

		//结束位置的迭代器
		const_iterator end() const
		{
			return _finish;
		}

		//扩容
		void reserve(size_t n)
		{
			//不去缩容,只去扩容
			if (n > capacity())
			{
				size_t sz = size();
				T* tmp = new T[n];
				if (_start != nullptr)
				{
					// 修改:memcpy 改为循环拷贝,支持非POD类型
					for (size_t i = 0; i < sz; ++i)
					{
						new (tmp + i) T(_start[i]);  // placement new 拷贝构造
						_start[i].~T();               // 析构原对象
					}
					delete[] _start;
				}
				_start = tmp;
				_finish = _start + sz;
				_end_of_storage = _start + n;
			}
		}

		//这里后面一个参数可以看成给了一个缺省值
		//做缺省参数:字面量常量、全局变量/类的静态成员、匿名对象
		void resize(size_t n, T val = T())
		{
			if (n < size())
			{
				//删除数据
				_finish = _start + n;
			}
			else
			{
				//扩容
				reserve(n);
				//插入数据
				while (_finish < _start + n)
				{
					*_finish = val;  // 修改:x 改为 val
					++_finish;
				}
			}
		}

		//存储空间
		size_t capacity() const
		{
			return _end_of_storage - _start;
		}

		//数据个数
		size_t size() const
		{
			return _finish - _start;
		}

		//对于普通对象可以读可以写
		T& operator[](size_t i)
		{
			assert(i < size());
			return _start[i];
		}

		//针对const对象,只可以读
		const T& operator[](size_t i) const
		{
			assert(i < size());
			return _start[i];
		}

		//头插
		void push_back(const T& x)
		{
			//没有多余空间
			if (_finish == _end_of_storage)
			{
				//如果capacity是0的话就开一个大小为2字节的空间,如果不为0就二倍扩容
				reserve(capacity() == 0 ? 2 : capacity() * 2);
			}
			*_finish = x;
			++_finish;
		}

		//头删
		void pop_back()
		{
			assert(!empty());
			--_finish;
		}

		//在pos位置插入一个x
		void insert(iterator pos, const T& x)
		{
			assert(pos >= _start);
			assert(pos <= _finish);
			//扩容
			if (_finish == _end_of_storage)
			{
				//原来我们扩容
				//reserve(capacity() == 0 ? 4 : capacity() * 2);
				size_t len = pos - _start;
				reserve(capacity() == 0 ? 4 : capacity() * 2);
				pos = _start + len;
			}
			//挪动数据
			iterator end = _finish - 1;
			while (end >= pos)
			{
				*(end + 1) = *end;
				--end;
			}
			*pos = x;
			++_finish;
		}
		//如果我们按照原方式去扩容这里会造成一个问题叫做迭代器失效,我们pos假设是在中间位置
		//现在我们要去进行扩容,但是pos指向的还是上面的位置(已经被释放了),不在这个新的空间中
		//所以就会造成迭代器失效,所以我们现在要让pos指向新的空间中来(如果不扩容的话就没有这个问题)

		//删除pos位置的值 - 带返回值的版本(C++标准做法)
		//返回值:返回被删除元素下一个位置的有效迭代器(其实就是pos位置)
		iterator erase(iterator pos)
		{
			assert(pos >= _start);
			assert(pos < _finish);  // 修改:<= 改为 <,因为不能删除 end()

			iterator it = pos + 1;
			while (it != _finish)
			{
				*(it - 1) = *it;
				++it;
			}

			--_finish;

			return pos;
		}

	private:
		iterator _start = nullptr;
		iterator _finish = nullptr;
		iterator _end_of_storage = nullptr;
	};
}

test.c

cpp 复制代码
#include"vector.h"

namespace Byte
{
	void Print(const vector<int>& v)
	{
		for (auto iu : v)
		{
			cout << iu << " ";
		}
		cout << endl;

		for (size_t i = 0; i < v.size(); i++)
		{
			cout << v[i] << " ";
		}
		cout << endl;
	}

	void test_vector1()
	{

		Byte::vector<int> v;
		v.push_back(1);
		v.push_back(2);
		v.push_back(0);
		v.push_back(6);

		Print(v);
	}

	void test_vactor2()
	{
		Byte::vector<int> v;
		v.push_back(1);
		v.push_back(2);
		v.push_back(0);
		v.push_back(6);

		Print(v);

		v.insert(v.begin(), 0);
		Print(v);

		auto it = v.begin() + 3;
		//it作为实参传给形参,pos位置的修改不会影响我们it
		//扩容才会失效,但是我们不知道他是否扩容
		//所以无论是否扩容我们都当作it失效了,形参的改变不影响实参
		//这也就意味着insert之后it就不能使用了
		v.insert(it, 30);
		Print(v);
	}

	void test_vactor3()
	{
		Byte::vector<int> v;
		v.push_back(1);
		v.push_back(2);
		v.push_back(0);
		v.push_back(6);

		Print(v);

		v.erase(v.begin());
		Print(v);

		auto it = v.begin() + 2;
		//如果这个it在末尾就会失效,不能访问,访问结果未定义
		v.erase(it);
		Print(v);
		//vs2019及其之后的版本会严格检查,大家可以简单记只要erase以后迭代器对象就失效了,不能访问
	}

	//深浅拷贝
	void test_vector4()
	{
		Byte::vector<int>v1;
		v1.push_back(1);
		v1.push_back(2);
		v1.push_back(0);
		v1.push_back(6);
		for (auto e : v1)
		{
			cout << e << " ";
		}
		cout << endl;

		Byte::vector<int> v2(v1);
		for (auto e : v2)
		{
			cout << e << " ";
		}
		cout << endl;
		//我们不写拷贝构造这里也可以运行出结果但是系统会崩溃

		//调用vector(initializer_list<value_type> il);
		//Byte::vector<int> v3({ 10,20,30,40 });
		
		//这里其实也是一个浅拷贝,我们要自己写一个深拷贝
		Byte::vector<int> v3 = { 10,20,30,40 };
		v1 = v3;
		for (auto e : v1)
		{
			cout << e << " ";
		}
		cout << endl;

		Byte::vector<int> v4(10u, 1);
		for (auto e : v4)
		{
			cout << e << " ";
		}
		cout << endl;
	}

	void test_vector5()
	{
		Byte::vector<string> v1;
		v1.push_back("111111111111111111111");
		v1.push_back("222222222222222222222");
		for (auto& e : v1)
		{
			cout << e << "";
		}
		cout << endl;
	}

}

int main()
{
	Byte::test_vector1();
	return 0;
}

四、迭代器失效

我们在尝试手搓vector的时候经常会出现一个问题叫做迭代器失效,我们之前根本没遇到过这个问题,下面我们就来详细说说什么是迭代器失效:

一、简单记忆规则

我们先简单记:只要erase以后迭代器对象就失效了就不能访问,vector为了防止迭代器失效给erase了一个放回值

只要调用了 eraseinsert,之前所有的迭代器都不能再用了。

这个规则:

  • 100% 安全(永远不会出现迭代器失效导致的 bug)

  • 容易记忆(不用记复杂的失效规则)

  • 适用于所有情况(包括扩容、不扩容、插入在前在后)

代价只是:

  • ⚠️ 可能错过一些"实际上仍然有效"的情况(但安全第一)

二、严格来说的正确规则

erase 的精确失效规则:

cpp 复制代码
vector<int> v = {10, 20, 30, 40, 50};
auto it1 = v.begin();      // 指向 10
auto it2 = v.begin() + 2;  // 指向 30
auto it3 = v.begin() + 3;  // 指向 40

v.erase(v.begin() + 2);  // 删除 30

// 结果:
// it1 仍然有效(指向 10)  ✅ 没失效
// it2 失效了(指向已删除元素)❌
// it3 失效了(指向 40,但后面元素前移了)❌

精确规则:被删除元素及之后的所有迭代器失效,之前的仍然有效。

insert 的精确失效规则:

cpp 复制代码
vector<int> v = {10, 20, 30};
v.reserve(100);  // 确保不扩容
auto it1 = v.begin();      // 指向 10
auto it2 = v.begin() + 1;  // 指向 20

v.insert(v.begin() + 1, 99);  // 插入到 20 前面

// 结果:
// it1 仍然有效(指向 10)✅
// it2 失效了(元素移动了)❌

精确规则:插入点及之后的所有迭代器失效,之前的仍然有效(前提是不扩容)。

三、erase的返回值

cpp 复制代码
iterator erase(iterator pos);          // 删除单个元素
iterator erase(iterator first, iterator last);  // 删除区间

返回值: 返回被删除元素下一个位置的有效迭代器。

四、典型用法(安全删除)

cpp 复制代码
vector<int> v = {1, 2, 3, 4, 5};

// ❌ 错误:迭代器失效
for (auto it = v.begin(); it != v.end(); ++it) {
    if (*it % 2 == 0) {
        v.erase(it);  // it 失效,++it 崩溃
    }
}

// ✅ 正确:使用返回值
for (auto it = v.begin(); it != v.end(); ) {
    if (*it % 2 == 0) {
        it = v.erase(it);  // it 被更新为下一个有效位置
    } else {
        ++it;
    }
}
// 结果:v = {1, 3, 5}

五、erase 返回值的精确行为

cpp 复制代码
vector<int> v = {10, 20, 30, 40, 50};
auto it = v.begin() + 2;  // 指向 30

auto new_it = v.erase(it);  // 删除 30

// new_it 现在指向 40(被删除元素的下一个)
cout << *new_it << endl;  // 输出 40

图示:

cpp 复制代码
删除前: [10, 20, 30, 40, 50]
              ↑
             it

删除后: [10, 20, 40, 50]
              ↑
           new_it (指向原来30的下一个元素)

六、insert 也有返回值(C++11 起)

cpp 复制代码
iterator insert(iterator pos, const T& value);
// 返回指向新插入元素的迭代器
cpp 复制代码
vector<int> v = {1, 2, 4, 5};
auto it = v.begin() + 2;  // 指向 4

it = v.insert(it, 3);  // 插入 3,it 被更新为指向新元素
cout << *it << endl;   // 输出 3

// 现在可以继续使用 it
it = v.insert(it + 2, 99);  // 安全

相关推荐
SilentSamsara2 小时前
生成器完全指南:`yield` 与惰性求值的工程价值
linux·开发语言·python·算法·机器学习·青少年编程
玛卡巴卡ldf2 小时前
【LeetCode 手撕算法】(二分查找)搜索插入位置、搜索二维矩阵、查找数组相同的所有位置、搜索旋转排序数组、旋转升序数组的最小值
数据结构·算法·leetcode
谷雨不太卷9 小时前
进程的状态码
java·前端·算法
顾温9 小时前
default——C#/C++
java·c++·c#
凉茶钱10 小时前
【c语言】动态内存管理:malloc,calloc,realloc,柔性数组
c语言·c++·vscode·柔性数组
脏脏a10 小时前
【C++模版】泛型编程:代码复用的终极利器
开发语言·c++·c++模版
island131410 小时前
【C++仿Muduo库#3】Server 服务器模块实现上
服务器·开发语言·c++
散峰而望10 小时前
【算法竞赛】C/C++ 的输入输出你真的玩会了吗?
c语言·开发语言·数据结构·c++·算法·github
小龙报10 小时前
【C语言】内存里的 “数字变形记”:整数三码、大小端与浮点数存储真相
c语言·开发语言·c++·创业创新·学习方法·visual studio