C++ STL vector基本原理和用法

文章目录

  • 基本原理
      • 1. 数据存储结构
      • 2. 内存管理机制
      • 3. 迭代器实现原理
      • 4. 元素访问原理
      • 5. 插入和删除元素原理
  • 常见用法
      • 1. 概述
      • 2. 包含头文件
      • 3. 定义和初始化
      • 4. 常用成员函数
      • 5. 迭代器
      • 6. 内存管理与性能特点
      • 7. 应用场景

基本原理

以下是关于 std::vector 的基本原理讲解:

1. 数据存储结构

std::vector 在内部通常是使用一段连续的内存空间来存储元素,这一点和普通的C风格数组类似。这种连续存储的方式使得它能够实现快速的随机访问,因为可以通过简单的指针运算(基于元素类型的大小)来定位到任意位置的元素,例如,对于一个存储 int 类型元素的 vector,如果知道首地址,要访问索引为 n 的元素,在内存中就是首地址加上 n * sizeof(int) 的偏移量。

2. 内存管理机制

  • 初始内存分配
    当创建一个 vector 时,它并不会立即分配很大的内存空间,通常会分配一个较小的初始容量(不同的标准库实现可能初始容量不同,有些可能是0,有些可能是一个较小的固定值,比如4或者8等)。例如,定义 std::vector<int> myVec; 时,它可能刚开始并没有分配实际可存储元素的内存块,只是进行了一些必要的内部状态初始化。

  • 扩容机制
    随着不断向 vector 中添加元素(通过 push_back 等操作),当元素个数达到当前容量上限时,vector 就需要进行扩容操作以容纳更多元素。扩容过程一般涉及以下几个步骤:

    • 新内存分配:它会按照一定的策略分配一块更大的连续内存空间,常见的扩容倍数是1.5倍或者2倍左右(具体倍数由不同的STL实现决定)。例如,当前 vector 的容量是4,已经存储了4个元素,当再调用 push_back 添加第5个元素时,如果按照2倍扩容策略,就会另外开辟一块能容纳8个元素的新内存空间。
    • 元素复制或移动:接着把原来内存空间中存储的所有元素复制或者移动(如果元素类型支持移动语义,在C++11及以后通常优先采用移动,以提高效率)到新分配的内存空间中。这一步涉及对每个元素的操作,如果元素类型的拷贝构造函数或者移动构造函数开销较大,那么扩容操作的整体开销也会相应增大。
    • 释放旧内存:在成功将元素迁移到新内存空间后,会释放原来的那块内存空间,以避免内存泄漏。
  • 缩容机制(可选)
    有些 vector 的实现提供了手动缩容或者在特定条件下自动缩容的机制,不过不是所有标准库都保证有自动缩容功能。比如,当调用 vectorclear 函数清空所有元素后,部分实现可能会自动释放多余的内存空间,将容量减小;也可以通过类似 shrink_to_fit 这样的函数(如果标准库支持)来显式要求 vector 释放多余内存,使其容量尽量接近当前元素个数,不过这个函数只是一个建议性的操作,具体是否能真正缩容以及缩容到何种程度还是依赖具体的实现。

3. 迭代器实现原理

vector 的迭代器本质上是一种类似指针的对象,它重载了一些必要的运算符(如 *(解引用)、++(前置和后置自增)、--(前置和后置自减)、!=(不等比较)等),使其能够像指针一样遍历 vector 中的元素。

  • 普通迭代器(beginend 等返回的迭代器)
    其内部通常就是直接持有指向 vector 中元素所在内存位置的指针。例如,begin 函数返回的迭代器指向 vector 的第一个元素所在内存地址,end 函数返回的迭代器指向最后一个元素后面的内存位置(形成左闭右开区间用于遍历等操作符合STL的通用约定)。当对迭代器进行自增操作时,就是按照元素类型的大小在内存中向后移动指针到下一个元素的位置,自减操作则相反,向前移动指针。

  • 反向迭代器(rbeginrend 等返回的迭代器)
    反向迭代器的实现相对复杂一些,它一般会对普通迭代器进行适配,内部通过一些机制来实现从后往前遍历的效果。实际上,它可以看作是对普通迭代器的一种“包装”,在自增、自减等操作的语义上进行了反转,使其能够从 vector 的末尾元素开始,向开头方向依次访问元素。

4. 元素访问原理

  • 下标访问(operator[]
    这个操作符重载的实现就是基于 vector 内部元素连续存储的特性,通过简单的指针算术运算来实现快速访问。它接收一个索引值作为参数,在内部会将这个索引值与元素类型的大小相乘(例如对于 int 类型就是乘以 sizeof(int)),然后加上首元素的地址(也就是 vector 内部管理的那块连续内存空间的起始地址),最后返回这个计算得到的内存地址对应的元素的引用,不过它不会进行边界检查,所以如果使用不当,访问越界就会导致未定义行为。

  • at 函数访问
    at 函数和 operator[] 类似也是用于访问元素,但它内部会首先进行边界检查,判断传入的索引值是否在合法的范围(即大于等于0且小于 vector 的大小)内,如果越界则会抛出 std::out_of_range 类型的异常,避免了因越界访问带来的潜在错误隐患,相对更加安全。

5. 插入和删除元素原理

  • 末尾插入(push_back
    在末尾添加元素时,如果当前 vector 还有剩余空间(即元素个数小于容量),那么直接在已有内存空间的末尾位置构造新元素即可,这个过程相对简单高效;但如果已经满容量需要扩容,就会触发上述的扩容流程后再添加元素。

  • 中间或开头插入(insert
    当要在 vector 的中间或者开头位置插入元素时,需要先将插入位置及之后的所有元素依次向后移动,为新插入的元素腾出空间,然后再在腾出的位置构造新元素。元素移动的过程同样涉及到对每个元素的拷贝或移动操作,所以在元素个数较多的 vector 中进行这类插入操作相对来说效率较低,时间复杂度是线性的 O(n),其中 nvector 的元素个数。

  • 删除元素(pop_backerase 等)
    pop_back 操作相对简单,只是销毁 vector 末尾的元素(调用其析构函数,如果有的话),然后将元素个数减1即可;而 erase 操作在删除指定位置的元素时,需要将该位置后面的所有元素依次向前移动来填补删除元素后留下的空位,同样涉及到多个元素的拷贝或移动,效率方面也会受 vector 元素个数影响,在中间或开头位置删除元素时间复杂度为 O(n)

总之,std::vector 通过巧妙的内存管理、迭代器设计以及对各种操作的底层实现,为开发者提供了一个方便、灵活且功能强大的动态数组容器,在了解其基本原理后能更好地掌握它的使用场景、性能特点以及避免一些因不当使用而可能出现的问题。 以下是对C++ STL(标准模板库)中vector的详细讲解:

常见用法

1. 概述

vector是C++ STL中非常常用的一个容器类,它提供了动态大小的数组功能,能够自动管理所存储元素的内存分配与释放,并且支持快速的随机访问。简单来说,你可以把它看作是一种可以根据需要自动扩容和缩容的数组,使用起来比普通的C风格数组更加方便、安全和灵活,广泛应用于各种需要存储和操作一组同类型元素的场景中。

2. 包含头文件

要使用vector容器,需要在代码中包含<vector>头文件,例如:

#include <vector>

3. 定义和初始化

  • 基本定义
    定义一个vector对象的语法形式通常为 std::vector<元素类型> 变量名;,这里的“元素类型”可以是C++中的各种基本数据类型(如intdoublechar等),也可以是自定义的结构体、类等类型。例如,定义一个存储整数的vector
std::vector<int> myVector;
  • 初始化方式
    • 默认初始化:像上面那样定义后,vector会被默认初始化为空,即其中不包含任何元素,其大小(通过size()成员函数获取)为0。
    • 指定初始大小和初始值:可以在定义时指定vector的初始大小以及每个元素的初始值,语法为 std::vector<元素类型> 变量名(元素个数, 初始值);。例如,创建一个包含5个初始值都为0的整数的vector
std::vector<int> anotherVector(5, 0);

此时,anotherVector的大小为5,并且每个元素的值都是0。
- 使用初始化列表初始化:利用花括号{}包裹元素来初始化vector,这种方式在C++11及以后的版本中更加常用和方便。例如:

std::vector<int> initListVector = {1, 2, 3};

这个vector包含了3个元素,分别是1、2和3。
- 从其他vector复制或移动初始化:可以通过已有的vector对象来初始化一个新的vector,实现复制或移动语义(如果适用)。例如:

std::vector<int> sourceVector = {1, 2, 3};
// 复制初始化
std::vector<int> copiedVector(sourceVector);
// 移动初始化(假设C++11及以上,利用std::move函数实现移动语义)
std::vector<int> movedVector(std::move(sourceVector));

复制初始化会创建一个新的vector,其元素与源vector完全相同;而移动初始化则是将源vector的资源(如内存空间等)转移给新的vector,源vector在移动后通常处于一种有效但已转移资源的特殊状态(比如大小变为0等)。

4. 常用成员函数

  • 添加和删除元素相关函数
    • push_back():用于在vector的末尾添加一个元素。例如:
std::vector<int> numbers;
numbers.push_back(5);  // 在末尾添加整数5
numbers.push_back(10); // 再添加整数10,此时numbers包含两个元素,分别是5和10
- **`pop_back()`**:与`push_back()`相反,它用于删除`vector`末尾的一个元素。例如:
std::vector<int> nums = {1, 2, 3};
nums.pop_back();  // 删除末尾的元素3,此时nums包含元素1和2
- **`insert()`**:可以在指定位置插入一个或多个元素。它有多种重载形式,常见的一种是 `iterator insert(iterator position, const T& value);`,其中`position`是指向插入位置的迭代器(可以通过`begin()`、`end()`等函数获取迭代器来指定位置),`value`是要插入的元素值。例如:
std::vector<int> vec = {1, 3};
auto it = vec.begin() + 1;  // 指向索引为1的位置(也就是元素3所在位置)
vec.insert(it, 2);  // 在索引为1的位置插入元素2,此时vec变为{1, 2, 3}

还可以一次插入多个相同元素,比如 iterator insert(iterator position, size_type n, const T& value); 形式可以在指定位置插入n个相同的value元素。
- erase():用于删除指定位置的一个或多个元素,同样有多种重载形式。例如,删除单个元素可以这样用:iterator erase(iterator position);,删除一段元素区间可以用 iterator erase(iterator first, iterator last);,其中firstlast分别是要删除区间的起始和结束迭代器(左闭右开区间)。例如:

std::vector<int> v = {1, 2, 3, 4};
auto it = v.begin() + 1;  // 指向元素2
v.erase(it);  // 删除元素2,此时v变为{1, 3, 4}

删除一段元素示例:

std::vector<int> ve = {1, 2, 3, 4, 5};
auto start = ve.begin() + 1;  // 指向元素2
auto end = ve.begin() + 3;  // 指向元素4后面的位置(左闭右开区间概念)
ve.erase(start, end);  // 删除元素2和3,此时ve变为{1, 4, 5}
  • 访问元素相关函数
    • at():用于安全地访问vector中的元素,它会进行边界检查,如果访问越界会抛出 std::out_of_range 异常。语法为 reference at(size_type n);,其中n是要访问元素的索引(从0开始计数)。例如:
std::vector<int> values = {10, 20, 30};
try {int element = values.at(1);  // 获取索引为1的元素,即20std::cout << element << std::endl;
} catch (const std::out_of_range& e) {std::cerr << "访问越界: " << e.what() << std::endl;
}
- **`operator[]`(下标运算符)**:也用于访问元素,它与数组的下标访问方式类似,但不会进行边界检查,如果访问越界会导致未定义行为,所以使用时要确保索引在合法范围内。例如:
std::vector<int> data = {5, 10};
int element = data[0];  // 获取第一个元素5,注意这里没有边界检查,需谨慎使用
- **`front()`**:返回`vector`的第一个元素的引用,可以用于获取或修改第一个元素的值。例如:
std::vector<int> firstVector = {1, 2};
int firstElement = firstVector.front();  // 获取第一个元素1
firstVector.front() = 10;  // 将第一个元素修改为10,此时firstVector变为{10, 2}
- **`back()`**:与`front()`相对,返回`vector`的最后一个元素的引用,同样可用于获取或修改最后一个元素的值。例如:
std::vector<int> lastVector = {3, 4};
int lastElement = lastVector.back();  // 获取最后一个元素4
lastVector.back() = 40;  // 将最后一个元素修改为40,此时lastVector变为{3, 40}
  • 获取容器信息相关函数
    • size():返回vector中当前元素的个数,返回值类型是 std::vector<元素类型>::size_type(通常是无符号整数类型)。例如:
std::vector<int> sizeVector = {1, 2, 3};
std::vector<int>::size_type elementCount = sizeVector.size();  // 获取元素个数,这里elementCount的值为3
- **`capacity()`**:返回`vector`当前已经分配的内存空间能够容纳的元素个数,也就是它的容量。一般来说,随着不断向`vector`中添加元素,当元素个数接近容量时,`vector`会自动进行扩容操作来分配更多的内存空间。例如:
std::vector<int> capVector;
std::cout << "初始容量: " << capVector.capacity() << std::endl;  // 初始容量可能为0或者一个较小的值,取决于实现
capVector.push_back(1);
capVector.push_back(2);
std::cout << "添加元素后的容量: " << capVector.capacity() << std::endl;  // 容量可能已经自动扩容了
- **`empty()`**:判断`vector`是否为空,如果为空(即元素个数为0)则返回`true`,否则返回`false`。例如:
std::vector<int> emptyVector;
if (emptyVector.empty()) {std::cout << "该vector为空" << std::endl;
}
std::vector<int> nonEmptyVector = {1};
if (!nonEmptyVector.empty()) {std::cout << "该vector不为空" << std::endl;
}
  • 其他常用函数
    • clear():用于清空vector中的所有元素,将其大小变为0,但不会释放已分配的内存空间(容量可能不会改变,除非后续有添加或删除元素等操作触发了内存重新分配)。例如:
std::vector<int> clearVector = {1, 2, 3};
clearVector.clear();
if (clearVector.empty()) {std::cout << "已清空vector,现在为空" << std::endl;
}
- **`resize()`**:可以改变`vector`的大小,如果新大小大于当前大小,会根据元素类型的默认构造函数(对于内置类型可能会进行未定义初始化,对于类类型会调用默认构造函数)或者指定的初始值来填充新增的元素;如果新大小小于当前大小,则会删除多余的元素。例如:
std::vector<int> resizeVector(3, 1);  // 初始化为{1, 1, 1}
resizeVector.resize(5, 0);  // 扩大为{1, 1, 1, 0, 0},新增元素初始化为0
resizeVector.resize(2);  // 缩小为{1, 1},删除后面的元素

5. 迭代器

vector支持迭代器来遍历容器中的元素,迭代器就像是指向容器中元素的指针,可以通过它来依次访问每个元素。常用的迭代器相关操作有:

  • begin()end()begin()返回指向vector第一个元素的迭代器,end()返回指向vector最后一个元素后面位置的迭代器(形成一个左闭右开的区间概念,用于循环遍历等操作符合STL的通用设计模式)。例如,使用迭代器遍历vector的常见方式如下:
std::vector<int> iterVector = {1, 2, 3};
for (auto it = iterVector.begin(); it!= iterVector.end(); ++it) {std::cout << *it << " ";  // 输出每个元素,*it用于解引用迭代器获取元素值
}
// 输出: 1 2 3
  • rbegin()rend():这两个函数分别返回反向迭代器,用于从后往前遍历vectorrbegin()指向最后一个元素,rend()指向第一个元素前面的位置(同样是左闭右开区间概念)。例如:
std::vector<int> reverseIterVector = {4, 5, 6};
for (auto rit = reverseIterVector.rbegin(); rit!= reverseIterVector.rend(); ++rit) {std::cout << *rit << " ";  // 从后往前输出元素,这里输出: 6 5 4
}

6. 内存管理与性能特点

  • 内存自动管理vector会自动处理内存的分配和释放,当添加元素导致当前分配的内存空间不足时,它会自动进行扩容操作,通常是按照一定的倍数(不同的STL实现可能有不同的扩容策略,常见的是1.5倍或2倍等)分配新的更大的内存空间,然后将原有元素复制或移动到新空间中,并释放原来的内存。这种自动管理机制方便了开发者,但也可能在频繁扩容时带来一定的性能开销(尤其是元素类型的拷贝或移动成本较高时)。
  • 随机访问性能好:由于vector内部元素在内存中是连续存储的,类似于普通的数组,所以它支持快速的随机访问,通过下标运算符或者迭代器解引用访问元素的时间复杂度为常数时间 O(1)
  • 插入和删除元素的性能特点:在vector末尾添加或删除元素(通过push_back()pop_back())的操作通常比较高效,时间复杂度接近常数时间(平均情况,不考虑偶尔的扩容操作带来的影响);但在中间或者开头位置插入或删除元素(通过insert()erase())可能相对较慢,因为需要移动插入或删除位置后面的所有元素,时间复杂度为线性时间 O(n),其中nvector中元素的个数。

7. 应用场景

  • 存储一组同类型的数据:比如存储游戏中的玩家分数列表、学生的成绩数组等,方便进行数据的添加、删除、修改以及遍历统计等操作。
  • 作为函数参数传递数组:在函数间传递数组时,如果数组大小不确定或者可能会变化,使用vector作为参数传递要比传递普通C风格数组更加方便、安全,避免了传递数组长度等额外参数以及数组越界等风险。例如:
void processVector(const std::vector<int>& vec) {// 在这里可以安全地遍历vec等操作,不用担心越界问题for (int element : vec) {std::cout << element << " ";}
}
int main() {std::vector<int> data = {1, 2, 3};processVector(data);return 0;
}
  • 配合其他STL算法使用vector可以很好地与STL中的各种算法(如排序算法std::sort、查找算法std::find等)配合使用,实现更复杂的数据处理功能。例如:
#include <algorithm>
std::vector<int> numbers = {5, 3, 1, 4, 2};
std::sort(numbers.begin(), numbers.end());  // 对vector中的元素进行排序

总之,vector是C++ STL中一个功能强大、使用方便的容器,掌握它的各种特性和使用方法对于高效地进行C++编程非常有帮助,能够满足很多常见的动态数组相关的编程需求。

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

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

相关文章

使用openvino加速部署paddleocr文本检测模型(C++版)

使用openvino加速部署paddleocr文本检测模型(C++版) 引言1,前处理2,后处理3,C++部署代码引言 文本检测在openvino部署端的前后处理与在paddleocr中的不太一样。 1,前处理 在将文本检测的模型转换成onnx格式(输入输出大小都已固定),并部署到openvino后,其预处理后的输…

2021-04-08 VSC++: 降序折半查找。

void 降序折半查找(int* a, int aa, int aaa) {//缘由https://bbs.csdn.net/topics/399166569int aaaa aaa / 2; bool k 0;if (a[0] aa){cout << 0, cout << ends << "查找&#xff1a;" << aa << endl;k 1;return;}else if (a[aa…

MySQL三层B+树能存多少数据

结论 bigint类型的索引&#xff08;8字节&#xff09;&#xff0c;一条数据假设是1KB的话&#xff0c; 三层B树 能存2000万条数据 该题主要考察数据如何在B树中存储的 计算思路 1.计算叶节点的大小 2.计算子节点的个数&#xff0c;由此算出第三层叶子节点的个数&#xff08;n*n…

win系统B站播放8k视频启用HEVC编码

下载HEVC插件 点击 HEVC Video Extension 2.2.20.0 latest downloads&#xff0c;根据教程下载安装 安装 Random User-Agent 点击 Random User-Agent 安装 配置 Random User-Agent ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/dda0ea75096c42c0a79ef6f6f5521…

中伟视界:AI识别摄像头+AI预警平台在矿山皮带空载监测中的应用

在矿山开采和矿物处理过程中&#xff0c;皮带运输机扮演着举足轻重的角色。它们负责将矿石、煤炭等物料从一处运送到另一处&#xff0c;是矿山生产流程中不可或缺的一环。然而&#xff0c;皮带运输机在运行过程中也面临着一些挑战&#xff0c;其中之一便是皮带空载问题。皮带空…

实训项目-人力资源管理系统-1Company子模块

目录 前言&#xff1a; 用例图设计&#xff1a; 系统设计 开发方式&#xff1a; 技术架构 系统结构&#xff1a; API文档&#xff1a; 工程搭建&#xff1a; 搭建父项目 pom&#xff1a; 创建公共子模块&#xff1a; 返回实体&#xff1a; 分布式id生成器&#xff1a; …

2.5.3 文件使用、共享、保护、安全与可靠性

文章目录 文件使用文件共享文件保护系统安全与可靠性 文件使用 操作系统向用户提供操作级、编程级文件服务。 操作级服务包括目录管理&#xff0c;文件操作&#xff08;复制、删除、修改&#xff09;&#xff0c;文件管理&#xff08;设置文件权限&#xff09;。 编程级服务包括…

路由器转发数据报的封装过程

✍作者&#xff1a;柒烨带你飞 &#x1f4aa;格言&#xff1a;生活的情况越艰难&#xff0c;我越感到自己更坚强&#xff1b;我这个人走得很慢&#xff0c;但我从不后退。 &#x1f4dc;系列专栏&#xff1a;网路安全入门系列 目录 路由器转发数据的封装过程 路由器转发数据的封…

vulnhub matrix-breakout靶机

1.搭建靶机 这样就是装好了 获取靶机IP nmap -O 192.168.47.129/24 2.信息收集 dirb http://192.168.47.128 dirb 首页 81端口一个登录页面 gobuster dir -u http://192.168.152.154 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php,txt,html gra…

微信小程序 不同角色进入不同页面、呈现不同底部导航栏

遇到这个需求之前一直使用的小程序默认底部导航栏&#xff0c;且小程序默认入口页面为pages/index/index&#xff0c;要使不同角色呈现不同底部导航栏&#xff0c;必须要在不同页面引用不同的自定义导航栏。本篇将结合分包&#xff08;subPackages&#xff09;展开以下三步叙述…

如何通过 Kafka 将数据导入 Elasticsearch

作者&#xff1a;来自 Elastic Andre Luiz 将 Apache Kafka 与 Elasticsearch 集成的分步指南&#xff0c;以便使用 Python、Docker Compose 和 Kafka Connect 实现高效的数据提取、索引和可视化。 在本文中&#xff0c;我们将展示如何将 Apache Kafka 与 Elasticsearch 集成以…

LLaMA-Factory GLM4-9B-CHAT LoRA 微调实战

&#x1f929;LLaMA-Factory GLM LoRA 微调 安装llama-factory包 git clone --depth 1 https://github.com/hiyouga/LLaMA-Factory.git进入下载好的llama-factory&#xff0c;安装依赖包 cd LLaMA-Factory pip install -e ".[torch,metrics]" #上面这步操作会完成…

view draw aosp15

基础/背景知识 如何理解Drawable&#xff1f; 在 Android 中&#xff0c;Drawable 是一个抽象的概念&#xff0c;表示可以绘制到屏幕上的内容。 它可以是位图图像、矢量图形、形状、颜色等。 Drawable 本身并不是一个 View&#xff0c;它不能直接添加到布局中&#xff0c;而是…

gridcontrol表格某一列设置成复选框,选择多行(repositoryItemCheckEdit1)

1. 往表格中添加repositoryItemCheckEdit1 2. 事件&#xff1a; repositoryItemCheckEdit1.QueryCheckStateByValue repositoryItemCheckEdit1_QueryCheckStateByValue; private void repositoryItemCheckEdit1_QueryCheckStateByValue(object sender, DevExpress.XtraEditor…

重温设计模式--适配器模式

文章目录 适配器模式&#xff08;Adapter Pattern&#xff09;概述适配器模式UML图适配器模式的结构目标接口&#xff08;Target&#xff09;&#xff1a;适配器&#xff08;Adapter&#xff09;&#xff1a;被适配者&#xff08;Adaptee&#xff09;&#xff1a; 作用&#xf…

C语言项目 天天酷跑(上篇)

前言 这里讲述这个天天酷跑是怎么实现的&#xff0c;我会在天天酷跑的下篇添加源代码&#xff0c;这里会讲述天天酷跑这个项目是如何实现的每一个思路&#xff0c;都是作者自己学习于别人的代码而创作的项目和思路&#xff0c;这个代码和网上有些许不一样&#xff0c;因为掺杂了…

公交车信息管理系统:构建智能城市交通的基石

程序设计 本系统主要使用Java语言编码设计功能&#xff0c;MySQL数据库管控数据信息&#xff0c;SSM框架创建系统架构&#xff0c;通过这些关键技术对系统进行详细设计&#xff0c;设计和实现系统相关的功能模块。最后对系统进行测试&#xff0c;这一环节的结果&#xff0c;基本…

MDS-NPV/NPIV

在存储区域网络&#xff08;SAN&#xff09;中&#xff0c;域ID&#xff08;Domain ID&#xff09;是一个用于区分不同存储区域的关键参数。域ID允许SAN环境中的不同部分独立操作&#xff0c;从而提高效率和安全性。以下是关于域ID的一些关键信息&#xff1a; 域ID的作用&…

【网络安全产品大调研系列】1. 漏洞扫描

1. 为什么会出现漏扫技术&#xff1f; 每次黑客攻击事件进行追溯的时候&#xff0c;根据日志分析后&#xff0c;我们往往发现基本都是系统、Web、 弱口令、配置这四个方面中的其中一个出现的安全问题导致黑客可以轻松入侵的。 操作系统的版本滞后&#xff0c;没有更新补丁&am…

验证 Dijkstra 算法程序输出的奥秘

一、引言 Dijkstra 算法作为解决图中单源最短路径问题的经典算法,在网络路由、交通规划、资源分配等众多领域有着广泛应用。其通过不断选择距离源节点最近的未访问节点,逐步更新邻居节点的最短路径信息,以求得从源节点到其他所有节点的最短路径。在实际应用中,确保 Dijkst…