ubuntu/vscode下的c/c++开发之-CMake语法与练习

Cmake学习

1 语法特性介绍

  • 基本语法格式:指令(参数 1 参数 2...)

    • 参数使用括弧括起
    • 参数之间使用空格分号分开
  • 指令是大小写无关的,参数和变量是大小写相关的

    set(HELLO hello.cpp)
    add_executable(hello main.cpp hello.cpp)
    ADD_EXECUTABLE(hello main.cpp ${HELLO})
    
  • 变量使用${}方式取值,但是在 IF 控制语句中是直接使用变量名 if (HELLO)

2重要指令和CMake常用变量

2.1 重要指令
  • cmake_minimum_required ,指定CMake的最小版本要求

    # CMake最小版本要求为2.8.3
    cmake_minimum_required(VERSION 2.8.3)
    
    • 语法: cmake_minimum_required(VERSION versionNumber [FATAL_ERROR])
  • project 定义工程名称,并可指定工程支持的语言

    # 指定工程名为HELLOWORLD
    project(HELLOWORLD)
    
    • 语法: project(projectname [CXX] [C] [Java])
  • set 显式的定义变量

    # 定义SRC变量,其值为sayhello.cpp hello.cpp
    set(SRC sayhello.cpp hello.cpp)
    
    • 语法:set(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])
  • include_directories 向工程添加多个特定的头文件搜索路径 --->相当于指定g++编译器的 -I 参数。语法: include_directories([AFTER|BEFORE] [SYSTEM] dir1 dir2 ...)

        直接写 include,不写绝对路径,即相对于cmakelists文件所在的路径

  • link_directories 向工程添加多个特定的库文件搜索路径 --->相当于指定g++编译器的-L参数

    # 将/usr/lib/mylibfolder 和 ./lib 添加到库文件搜索路径
    link_directories(/usr/lib/mylibfolder ./lib)
    

          语法: link_directories(dir1 dir2 ...)

  • add_library 生成库文件

    # 通过变量 SRC 生成 libhello.so 共享库
    add_library(hello SHARED ${SRC})
    
    • 语法: add_library(libname [SHARED|STATIC|MODULE] [EXCLUDE_FROM_ALL] source1 source2 ... sourceN)
  • add_compile_options 添加编译参数

    # 添加编译参数 -Wall -std=c++11 -O2
    add_compile_options(-Wall -std=c++11 -O2)
    

  • add_executable 生成可执行文件

    # 编译main.cpp生成可执行文件main
    add_executable(main main.cpp)
    
    • 语法:add_executable(exename source1 source2 ... sourceN)

  • target_link_libraries 为 target 添加需要链接的共享库 -->相同于指定g++编译器-l参数

    # 将hello动态库文件链接到可执行文件main
    target_link_libraries(main hello)
    
    • 语法: target_link_libraries(target library1<debug | optimized> library2...)
  • add_subdirectory 向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制存放的位置

    # 添加src子目录,src中需有一个CMakeLists.txt
    add_subdirectory(src)
    

        语法: add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])

使用案例,推荐查看下面链接的第五章节:

Linux下CMake简明教程_linux cmake-CSDN博客

  • aux_source_directory 发现一个目录下所有的源代码文件并将列表存储在一个变量中,这个指令临时被用来自动构建源文件列表

    • 语法: aux_source_directory(dir VARIABLE)
    # 定义SRC变量,其值为当前目录下, 所有的源代码文件
    aux_source_directory(. SRC)
    # 编译SRC变量所代表的源代码文件,生成main可执行文件
    add_executable(main ${SRC})
    
2.2 CMake常用变量
  • CMAKE_C_FLAGS  gcc编译选项

  • CMAKE_CXX_FLAGS  g++编译选项

    # 在CMAKE_CXX_FLAGS编译选项后追加-std=c++11
    set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
    
  • CMAKE_BUILD_TYPE  编译类型(Debug, Release)

    # 设定编译类型为debug,调试时需要选择debug
    set(CMAKE_BUILD_TYPE Debug)
    # 设定编译类型为release,发布时需要选择release
    set(CMAKE_BUILD_TYPE Release)
    
  • CMAKE_BINARY_DIR

    PROJECT_BINARY_DIR

    <projectname>_BINARY_DIR

    这三个变量指代的内容是一致的。如果是 in source build,指的就是工程顶层目录路径。如果是 out-of-source 编译,指的是工程编译发生的目录路径。

  • CMAKE_SOURCE_DIR

    PROJECT_SOURCE_DIR

        <projectname>_SOURCE_DIR

        这三个变量指代的内容是一致的,不论采用何种编译方式,都是指工程顶层目录的绝对你路径。

        例如: include_directories(${CMAKE_SOURCE_DIR}/include)

        当我们想指明我们头文件所在的位置时,可以借用CMAKE_SOURCE_DIR宏,先导航到你的代码工程文件的绝对路径。

使用示例:

include_directories(${CMAKE_SOURCE_DIR}/include)
  • CMAKE_C_COMPILER:指定C编译器
  • CMAKE_CXX_COMPILER:指定C++编译器
  • EXECUTABLE_OUTPUT_PATH:可执行文件输出的存放路径
  • LIBRARY_OUTPUT_PATH:库文件输出的存放路径

3 CMake编译工程

CMake目录结构:项目主目录存在一个CMakeLists.txt文件

两种方式设置编译规则

  1. 包含源文件的那个子文件夹包含CMakeLists.txt文件,主目录的CMakeLists.txt通过add_subdirectory添加子目录即可;
  2. 包含源文件的子文件夹未包含CMakeLists.txt文件,子目录编译规则体现在主目录的CMakeLists.txt中;
3.1 编译流程

在 linux 平台下使用 CMake 构建C/C++工程的流程如下:

  • 手动编写 CMakeLists.txt。
  • 执行命令 cmake PATH生成 Makefile ( PATH 是顶层CMakeLists.txt 所在的目录 )。
  • 执行命令make 进行编译。
# important tips
.  		# 表示当前目录
./ 		# 表示当前目录

..  	# 表示上级目录
../ 	# 表示上级目录
3.2 两种构建方式
  • 内部构建(in-source build):不推荐使用

    内部构建会在同级目录下产生一大堆中间文件,这些中间文件并不是我们最终所需要的,和工程源文件放在一起会显得杂乱无章。

    ## 内部构建
    
    # 在当前目录下,编译本目录的CMakeLists.txt,生成Makefile和其他文件
    cmake .
    # 执行make命令,生成target
    make
    
  • 外部构建(out-of-source build):推荐使用

    将编译输出文件与源文件放到不同目录中,(源cmakelist放在一级项目文件夹里面,再在一级项目文件夹里新建一个 build文件夹,存放cmakelist执行输出的makefile等生成文件,不使得项目结构杂乱

    ## 外部构建
    
    # 1. 在当前目录下,创建build文件夹
    mkdir build
    # 2. 进入到build文件夹
    cd build
    # 3. 编译上级目录的CMakeLists.txt,生成Makefile和其他文件
    cmake ..
    # 4. 执行make命令,生成target
    make
    

4【实战】CMake代码实践

4.1 最小CMake工程
4.1.1 项目代码

项目文件只有一个,就是一个helloworld.cpp

4.1.2 cmake工程 

1)在helloworld.cpp文件夹下,新建一个CMakeList.txt,一个build文件夹

2)CMakeLists.txt文件内写入如下内容(注意写指令借助tab)

3)在build 目录下执行 cmake . .;会发现 build文件夹 下生成许多cmake工程文件,包括makefile

4)在 build目录下执行makefile;该目录下会生成 最终可执行的目标二进制文件。

CMakeLists.txt:

# Set the minimum version of CMake that can be used
cmake_minimum_required(VERSION 3.0)

# Set the project name
project (HELLO)

# Add an executable
add_executable(hello_cmake main.cpp)
4.2 多目录工程

//mian.cpp
#include "swap.h"

int main() {
    swap obj_ab(2,3);
    obj_ab.print_info();
    obj_ab.run();
    obj_ab.print_info();
}
//swap.h	
#pragma once
#include <iostream>

class swap
{
private:
    int a, b;
public:
    swap(int a, int b)
    {
        this->a = a;
        this->b = b;
    }
    void run();
    void print_info();
};
//swap.cpp
#include "swap.h"

void swap::run() {
    int temp;
    temp = a;
    a = b;
    b = temp;
}

void swap::print_info() {
    std::cout << "a = " << a << std::endl;
    std::cout << "b = " << b << std::endl;
}

4.2.1 多目录工程-直接编译

这个案例直接编译的cmakelists的写法和上面的helloworld案例的套路基本上一样,区别就是多了一个指定头文件、练习使用下set。 

CMakeLists.txt:

cmake_minimum_required(VERSION 3.0)

project(SWAP)

include_directories(include)

set(SOURCES main.cpp src/swap.cpp)

add_executable(swap_cmake ${SOURCES})

编译后的工程结构: 

.
├── build
│   ├── CMakeCache.txt
│   ├── CMakeFiles
│   │   ├── 3.22.1
│   │   │   ├── CMakeCCompiler.cmake
│   │   │   ├── CMakeCXXCompiler.cmake
│   │   │   ├── CMakeDetermineCompilerABI_C.bin
│   │   │   ├── CMakeDetermineCompilerABI_CXX.bin
│   │   │   ├── CMakeSystem.cmake
│   │   │   ├── CompilerIdC
│   │   │   │   ├── a.out
│   │   │   │   ├── CMakeCCompilerId.c
│   │   │   │   └── tmp
│   │   │   └── CompilerIdCXX
│   │   │       ├── a.out
│   │   │       ├── CMakeCXXCompilerId.cpp
│   │   │       └── tmp
│   │   ├── cmake.check_cache
│   │   ├── CMakeDirectoryInformation.cmake
│   │   ├── CMakeOutput.log
│   │   ├── CMakeTmp
│   │   ├── main_cmake.dir
│   │   │   ├── build.make
│   │   │   ├── cmake_clean.cmake
│   │   │   ├── compiler_depend.make
│   │   │   ├── compiler_depend.ts
│   │   │   ├── DependInfo.cmake
│   │   │   ├── depend.make
│   │   │   ├── flags.make
│   │   │   ├── link.txt
│   │   │   ├── main.cpp.o
│   │   │   ├── main.cpp.o.d
│   │   │   ├── progress.make
│   │   │   └── src
│   │   │       ├── swap.cpp.o
│   │   │       └── swap.cpp.o.d
│   │   ├── Makefile2
│   │   ├── Makefile.cmake
│   │   ├── progress.marks
│   │   ├── swap_cmake.dir
│   │   │   ├── build.make
│   │   │   ├── cmake_clean.cmake
│   │   │   ├── compiler_depend.make
│   │   │   ├── compiler_depend.ts
│   │   │   ├── DependInfo.cmake
│   │   │   ├── depend.make
│   │   │   ├── flags.make
│   │   │   ├── link.txt
│   │   │   ├── main.cpp.o
│   │   │   ├── main.cpp.o.d
│   │   │   ├── progress.make
│   │   │   └── src
│   │   │       ├── swap.cpp.o
│   │   │       └── swap.cpp.o.d
│   │   └── TargetDirectories.txt
│   ├── cmake_install.cmake
│   ├── Makefile
│   └── swap_cmake
├── CMakeLists.txt
├── include
│   └── swap.h
├── main.cpp
└── src
    └── swap.cpp

4.2.2 多目录工程-src做成(静态/动态)库,间接编译 

4.2.2.1 src分别做成 动态库(.so)/ 静态库(.a)

        直接在项目文件的一级目录修改cmakelists.txt的内容,先忽略 main.cpp,把src做成静态库或者动态库,保存在 /usr/lib 中。

cmake_minimum_required(VERSION 3.0)

project(SWAP)

include_directories(include)

set(SOURCES src/swap.cpp)

add_library(SwapTest SHARED ${SOURCES})
add_library(SwapTest STATIC ${SOURCES})

set (LIBRARY_OUTPUT_PATH /usr/lib)

4.2.2.2 使用静态库或动态库

        再次在项目文件的一级目录修改cmakelists.txt的内容,编译 main.cpp,并链接所需要的库。生成最终可执行二进制。

cmake_minimum_required(VERSION 3.0)

project(SWAP_TEST)

include_directories(include)

#选择链接静态库或者动态库
#set (PROJECT_LINK_LIBS /usr/lib/libswapTest_shared.so)
set (PROJECT_LINK_LIBS /usr/lib/libswapTest_static.a)

add_executable(swap_cmake main.cpp)

target_link_libraries (swap_cmake ${PROJECT_LINK_LIBS})

# libswapTest_shared.so
# libswapTest_static.a


4.3 更完整的项目c++开发过程

案例名称:士兵突击

需求列表:

  1. 士兵 许三多 有一把,叫做 AK47
  2. 士兵 可以 开火
  3. 士兵 可以 给枪装填子弹
  4. 能够 发射 子弹
  5. 能够 装填子弹 —— 增加子弹数量

开发过程:

  • 开发枪类
  • 开发士兵类
4.3.1 合理设置项目目录

//Gun.h
#pragma once
#include <string>

class Gun
{
public:
    Gun(std::string type){
        this->_bullet_count = 0;
        this->_type = type;
    }

    void addBullet(int bullet_num);
    bool shoot();

private:
    int _bullet_count;
    std::string _type;

};
//Soldier.h
#pragma once

#include <string>
#include "Gun.h"

class Solider
{
public:
    Solider(std::string name);
    ~Solider();
    void addGun(Gun *ptr_gun);
    void addBulletToGun(int num);
    bool fire();

private:
    std::string _name;
    Gun *_ptr_gun;
};
4.3.2 编写项目源文件
//Gun.cpp
#include "Gun.h"
#include "iostream"
using namespace std;

void Gun::addBullet(int bullet_num)
{
    this->_bullet_count += bullet_num;
}

bool Gun::shoot()
{
    if (this->_bullet_count<=0)
    {
        cout << "There is no bullet!" << endl;
        return false;
    }

    this->_bullet_count -= 1;
    cout << "shoot successfully!" << endl;
    return true;
    
}
//Soldier.cpp
#include "Soldier.h"

Solider::Solider(std::string name)
{
    this->_name = name;
    this->_ptr_gun = nullptr;
}

void Solider::addGun(Gun *ptr_gun)
{
    this->_ptr_gun = ptr_gun;
}

void Solider::addBulletToGun(int num)
{
    this->_ptr_gun->addBullet(num);
}

bool Solider::fire()
{
    return(this->_ptr_gun->shoot());
}

Solider::~Solider()
{
    if (this->_ptr_gun==nullptr)
    {
        return;
    }
    delete this->_ptr_gun;
    this->_ptr_gun = nullptr;
    
}
#include "Gun.h"
#include "Soldier.h"
#include <iostream>

void test()
{
    Solider sanduo("xusanduo");
    sanduo.addGun(new Gun("AK47"));
    sanduo.addBulletToGun(20);
    sanduo.fire();
}

int main()
{
    std::cout << "This is a test string..." << std::endl;
    std::cout << "This is a test string..." << std::endl;
    test();
    return 0;
}
4.3.3 编写CMakeLists.txt
cmake_minimum_required(VERSION 3.0)

project(SOLIDERFIRE)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")

set(CMAKE_BUILD_TYPE Debug)

include_directories(${CMAKE_SOURCE_DIR}/include)

add_executable(my_cmake_exe main.cpp src/Gun.cpp src/Solider.cpp)
4.3.4 编译CMake项目

操作和4.1完全相同。

        这里强调一点,当我们随着代码的迭代开发,要修改项目中的文件,重新编译获得可执行文件时,我们只需要重新make一下。注意:这个时候只会重新编译你修改的文件。这一点非常重要,对于大型项目的编译时间。

makefile和CMakeLists使用区别与联系

CMake主要是编写CMakeLists.txt文件,然后用cmake命令将CMakeLists.txt文件转化为make所需要的makefile文件,最后用make命令编译源码生成可执行程序或共享库。

简单来说:

makefile里面封装的是最底层的gcc/g++编译命令、编译参数、文件之间的依赖关系等;

CMakeLists.txt 则在makefile基础上更抽象了一层,使得项目编译工程的书写更简洁。

二者关系如下图:

  6.1Cross-platform development 6.2 语法特性介绍_哔哩哔哩_bilibili

更详细的:

        CMake是一个跨平台的自动化构建系统,可以用于管理和构建项目的源代码,比make更为高级。而Makefile是一种构建工具,用于自动化构建可执行文件和库。CMake可以生成Makefile,但是CMake不仅仅可以生成Makefile,还可以生成Visual Studio项目、Xcode项目等。因此,CMake可以完全代替Makefile使用。

CMake的使用场景包括但不限于以下几种:

  1. 跨平台构建:CMake可以在不同的平台上生成相应的构建文件,使得项目可以在不同的平台上构建。在 Linux/Unix 平台,生成 makefile,在苹果平台,可以生成 xcode,在 Windows 平台,可以生成 MSVC 的工程文件。想像如果平台的构建工程使用 makefile构建,换平台将会很麻烦。
  2. 管理大型项目:CMake可以管理大型项目的构建,包括多个子目录和库的构建。
  3. 自定义构建:CMake可以根据用户的需求自定义构建过程,包括编译选项、链接选项等。

而Makefile的使用场景包括但不限于以下几种:

  1. 简单项目构建:对于简单的项目,使用Makefile可以快速构建可执行文件和库。
  2. 熟悉Unix/Linux环境:Makefile是Unix/Linux环境下的标准构建工具,熟悉Makefile可以更好地理解Unix/Linux环境下的构建过程。
  3. 自定义构建:Makefile可以根据用户的需求自定义构建过程,包括编译选项、链接选项等。

因此,CMake适用于大型、跨平台的项目,而Makefile适用于简单的项目和熟悉Unix/Linux环境的用户。

学习参考:

6.1Cross-platform development 6.2 语法特性介绍_哔哩哔哩_bilibiliNotion – The all-in-one workspace for your notes, tasks, wikis, and databases.

认识Make、Makefile、CMake和CMakeLists_cmakelists makefile-CSDN博客 ​ 

CMake入门教程_cmake -dgcov=on -dcmake_build_type=debug-CSDN博客

Linux下CMake简明教程_linux cmake-CSDN博客

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

spring-cloud-alibaba 官方版本说明

版本说明 alibaba/spring-cloud-alibaba Wiki GitHub 云原生应用脚手架 构建方式&#xff1a;maven 语言&#xff1a;java springboot 2.7.6 架构&#xff1a;单模块&#xff0c;mvc 架构 下面是脚手架生成。。。。。。。。。。。。。。。。。。。 单模块pom mvc 代码…

关于PFMEA的风险评估都在这里——SunFMEA软件

1、评价严重度 严重度应该独立于发生度和探测度的评价&#xff0c;不能认为发生概率低或者探测能力强&#xff0c;失效就不会到达顾客手中&#xff0c;不会造成糟糕的失效影响&#xff0c;严重度就低。 其实&#xff0c;严重度评估的是已经识别的失效链的严重程度&#xff0c…

Oracle忘记所有密码怎么办

最近遇到一个Oracle的问题&#xff0c;密码要过期了&#xff0c;但是除了用户密码&#xff0c;其他密码都不知道了&#xff0c;修改不了密码怎么办呢&#xff1f; 试了各种方法&#xff0c;最终下面的方式生效了&#xff1a; 首先&#xff0c;使用orapwd生成新的密码文件&…

六、三台主机免密登录和时钟同步

目录 1、免密登录 1.1 为什么要免密登录 1.2 免密 SSH 登录的原理

CSS 在性能优化方面的实践

前言 CSS&#xff08;层叠样式表&#xff09;是一种用于描述网页外观和格式的语言。随着网页变得越来越复杂&#xff0c;CSS文件的大小也随之增加&#xff0c;这可能会对网页性能产生负面 .box {width: 100px;height: 100px;transition: transform 0.3s; }.box:hover {transf…

Java全栈基础篇--集合

集合 集合&#xff1a;集合是java中提供的一种容器&#xff0c;可以用来存储多个数据。 特点&#xff1a; 长度不固定&#xff0c;还可以存储不同的数据&#xff08;但是一般都用同一类型&#xff09; 集合和数组既然都是容器&#xff0c;它们有啥区别呢&#xff1f; 数组的长…

数据可视化工具APITable:实现强大的多维表格功能并随时随地远程访问

APITable免费开源的多维表格与可视化数据库公网远程访问 文章目录 APITable免费开源的多维表格与可视化数据库公网远程访问前言1. 部署APITable2. cpolar的安装和注册3. 配置APITable公网访问地址4. 固定APITable公网地址 前言 vika维格表作为新一代数据生产力平台&#xff0c…

血的教训---入侵redis并免密登录redis所在服务器漏洞复现

血的教训—入侵redis并免密登录redis所在服务器漏洞复现 今天就跟着我一起来入侵redis并免密登录redis所在服务器吧&#xff0c;废话不多说&#xff0c;我们直接开始吧。 这是一个体系的学习步骤&#xff0c;当然如果基础扎实的话可以继续往下面看 以下都是关联的文章&#xff…

算法与数据结构有区别吗?

算法和数据结构并不是同一件事。严格来说&#xff0c;它们并不是等效的。但是&#xff0c;我们通常在使用的时候会互换这两个术语。为了简便&#xff0c;后文我们会用数据结构这个术语来指代“数据结构及其所有相关的方法”。 有很多方法可以用来说明这两个术语之间的区别&…

python执行shell

0x00:前言 正常一个网站分为服务端和客户端&#xff0c;因为是正向的&#xff0c;所以服务端是在目标机器上的&#xff0c;客户端则是攻击者机器上&#xff0c;在这里要感谢MiaGz大师傅&#xff0c;这里很多都是参考了MiaGz大师傅的文章写出来的&#xff0c;进行了一点个人修改…

STM32/GD32_分散加载

Q&#xff1a;如何将一个变量、某个源文件的函数在编译阶段就存储在用户指定的区域&#xff1f; KEIL环境&#xff1a;.map后缀文件、.sct后缀文件 IAR环境&#xff1a;.map后缀文件、.icf后缀文件 【map文件】 对固件里面的变量、函数、常量等元素的存储空间进行分配的说明…

【android开发-04】android中activity的生命周期介绍

1&#xff0c;返回栈 android中使用任务task来管理activity&#xff0c;一个任务就是一组存放在栈里的活动的集合&#xff0c;这个栈被称为返回栈。栈是一种先进先出的数据结构。当我们启动一个新的活动&#xff0c;他会在返回栈中人栈&#xff0c;并处以栈顶的位置&#xff0…

刘恋对话贾樟柯:焦虑往往是因为我们还有的选

11月29日&#xff0c;由房产服务国民品牌我爱我家与贾樟柯联合呈现的《住进每一种生活》&#xff0c;在乐迷和网友的热烈期盼下迎来了第三期。自首期节目在腾讯视频和极光TV播出以来&#xff0c;《住进每一种生活》频频在社交媒体上引发观众热议。 《住进每一种生活》以六位名…

指纹芯片的工作原理及应用领域详解

指纹芯片是一种利用指纹识别技术的电子设备,可以通过扫描人体指纹的纹理特征,将其转化为数字化信息并进行存储和识别。指纹芯片广泛应用于各个领域,包括智能手机、银行和金融、门禁系统、身份验证等,因其高度准确、快速便捷的特点,得到了广大用户的青睐。 指纹芯片的原理是基于…

从订阅式需求发展,透视凌雄科技DaaS模式增长潜力

订阅制&#xff0c;C端消费者早已耳熟能详&#xff0c;如今也凭借灵活、服务更新稳定的特点&#xff0c;逐渐成为B端企业服务的新热点。 比如对中小企业而言&#xff0c;办公IT设备等配套支出都必不可少&#xff0c;但收入本身并不稳定&#xff0c;购置大堆固定资产&#xff0…

【散列函数的构造方法(直接定址法 ==除留余数法==),散列表的查找(1.开放地址法,2.链地址法(拉链法))】

文章目录 散列函数的构造方法直接定址法除留余数法 散列表的查找1.开放地址法线性探测法二次探测法伪随机探测法 2.链地址法&#xff08;拉链法&#xff09; 散列表的查找效率 散列函数的构造方法 散列存储 选取某个函数&#xff0c;依该函数按关键字计算元素的存储位置。 Loc…

3.3 路由器的远程配置

实验3.3 路由器的远程配置 一、任务描述二、任务分析三、具体要求四、实验拓扑五、任务实施&#xff08;一&#xff09;、配置通过Telnet登录系统1.RA的基本配置2.RB的基本配置3.在RA上配置Telnet用户登录界面 &#xff08;二&#xff09;、配置通过STelnet登录系统1.RA的基本配…

SpringBoot 实现动态切换数据源,这样做才更优雅!

最近在做业务需求时&#xff0c;需要从不同的数据库中获取数据然后写入到当前数据库中&#xff0c;因此涉及到切换数据源问题。本来想着使用Mybatis-plus中提供的动态数据源SpringBoot的starter&#xff1a;dynamic-datasource-spring-boot-starter来实现。 结果引入后发现由于…

物联网实训室虚拟仿真软件建设方案

一、概述 物联网实训室虚拟仿真软件旨在紧密围绕立德树人的根本任务&#xff0c;充分依托先进的数字技术&#xff0c;并对接物联网行业的发展趋势和人才需求。通过对比真实企业工作环境&#xff0c;融合创新创业教育基因&#xff0c;秉承虚拟仿真技术与教育教学深度融合的理念&…

Linux系统iptables扩展

目录 一. iptables规则保存 1. 导出规则保存 2. 自动重载规则 ①. 当前用户生效 ②. 全局生效 二. 自定义链 1. 新建自定义链 2. 重命名自定义链 3. 添加自定义链规则 4. 调用自定义链规则 5. 删除自定义链 三. NAT 1. SNAT 2. DNAT 3. 实验 ①. 实验要求 ②. …