CMake基础【学习笔记(八)】

news2024/11/29 20:39:01

声明此博客为转载

CMake基础

文章目录

  • CMake基础
    • 一、准备知识
      • 1.1 C++的编译过程
      • 1.2 静态链接库和动态链接库
      • 1.3 为什么需要CMake
        • 1.3.1 g++ 命令行编译
        • 1.3.2 CMake简介
    • 二、CMake基础知识
      • 2.1 安装
      • 2.2 第一个CMake例子
      • 2.3 语法基础
        • 2.3.1 指定版本
        • 2.3.2 设置项目
        • 2.3.3 添加可执行文件目标
        • 2.3.4 生成静态库并链接
        • 2.3.5 生成动态库并连接
        • 2.3.6 CMake 中的 PUBLIC、PRIVATE、INTERFACE
        • 2.3.7 变量
        • 2.3.8 include引入其他代码
        • 2.3.9 条件控制
        • 2.3.10 CMake分步编译
        • 2.3.11 生成器表达式
        • 2.3.12 函数和宏
        • 2.3.13 设置安装
        • 2.3.14 寻找依赖 find_package
    • 三、opencv CMake示例

一、准备知识

1.1 C++的编译过程

使用g++等编译工具,从源码生成最终的可执行文件一般有这几步:预处理(Preprocess)、编译(Compile)、汇编(assemble)、链接(link)。

输入g++ --help可以看到对应命令:

-E                       Preprocess only; do not compile, assemble or link.
-S                       Compile only; do not assemble or link.
-c                       Compile and assemble, but do not link.
-o <file>                Place the output into <file>.

以下面程序为例:

#include <iostream>

int main() {
    std::cout << "Hello World!" << std::endl;
    return 0;
}
  • 第一步:预处理
    C++中预处理指令以 # 开头。在预处理阶段,会对#define进行宏展开,处理#if,#else等条件编译指令,递归处理#include。这一步需要我们添加所有头文件的引用路径。

    # 将xx.cpp源文件预处理成xx.i文件(文本文件)
    g++ -E main.cpp -o main.i
    
  • 第二步:编译

    检查代码的规范性和语法错误等,检查完毕后把代码翻译成汇编语言文件。

    # 将xx.i文件编译为xx.s的汇编文件(文本文件)
    g++ -S main.i -o main.s
    
  • 第三步:汇编
    基于汇编语言文件生成二进制格式的目标文件。

    # 将xx.s文件汇编成xx.o的二进制目标文件
    g++ -c main.s -o main.o
    
  • 第四步:链接

    将目标代码与所依赖的库文件进行关联或者组装,合成一个可执行文件

    # 将xx.o二进制文件进行链接,最终生成可执行程序
    g++ main.o -o main
    

1.2 静态链接库和动态链接库

所谓静态和动态,其区别是链接的阶段不一样。

  • 静态链接库名称一般是lib库名称.a.a代表archive library),其链接发生在编译环节。一个工程如果依赖一个静态链接库,其输出的库或可执行文件会将静态链接库*.a打包到该工程的输出文件中(可执行文件或库),因此生成的文件比较大,但在运行时也就不再需要库文件了。

  • 而动态链接库的链接发生在程序的执行过程中,其在编译环节仅执行链接检查,而不进行真正的链接,这样可以节省系统的开销。动态库一般后缀名为*.so.so代表shared object,Linux:lib库名称.so ,macOS:lib库名称.dylib)。动态链接库加载后,在内存中仅保存一份拷贝,多个程序依赖它时,不会重复加载和拷贝,这样也节省了内存的空间。

  • 以下图为例

    • 工程AB依赖静态链接库 static libraryAB在运行时,内存中会有多份static library

    • 工程AB依赖动态链接库 shared libraryAB在运行时,内存中只有一份 shared library(shared:共享)。

以上只是非常简单的一个解释以区分动态链接库和静态链接库。更多底层的知识需要单独进行深入讲解。

1.3 为什么需要CMake

1.3.1 g++ 命令行编译

当我们编译附件中1.hello_world时,我们可以运行

g++ main.cpp -o main

当我们需要引入外部库时,如附件中的2.external_libs,需要引入gflags(Google开源的命令行参数处理库),我们则需要运行:

# 安装gflags
sudo apt-get install libgflags-dev libgflags2.2 

// -lgflags表示链接gflags库,-o main表示输出文件名为main
g++ main.cpp -lgflags -o main 

# 或者:

# 安装pkg-config
sudo apt-get install pkg-config

// pkg-config是一个工具,用于查找和管理安装在系统上的库文件,--cflags --libs gflags表示查找gflags库的头文件和库文件的路径,-o main表示输出文件名为main

g++ main.cpp `pkg-config --cflags --libs gflags`  -o main 


# 测试输出
./main --age 31 --name alice

有些时候有一些常用库我们也不用手动添加头文件或链接库路径,通常g++能在默认查询路径中找到他们。当我们的项目文件变得多起来,引入的外部库也多起来时,命令行编译这种方式就会变得十分臃肿,也不方便调试和编辑。通常在测试单个文件时会使用命令行进行编译,但不推荐在一个实际项目中使用命令行编译。

1.3.2 CMake简介

在实际工作中推荐使用CMake构建C++项目,CMake是用于构建、测试和软件打包的开源跨平台工具;

特性:

  • 自动搜索可能需要的程序、库和头文件的能力;
  • 独立的构建目录(如build),可以安全清理
  • 支持复杂的自定义命令(下载、生成各种文件)
  • 自定义配置可选组件
  • 从简单的文本文件(CMakeLists.txt)自动生成工作区和项目的能力
  • 在主流平台上自动生成文件依赖项并支持并行构建
  • 几乎支持所有的IDE

二、CMake基础知识

2.1 安装

ubuntu上请执行

sudo apt install cmake -y

或者编译安装:

# 以v3.25.1版本为例
git clone -b v3.25.1 https://github.com/Kitware/CMake.git 
cd CMake
# 你使用`--prefix`来指定安装路径,或者去掉`--prefix`,安装在默认路径。
./bootstrap --prefix=<安装路径> && make && sudo make install

# 验证
cmake --version

2.2 第一个CMake例子

附件位置:3.first_cmake

# 第一步:配置,-S 指定源码目录,-B 指定构建目录
cmake -S . -B build 
# 第二步:生成,--build 指定构建目录
cmake --build build
# 运行
./build/first_cmake

vs code插件:

  • 安装twxs.cmake做代码提示;
  • 安装ms-vscode.cmake-tools界面操作。

2.3 语法基础

2.3.1 指定版本

以附件:3.first_cmake/CMakeLists.txt为例:

# CMake 最低版本号要求
cmake_minimum_required(VERSION 3.10)

# first_cmake是项目名称,VERSION是版本号,DESCRIPTION是项目描述,LANGUAGES是项目语言
project(first_cmake 
        VERSION 1.0.0 
        DESCRIPTION "项目描述"
        LANGUAGES CXX) 

# 添加一个可执行程序,first_cmake是可执行程序名称,main.cpp是源文件
add_executable(first_cmake main.cpp)

命令cmake_minimum_required来指定当前工程所使用的CMake版本,不区分大小写的,通常用小写。VERSION是这个函数的一个特殊关键字,版本的值在关键字之后。CMake中的命令大多和cmake_minimum_required相似,不区分大小写,并有很多关键字来引导命令的参数输入(类似函数传参)。

2.3.2 设置项目

以附件:3.first_cmake/CMakeLists.txt为例:

project(ProjectName 
        VERSION 1.0.0 
        DESCRIPTION "项目描述"
        LANGUAGES CXX) 

CMakeLists.txt的开头,都会使用project来指定本项目的名称、版本、介绍、与使用的语言。在project中,第一个ProjectName(例子中用的是first_cmake)不需要参数,其他关键字都有参数。

2.3.3 添加可执行文件目标

以附件:3.first_cmake/CMakeLists.txt为例:

add_executable(first_cmake main.cpp)

这里我们用到add_executable,其中第一个参数是最终生成的可执行文件名以及在CMake中定义的Target名。我们可以在CMake中继续使用Target的名字为Target的编译设置新的属性和行为。命令中第一个参数后面的参数都是编译目标所使用到的源文件。

2.3.4 生成静态库并链接

附件位置:4.static_lib_test

A.生成静态库

#account_dir/CMakeLists.txt

# 最低版本要求
cmake_minimum_required(VERSION 3.10)

# 项目信息
project(Account)

# 添加静态库,Linux下会生成libAccount.a
add_library(Account STATIC Account.cpp Account.h)
# 编译静态库后,会在build下生成 build/libAccount.a 静态库文件
account_dir/
├── Account.cpp
├── Account.h
├── build
│   └── libAccount.a
└── CMakeLists.txt

这里我们用到add_library, 和add_executable一样,Account为最终生成的库文件名(lib库名称.a),第二个参数是用于指定链接库为动态链接库(SHARED)还是静态链接库(STATIC),后面的参数是需要用到的源文件。

B.链接

# test_account/CMakeLists.txt

# 最低版本要求
cmake_minimum_required(VERSION 3.10)

# 项目名称
project(test_account)

# 添加执行文件
add_executable(test_account test_account.cpp)

# 添加头文件目录,如果不添加,找不到头文件
target_include_directories(test_account PUBLIC "../account_dir")
# 添加库文件目录,如果不添加,找不到库文件
target_link_directories(test_account PUBLIC "../account_dir/build")
# 添加目标链接库
target_link_libraries(test_account PRIVATE Account)
# 编译后目录如下
4.static_lib_test/
├── account_dir
│   ├── Account.cpp
│   ├── Account.h
│   ├── build
│   │   └── libAccount.a
│   └── CMakeLists.txt
└── test_account 
    ├── build
    │   └── test_account
    ├── CMakeLists.txt
    └── test_account.cpp

我们通过add_libraryadd_executable定义了Target,我们可以通过Target的名称为其添加属性,例如:

# 指定目标包含的头文件目录
target_include_directories(test_account PUBLIC "../account_dir")
# 添加库文件目录,如果不添加,找不到库文件
target_link_directories(test_account PUBLIC "../account_dir/build")
# 指定目标链接的库
target_link_libraries(test_account PRIVATE Account)
  • 通过target_include_directories,我们给test_account添加了头文件引用路径"../account_dir"。上面的关键词PUBLIC,PRIVATE用于说明目标属性的作用范围,更多介绍参考下节。
  • 通过target_link_libraries,将前面生成的静态库libAccount.a链接给对象test_account,但此时还没指定库文件的目录,CMake无法定位库文件
  • 再通过target_link_directories,添加库文件的目录即可。
2.3.5 生成动态库并连接

附件位置:5.dynamic_lib_test

A.生成动态库

#account_dir/CMakeLists.txt

# 添加动态库,Linux下会生成libAccount.so
add_library(Account SHARED Account.cpp Account.h)
# 编译动态库后,会在build下生成 build/libAccount.so 动态库文件
account_dir/
├── Account.cpp
├── Account.h
├── build
│   └── libAccount.so
└── CMakeLists.txt

B.链接

操作不变。

# ldd查看依赖的动态库
libAccount.so => /home/enpei/Documents/course_cpp_tensorrt/course_5/src/5.dynamic_lib_test/test_account/../account_dir/build/libAccount.so (0x00007fb692cf1000)

当然,也可以用一个CMakeLists.txt来一次性编译,参考附件6.build_together

#6.build_together/CMakeLists.txt`

# 最低版本要求
cmake_minimum_required(VERSION 3.10)

# 项目信息
project(test_account)

# 添加动态库
add_library(Account SHARED "./account_dir/Account.cpp" "./account_dir/Account.h")

# 添加可执行文件
add_executable(test_account "./test_account/test_account.cpp")

# 添加头文件
target_include_directories(test_account PUBLIC "./account_dir")
# 添加链接库
target_link_libraries(test_account Account)
2.3.6 CMake 中的 PUBLIC、PRIVATE、INTERFACE

CMake中经常使用target_...()类似的命令,一般这样的命令支持通过PUBLICPRIVATEINTERFACE关键字来控制传播。

target_link_libraries(A B)为例,从理解的角度来看

  • PRIVATE :依赖项B仅链接到目标 A,如果有C 链接了AC不会链接B
  • INTERFACE :依赖项B并不链接到目标A,如果有C 链接了AC会链接B
  • PUBLIC :依赖项B链接到目标 A,如果有C 链接了AC也会链接B

其实就是对象属性的传递,打个散烟的比方:

  • PRIVATE: 就是自己抽,不给别人抽
  • INTERFACE :就是自己不抽,给别人抽
  • PUBLIC :就是自己抽,也给别人抽

从使用的角度来说,如果有C链接了目标A

  • 如果B仅用于A的实现,且不在头文件中提供给C使用,使用PRIVATE
  • 如果B不用于A的实现,仅在头文件中作为借口给C使用,使用INTERFACE
  • 如果B既用于A的实现,也在头文件中提供给C使用,使用PUBLIC

举例:

# 创建库
add_library(C c.cpp)
add_library(D d.cpp)
add_library(B b.cpp)

# C是B的PUBLIC依赖项
target_link_libraries(B PUBLIC C)
# D是B的PRIVATE依赖项
target_link_libraries(B PRIVATE D)

# 添加可执行文件
add_executable(A a.cpp)

# 将B链接到A
target_link_libraries(A B)
  • 因为CBPUBLIC依赖项,所以C会传播到A
  • 因为DBPRIVATE依赖性,所以D不会传播到A
2.3.7 变量

附件位置:7.message_var_demo

像其他编程语言一样,我们应该将CMake理解为一门编程语言。我们也需要设定变量来储存我们的选项,信息。有时候我们通过变量来判断我们在什么平台上,通过变量来判断我们需要编译哪些Target,也通过变量来决定添加哪些依赖。

2.3.8 include引入其他代码

附件位置:8.include_demo

2.3.9 条件控制

附件位置:9.if_demo

正如前面所讲,应该把CMake当成编程语言,除了可以设置变量以外,CMake还可以写条件控制。

if(variable)
    # 为true的常量:ON、YES、TRUE、Y、1、非0数字
else()
    # 为false的常量:OFF、NO、FALSE、N、0、空字符串、NOTFOUND
endif()

可以和条件一起使用的关键词有

NOT, TARGET, EXISTS (file), DEFINED等
STREQUAL, AND, OR, MATCHES (regular expression), VERSION_LESS, VERSION_LESS_EQUAL等
2.3.10 CMake分步编译

附件位置:10.steps_demo

# 查看所有目标
$ cmake -S . -B build
$ cd build
$ cmake --build . --target help

The following are some of the valid targets for this Makefile:
... all (the default if no target is provided)
... clean
... depend
... rebuild_cache
... edit_cache
... steps_demo
... main.o
... main.i
... main.s



# 1.预处理
$ cmake --build . --target main.i
# 输出:Preprocessing CXX source to CMakeFiles/steps_demo.dir/main.cpp.i
# 可以打开滑到底部

# 2.编译
$ cmake --build . --target main.s
# 输出汇编代码:Compiling CXX source to assembly CMakeFiles/steps_demo.dir/main.cpp.s

# 3.汇编
$ cmake --build . --target main.o
# 输出二进制文件:Building CXX object CMakeFiles/steps_demo.dir/main.cpp.o

# 链接
$ cmake --build .
Scanning dependencies of target steps_demo
[ 50%] Linking CXX executable steps_demo
[100%] Built target steps_demo

# 运行
./steps_demo
2.3.11 生成器表达式

附件位置:11.generator_expression

生成器表达式简单来说就是在CMake生成构建系统的时候根据不同配置动态生成特定的内容。有时用它可以让代码更加精简,我们介绍几种常用的。

需要注意的是,生成表达式被展开是在生成构建系统的时候,所以不能通过解析配置CMakeLists.txt阶段的message命令打印,可以用类似file(GENERATE OUTPUT "./generator_test.txt" CONTENT "$<$<BOOL:TRUE>:TEST>")生成文件的方式间接测试。

在其最一般的形式中,生成器表达式是$<...>,尖括号中间可以是如下几种类型:

  • 条件表达式
  • 变量查询(Variable-Query)
  • 目标查询(Target-Query)
  • 输出相关的表达式
# 1.条件表达式:$<condition:true_string>,当condition为真时,返回true_string,否则返回空字符串
$<0:TEST>  
$<1:TEST>  
$<$<BOOL:TRUE>:TEST>

# 2.变量查询(Variable-Query)
$<TARGET_EXISTS:target>:判断目标是否存在
$<CONFIG:Debug>:判断当前构建类型是否为Debug

# 3.目标查询(Target-Query)
$<TARGET_FILE:target>:获取编译目标的文件路径
$<TARGET_FILE_NAME:target>:获取编译目标的文件名

4.输出相关表达式:用于在不同的环节使用不同参数,比如需要在installbuild环节分别用不同的参数,我们可以这样写:

add_library(Foo ...)
target_include_directories(Foo
    PUBLIC
        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
        $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)

其中$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>仅在build环节生效;而$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>仅在install环节生效。通过设定不同阶段不同的参数,我们可以避免路径混乱的问题。

2.3.12 函数和宏

附件位置:12.function_macro

# 定义一个宏,宏名为my_macro,没有参数
macro(my_macro)
    message("宏内部的信息")
    set(macro_var "宏内部变量test")
endmacro(my_macro)

# 定义一个函数,函数名为second_func,有两个参数
function(second_func arg1 arg2)
    message("第一个参数:${arg1}, 第二个参数:${arg2}")
endfunction(second_func)
2.3.13 设置安装

附件位置:13.install_demo

当需要发布项目时你需要指定项目文件的安装路径。下面的代码片段中,使用install安装demo_test,并分别将可执行文件安装在bin中,动态链接库和静态链接库都安装在lib,公共头文件安装在include。这里的路径都将添加${CMAKE_INSTALL_PREFIX}作为前缀(如果不设置CMAKE_INSTALL_PREFIX,则会安装到/usr/local 目录下)。实现安装的功能在你需要发布你项目给其他人使用时,非常有用。

# 设置安装
install(TARGETS demo_test
        RUNTIME DESTINATION bin # 可执行文件
        LIBRARY DESTINATION lib # 动态库
        ARCHIVE DESTINATION lib # 静态库
        PUBLIC_HEADER DESTINATION include # 公共头文件
)
2.3.14 寻找依赖 find_package

对于大部分支持了CMake的项目来说,均可以通过find_package找到对应的依赖库,参考附件:14.find_demo

# 使用find_package寻找<LibaryName>库,如果找到,一般都会有以下变量(库作者设置)
<LibaryName>_FOUND:表示是否找到
<LibaryName>_INCLUDE_DIR:表示头文件目录
<LibaryName>_LIBRARIES:表示库文件目录

假设我们编写了一个新的函数库,我们希望别的项目可以通过find_package对它进行引用,我们有两种办法:

  • 编写一个Find<LibraryName>.cmake,适用于导入非cmake安装的项目,参考附件:15.custom_find
  • 使用install安装,生成<LibraryName>Config.cmake文件,适用于导入自己开发的cmake项目,参考附件:16.custom_install_demo

三、opencv CMake示例

附件位置:17.demo_opencv/

安装OpenCV:sudo apt install libopencv-dev

依赖和链接OpenCV与常规的添加依赖并没有太多不同,同时OpenCV提供了cmake find package的功能,因此我们可以通过find_package方便的定位opencv在系统中的位置和需要添加的依赖。

find_package(OpenCV REQUIRED)

message("OPENCV INCLUDE DIRS: ${OpenCV_INCLUDE_DIRS}")
message("OPENCV LINK LIBRARIES: ${OpenCV_LIBS}")

如果cmake找到了OpenCV,配置cmake后,命令行会有如下输出:

OPENCV INCLUDE DIRS: /usr/include/opencv4
OPENCV LINK LIBRARIES: opencv_calib3d;opencv_core;opencv_dnn;opencv_features2d;opencv_flann;opencv_highgui;opencv_imgcodecs;opencv_imgproc;opencv_ml;opencv_objdetect;opencv_photo;opencv_stitching;opencv_video;opencv_videoio;opencv_aruco;opencv_bgsegm;opencv_bioinspired;opencv_ccalib;opencv_datasets;opencv_dnn_objdetect;opencv_dnn_superres;opencv_dpm;opencv_face;opencv_freetype;opencv_fuzzy;opencv_hdf;opencv_hfs;opencv_img_hash;opencv_line_descriptor;opencv_optflow;opencv_phase_unwrapping;opencv_plot;opencv_quality;opencv_reg;opencv_rgbd;opencv_saliency;opencv_shape;opencv_stereo;opencv_structured_light;opencv_superres;opencv_surface_matching;opencv_text;opencv_tracking;opencv_videostab;opencv_viz;opencv_ximgproc;opencv_xobjdetect;opencv_xphoto

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

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

相关文章

Python画图之皮卡丘

Python-turtle画出皮卡丘&#xff08;有趣小游戏&#xff09; 一、效果图二、Python代码 一、效果图 二、Python代码 import turtledef getPosition(x, y):turtle.setx(x)turtle.sety(y)print(x, y)class Pikachu:def __init__(self):self.t turtle.Turtle()t self.tt.pensi…

小样本分割的新视角,Learning What Not to Segment【CVPR 2022】

论文地址&#xff1a;Excellent-Paper-For-Daily-Reading/image-segmentation at main 类别&#xff1a;图像分割 时间&#xff1a;2023/11/01 摘要 目前背景&#xff1a;少样本分割 &#xff08;FSS&#xff09; 得到了广泛的发展。以前的大多数工作都在努力通过分类任务衍…

Linux C语言进阶-D5~D6指针及指针的运算

指针好处&#xff1a; 使程序更加间接、紧凑、高效 有效地表示复杂的数据结构 动态分配内存 得到多于一个的函数返回值 在C语言中&#xff0c;内存单元的地址称为指针&#xff0c;专门用来存放地址的变量&#xff0c;称为指针变量 在不影响理解的情况下&#xff0c;对地址、指…

gRPC之grpcui界面工具

1、grpcui界面工具 简单的说&#xff0c;就是gRPC中的postman&#xff0c;grpcui官方地址&#xff1a;https://github.com/fullstorydev/grpcui。 1.1 安装 go get -u github.com/fullstorydev/grpcui go install github.com/fullstorydev/grpcui/cmd/grpcuiv1.2.0[rootzsx …

VueX介绍和工作原理

一、VueX的作用 VueX就是在Vue中专门集中地管理数据的一个Vue插件。 在VueX中的数据不属于任何一个组件&#xff0c;所有的组价都可以访问和修改这个数据。 因此&#xff0c;当我们的多个组件依赖同一个状态&#xff08;如用户信息&#xff09;时&#xff0c;就可以使用VueX…

【Tomcat Servlet】如何在idea上部署一个maven项目?

目录 1.创建项目 2.引入依赖 3.创建目录 4.编写代码 5.打包程序 6.部署项目 7.验证程序 什么是Tomcat和Servlet? 以idea2019为例&#xff1a; 1.创建项目 1.1 首先创建maven项目 1.2 项目名称 2.引入依赖 2.1 网址输入mvnrepository.com进入maven中央仓库->地址…

Docker 学习路线 9:运行容器

要启动一个新的容器&#xff0c;我们使用 docker run 命令&#xff0c;后跟镜像名称。基本语法如下&#xff1a; docker run [选项] 镜像 [COMMAND] [ARG...] 例如&#xff0c;要运行官方的 Nginx 镜像&#xff0c;我们可以使用&#xff1a; docker run -d -p 8080:80 nginx…

Qt5 安装 phonon

Qt5 安装 phonon Qt5 安装 phonon问题描述安装组件 Qt5 安装 phonon 开发环境&#xff1a;Qt Creator 4.6.2 Based on Qt 5.9.6 问题描述 在运行 Qt5 项目时&#xff0c;显示错误&#xff1a; error: Unknown module(s) in QT: phonon这是缺少组件的原因&#xff0c;QT: pho…

MTK联发科天玑9000旗舰5G移动平台处理器_MT6983芯片定制开发

MT6983天玑9000采用台积电4纳米工艺制程&#xff0c;CPU采用“134”三丛集Armv9架构&#xff0c;APU性能提升&#xff0c;ISP处理速度提升&#xff0c;最高支持3.2亿像素摄像头&#xff0c;采用Mali-G710十核GPU&#xff0c;搭载R16 5G调制解调器。 MT6983天玑9000芯片基本概…

吴恩达《机器学习》4-1->4-5:多变量线性回归

一、引入多维特征 在多维特征中&#xff0c;我们考虑的不再是单一的特征&#xff0c;而是一组特征&#xff0c;例如房价模型中可能包括房间数、楼层等多个特征。这些特征将组成一个向量&#xff0c;表示为(&#x1d465;₁, &#x1d465;₂, . . . , &#x1d465;ₙ)&#x…

英语教育目标转变:更加注重实际应用能力培养

今年九月份,北京市教委发布了《关于深入推进高中阶段学校考试招生改革的实施意见》。按照该意见,北京市2024年初三年级学生的初中学业水平考试英语科目听力口语考试与笔试将分离,首次计算机考试将于2023年12月17日进行。 根据《意见》规定,听力口语计算机考试共有两次考试机会…

c++ 实现二叉搜索树

二叉搜索树的概念 二叉搜索树 (BST&#xff0c;Binary Search Tree)&#xff0c;也称二叉排序树或二叉查找树。它要么是一颗空树&#xff0c;要么是满足以下性质的二叉树&#xff1a; 若它的左子树不为空&#xff0c;则左子树上所有节点的值都小于根节点的值。若它的右子树不为…

css图片保持比例and图片占满整个div

一、非背景图 ①保持宽度固定 img { width: 200px; height: auto; } ②保持高度固定 img { height: 300px; width: auto; } ③保持比例 /* 比例不变 */ img { max-width: 100%; height: auto; } /* 垂直居中 */ img { max-width: 100%; height: auto; display: block; margin:…

都2023年了,不会还有人不会设计软件测试用例叭?不会吧不会吧

一、概念 测试用例的基本概念&#xff1a; 测试用例&#xff08;Test Case&#xff09;是为了实施测试而向被测试的系统提供的一组集合&#xff0c;这组集合包含&#xff1a;测试环境、操作步骤、测试数据、预期结果等要素 。 主要步骤&#xff1a; 测试环境——测试步骤—…

有关YOLOV5在测试时,图片大小被调整的问题

执行detect.py文件&#xff0c;在运行栏中出现以下&#xff1a; detect: weightsyolov5s.pt, sourcedata\images, datadata\coco128.yaml, imgsz[640, 640], conf_thres0.25, iou_thres0.45, max_det1000, device, view_imgFalse, save_txtFalse, save_confFalse, save_cropFa…

JDK 新特性深度分析,但我用Java 8

官方文档链接&#xff1a;https://openjdk.org/projects/jdk/21/ 下载链接&#xff1a;https://www.oracle.com/cn/java/technologies/downloads/#jdk21-windows 1、介绍 JDK21 是2023.09.19发布的正式版 其他版本的含义&#xff1a; Alpha&#xff1a;软件或系统的内部测试版…

SpringBoot源码透彻解析—自动装配

花点时间找到程序入口&#xff1a; 整个自动装配的流程总结如下&#xff1a; bean工厂后置处理器(ConfigurationClassPostProcessor) 扫描spring.factories和spring-autoconfigure-metadata.properties两个文件&#xff0c;将文件中的自动装配类信息抽象成Con…

Kubernetes-网络插件

目录 一、flannel网络插件 二、calico网络插件 1、部署 2、网络策略 &#xff08;1&#xff09;限制pod流量 &#xff08;2&#xff09;限制namespace流量 &#xff08;3&#xff09;同时限制namespace和pod &#xff08;4&#xff09;限制集群外部流量 一、flannel网络…

518抽奖软件,安全稳定,不怕手抖误按键

518抽奖软件简介 518抽奖软件&#xff0c;518我要发&#xff0c;超好用的年会抽奖软件&#xff0c;简约设计风格。 包含文字号码抽奖、照片抽奖两种模式&#xff0c;支持姓名抽奖、号码抽奖、数字抽奖、照片抽奖。(www.518cj.net) 防误按功能 入口&#xff1a; 主界面上点右…

node复制当前目录下的文件夹到另一层目录(包含多层文件夹嵌套)

前段时间在跟进node项目时有个node项目的需求&#xff0c;然后上线流程是把前端build后的文件夹放到后端仓库的静态资源目录下&#xff0c;再把后端代码发布上线。这样做的好处是在前端页面调用接口时&#xff0c;可以直接 /xxx来调用&#xff08;浏览器会自动把域名补全&#…