当前位置: 首页 > news >正文

Cpp基础Ⅰ之编译、链接

1 C++是如何工作的

工具:Visual Studio

1.1 预处理语句

.cpp源文件中,所有#字符开头的语句为预处理语句

例如在下面的 Hello World 程序中

#include<iostream>int main() {std::cout <"Hello World!"<std::endl;std::cin.get();
}

#include<iostream>就是一个预处理语句(pre-process statement),编译器在加载源文件的时候,识别到#开头的语句,会优先处理这个语句,所以称为预处理语句。

注意:预处理语句是在 编译器加载源文件的时候处理的,那个时候还没有发生编译动作

1.2 include

include关键字的含义就是找到<xxx>里面指定名称的文件,然后把文件里面的内容拷贝到当前文件,以供调用;

这个被导入的文件称为头文件;

1.3 main()

main()函数是程序的入口,计算机从main()函数开始运行程序,每个程序都要有一个main()函数;

main()函数的返回值是int类型,但是在 Hello World 程序中我们没有返回任何值,这是因为main()函数比较特殊,如果没有显式返回一个int值,他会默认返回0

1.4 编译

单独编译

当写好了一个源文件,就可以对其进行编译操作,在Visual Studio上直接按快捷键ctrl + F7,或者点击编译按钮执行编译
在这里插入图片描述
编译结果在输出窗口就能看到:
在这里插入图片描述
注意,此时我们是针对一个源文件进行单独编译,而不是编译整个项目。

每次编译需要指定规则和目标平台
在这里插入图片描述

  • 规则:
    默认分为DebugRelease,代表着编译代码时按照设置选择的的规则设置进行编译,这些规则是可以自行设置的,但一般都用默认设置。比如在 Debug 规则的默认设置中,不会对程序进行优化
    在这里插入图片描述
    Release 规则的默认设置中,则会对程序进行链接优化
    在这里插入图片描述
  • 目标平台:
    意思就是你这个代码编译后是用在哪个平台的,比如x86/windows64位x64/windows32位或者是Android等移动平台(因为C++是不能跨平台运行的,所以不同的目标平台编译出来的二进制码不一样,不像Java

打开项目目录可以看到,在不同的规则下编译生成的文件分别放在不同的目录下面:
在这里插入图片描述
在目录里面可以看到.cpp文件编译生成的.obj文件
在这里插入图片描述
打开看, 里面的内容都是二进制机器码
在这里插入图片描述

项目编译

在资源文件窗口中,项目名称→右键→生成,这也是一个编译操作,但此时是编译整个项目;
它会把项目中的每个.cpp文件编译成.obj文件,然后再把这些.obj文件链接成一个程序比如.exe程序;
在这里插入图片描述
在输出窗口可以看到生成了一个.exe文件
在这里插入图片描述
打开对应的目录就能看到
在这里插入图片描述

1.5 链接

链接比较复杂,大概就是一个C++项目通常都包含这很多个源文件,而编译后每一个源文件对应地都会生成一个.obj二进制码文件,然后链接的作用就是把这些二进制码文件链接起来构成一个完整的项目。

2 定义和调用函数

写在同一个文件中:

#include<iostream>void Log(const char* message) {std::cout << message << std::endl;
}int main() {Log("Hello World!");std::cin.get();
}

写在不同的文件中:
在这里插入图片描述
Log.cpp

#include<iostream>void Log(const char* message) {std::cout << message << std::endl;
}

Main.cpp

#include<iostream>void Log(const char* message);
//void Log(const char*); // 声明函数时可以忽略参数名int main() {Log("Hello World!");std::cin.get();
}

想要在Main.cpp文件中调用Log函数,必须先声明,声明函数和定义函数的区别就是一个有方法体,一个没有方法体;

这里注意的点是,编译器在编译单个Main.cpp这个源代码文件的时候,并不会去检查这个声明的函数是否真实存在,而且编译单个文件的时候不会对编译文件进行链接;
但是当运行或者编译整个项目的时候,也就是进行文件链接的时候,如果声明的函数不存在,就会报错:
在这里插入图片描述

3 编译器如何工作

首先需要知道,编译分为两个阶段: 编译 + 链接

3.1 编译

不经过设置时, 执行编译默认会直接编译成.obj文件, 直接就是二进制码了
为了能够搞清楚从 源代码 → 二进制码 的过程中发生了什么, 我们接下来就先不直接编译成.obj文件
而是先把编译过程中预处理后产生的内容输出成文件, 看看预处理都处理了啥.

3.1.1 引入头文件

前面说过,编译器处理#include<xxx>这个语句就是把对应的xxx头文件里面的内容copy到当前文件中#include语句所在的地方

下面来证实一下
首先打开项目属性:
在这里插入图片描述
这一步是为了让编译器在预编译后把内容输出成一个文件, 这样我们就可以看到预编译的内容了, 注意这个设置也是精确到配合和平台的, 选择所有配置和自己的操作系统平台就行;

注意: 修改了输出预编译文件后, 编译器就不会输出.obj文件, 所以做完实验就要把它改回去!!

系统头文件

Main.cpp中, 我们引入了iostream头文件

#include<iostream>int main() {std::cout << "Hello World!" << std::endl;std::cin.get();
}

编译一下, 生成的.i文件就是预编译文件
在这里插入图片描述
可以发现这个文件有1.6M大, 我只写了几行代码
直接打开看
在这里插入图片描述
会发现这个文件有6万多行, 这些内容就是从iostream头文件中copy过来的, 所以整个文件很大
这就印证了#inclued引入语句的作用.

自定义头文件

下面我们要编译这个Mutiply.cpp

int Mutiply(int a, int b) {int result = a * b;return result;

可以看到这个函数是缺少了一个}的, 故意的
接下来创建一个头文件EndBrace.h
在这里插入图片描述
头文件里面的内容就是一个}

}

接下来在Mutiply.cpp中引入这个EndBrace.h头文件
在这里插入图片描述
引入后Mutiply.cpp内容如下:

int Mutiply(int a,int b) {int result = a * b;return result;
#include"EndBrace.h"

好,接下来编译一下这个Mutiply.cpp源文件
在项目对应目录中可以看到生成了一个Mutiply.i文件, 这个就是预编译生成的文件
在这里插入图片描述
直接打开看内容

#line 1 "D:\\workspace\\CPP\\cherno\\cherno\\Mutiply.cpp"int Mutiply(int a,int b) {int result = a * b;return result;#line 1 "D:\\workspace\\CPP\\cherno\\cherno\\EndBrace.h"
}
#line 5 "D:\\workspace\\CPP\\cherno\\cherno\\Mutiply.cpp"

忽略那些#line语句, 可以看到EndBrace.h头文件中的}被复制到了Mutiply.cpp

注意,在c++中,引入头文件有两种方式:

  • #include<>这个语法用于引入系统头文件, 这种引入方式下预处理器会在标准系统目录中搜索这些文件。例如引入iostream头文件,可以使用#include <iostream>
  • #include ""语法用于引入用户定义的头文件, 这种引入方式下预处理器会首先在当前目录中搜索这些文件,如果没有找到,则在标准系统目录中搜索。例如,如果当前目录中有一个名为myheader.h的头文件,则可以使用#include "myheader.h"将其包含到程序中。

3.1.2 自定义类型

还是在Mutiply.cpp中做修改,

#define ECHOO intECHOO Mutiply(int a, int b) {ECHOO result = a * b;return result;
}

这里我自定义了一个ECHOO类型, 实际上是一个int类型
然后我在Mutiply定义中用了这个ECHOO类型代替原来的int

接下来编译看一下预编译生成的文件内容:

#line 1 "D:\\workspace\\CPP\\cherno\\cherno\\Mutiply.cpp"
int Mutiply(int a, int b) {int result = a * b;return result;
}

编译器自动把ECHOO替换成了它的实际类型int,
所以说#define实际上是自定义别名, 用一个别名代替实际的类型或者字符,符号
这也是C++灵活的地方,可以给各种类型, 符号自定义别名

再改一下, 用ECHOO代替Hello

#define ECHOO HelloECHOO Mutiply(int a, int b) {ECHOO result = a * b;return result;
}

预编译:

#line 1 "D:\\workspace\\CPP\\cherno\\cherno\\Mutiply.cpp"
Hello Mutiply(int a, int b) {Hello result = a * b;return result;
}

有点意思

3.1.3 条件判断

改一下Mutiply.cpp, 用#if语句来做条件判断

#if 0
int MutiplyOne(int a, int b) {int result = a * b;return result;//#include "EndBrace.h"
}
#endif // 0#if 1
int MutiplyTwo(int a, int b) {int result = a * b;return result;//#include "EndBrace.h"
}
#endif // 1

函数MutiplyOne#if 0#endif包起来了
函数MutiplyTwo#if 1#endif包起来了

预编译:

#line 1 "D:\\workspace\\CPP\\cherno\\cherno\\Mutiply.cpp"
#line 8 "D:\\workspace\\CPP\\cherno\\cherno\\Mutiply.cpp"
int MutiplyTwo(int a, int b) {int result = a * b;return result;
}
#line 17 "D:\\workspace\\CPP\\cherno\\cherno\\Mutiply.cpp"

函数MutiplyOne不能被预编译
函数MutiplyTwo能正常被预编译
非常明显, 是因为判断条件的原因
我们可以通过#if condition这个语句来动态地禁用 / 启用某一段代码, 非常灵活
有点儿意思

拓展: 汇编

通过Visual Studio可以输出汇编文件, 设置一下汇编程序输出
,
接下来编译Mutiply.cpp:

int MutiplyOne(int a, int b) {int result = a * b;return result;//#include "EndBrace.h"
}

在项目目录中生成的.asm文件就是生成的汇编程序文件
在这里插入图片描述
打开可以看到一条一条的汇编指令,

; Listing generated by Microsoft (R) Optimizing Compiler Version 19.36.32537.0 include listing.incINCLUDELIB MSVCRTD
INCLUDELIB OLDNAMESmsvcjmc	SEGMENT
__B1702CDC_Mutiply@cpp DB 01H
msvcjmc	ENDS
PUBLIC	?MutiplyOne@@YAHHH@Z				; MutiplyOne
PUBLIC	__JustMyCode_Default
EXTRN	_RTC_InitBase:PROC
EXTRN	_RTC_Shutdown:PROC
EXTRN	__CheckForDebuggerJustMyCode:PROC
;	COMDAT pdata
pdata	SEGMENT
$pdata$?MutiplyOne@@YAHHH@Z DD imagerel $LN3DD	imagerel $LN3+63DD	imagerel $unwind$?MutiplyOne@@YAHHH@Z
pdata	ENDS
;	COMDAT rtc$TMZ
rtc$TMZ	SEGMENT
_RTC_Shutdown.rtc$TMZ DQ FLAT:_RTC_Shutdown
rtc$TMZ	ENDS
;	COMDAT rtc$IMZ
rtc$IMZ	SEGMENT
_RTC_InitBase.rtc$IMZ DQ FLAT:_RTC_InitBase
rtc$IMZ	ENDS
;	COMDAT xdata
xdata	SEGMENT
$unwind$?MutiplyOne@@YAHHH@Z DD 025051601HDD	01112316HDD	0700a0021HDD	05009H
xdata	ENDS
; Function compile flags: /Odt
;	COMDAT __JustMyCode_Default
_TEXT	SEGMENT
__JustMyCode_Default PROC				; COMDATret	0
__JustMyCode_Default ENDP
_TEXT	ENDS
; Function compile flags: /Odtp /RTCsu /ZI
;	COMDAT ?MutiplyOne@@YAHHH@Z
_TEXT	SEGMENT
result$ = 4
a$ = 256
b$ = 264
?MutiplyOne@@YAHHH@Z PROC				; MutiplyOne, COMDAT
; File D:\workspace\CPP\cherno\cherno\Mutiply.cpp
; Line 2
$LN3:mov	DWORD PTR [rsp+16], edxmov	DWORD PTR [rsp+8], ecxpush	rbppush	rdisub	rsp, 264				; 00000108Hlea	rbp, QWORD PTR [rsp+32]lea	rcx, OFFSET FLAT:__B1702CDC_Mutiply@cppcall	__CheckForDebuggerJustMyCode
; Line 3mov	eax, DWORD PTR a$[rbp]imul	eax, DWORD PTR b$[rbp]mov	DWORD PTR result$[rbp], eax
; Line 4mov	eax, DWORD PTR result$[rbp]
; Line 6lea	rsp, QWORD PTR [rbp+232]pop	rdipop	rbpret	0
?MutiplyOne@@YAHHH@Z ENDP				; MutiplyOne
_TEXT	ENDS
END

看汇编指令在某些需要极致性能优化的时候会很有用, 但一般很少没多少人会这样做.

3.2 链接

当源文件被编译成一个个.obj文件的时候, 它们实际上还是一个一个独立的文件, 彼此直接没有关系
链接就是把所有.obj文件链接在一起,形成一个完整的程序
而这个程序必须有一个起始函数,如果没有特殊指定,这个起始函数默认是main函数!

3.2.1 起始函数

所以当一个项目是没有main函数的时候,单独编译文件不会报错,但是build或者运行项目的时候会报错

例:
当前项目中只有两个源文件,都不包含main函数
在这里插入图片描述
单独编译这两个源文件都没问题
但是当我生成整个项目时,发生了报错LNK1120
在这里插入图片描述
LNK 开头的错误是链接阶段发生的错误;
C 开头的错误是编译阶段发生的错误,比如语法错误;

现在我加一个源文件,里面声明了MutiplyOneLog函数,定义了main函数
并且在main函数中调用MutiplyOneLog函数

#include<iostream>int MutiplyOne(int a, int b);
void Log(const char* message);int main() {Log("Hello World!");std::cout << MutiplyOne(5, 18) << std::endl;std::cin.get();
}

在这里插入图片描述
直接运行项目,成功

Hello World!
90

这就代表着程序编译成功,链接器也成功找到了程序的起始函数,并成功把相关的函数找到、链接起来了。

3.2.2 被调用的函数

如果被调用的函数没有在当前源文件中声明

#include<iostream>int MutiplyOne(int a, int b);
//void Log(const char* message);int main() {Log("Hello World!");std::cout << MutiplyOne(5, 18) << std::endl;std::cin.get();
}

会报编译错误,因为这个属于语法错误:
在这里插入图片描述
如果被调用的函数不存在
会报链接错误:
在这里插入图片描述
所以很明显,链接的作用是把以起始函数为根节点,所有被调用到的函数链接起来,形成一整条调用链(或者说一整棵调用树)
在这里插入图片描述

如果想要指定其他函数作为程序的起始函数,可以通过链接器的高级设置指定:
在这里插入图片描述

3.3 总结,编译和链接的区别

编译:是先对源文件进行预处理,引入头文件,把头文件内容copy到源文件中,然后再把这些源文件编译成.obj或其他格式的二进制文件

链接:是把编译好的.obj文件里面相互调用的函数链接起来,形成一个完整的程序

相关文章:

Cpp基础Ⅰ之编译、链接

1 C是如何工作的 工具&#xff1a;Visual Studio 1.1 预处理语句 在.cpp源文件中&#xff0c;所有#字符开头的语句为预处理语句 例如在下面的 Hello World 程序中 #include<iostream>int main() {std::cout <"Hello World!"<std::endl;std::cin.get…...

用户新增预测(Datawhale机器学习AI夏令营第三期)

文章目录 简介任务1&#xff1a;跑通Baseline实操并回答下面问题&#xff1a;如果将submit.csv提交到讯飞比赛页面&#xff0c;会有多少的分数&#xff1f;代码中如何对udmp进行了人工的onehot&#xff1f; 任务2.1&#xff1a;数据分析与可视化编写代码回答下面的问题&#xf…...

RGOS日常管理操作

RGOS日常管理操作 一、前言二、RGOS平台概述2.1、锐捷设备的常用登陆方式2.2、使用Console登入2.3、Telnet远程管理2.4、SSH远程管理2.5、登陆软件&#xff1a;SecureCRT 三、CLI命令行操作3.1、CLI命令行基础3.2、CLI模式3.3、CLI模式互换3.4、命令行特性3.4.1、分屏显示3.4.2…...

阿里云使用WordPress搭建个人博客

手把手教你使用阿里云服务器搭建个人博客 一、免费创建服务器实例 1.1 点击试用 点击试用会需要你创建服务器实例&#xff0c;直接选择默认的操作系统即可&#xff0c;点击下一步 1.2 修改服务器账号密码 二、创建云数据库实例 2.1 免费获取云数据库使用 2.2 实例列表页 在…...

供应链安全和第三方风险管理:讨论如何应对供应链中的安全风险,以及评估和管理第三方合作伙伴可能带来的威胁

第一章&#xff1a;引言 在当今数字化时代&#xff0c;供应链的安全性越来越受到重视。企业的成功不仅仅依赖于产品和服务的质量&#xff0c;还取决于供应链中的安全性。然而&#xff0c;随着供应链越来越复杂&#xff0c;第三方合作伙伴的参与也带来了一系列安全风险。本文将…...

《Java极简设计模式》第04章:建造者模式(Builder)

作者&#xff1a;冰河 星球&#xff1a;http://m6z.cn/6aeFbs 博客&#xff1a;https://binghe.gitcode.host 文章汇总&#xff1a;https://binghe.gitcode.host/md/all/all.html 源码地址&#xff1a;https://github.com/binghe001/java-simple-design-patterns/tree/master/j…...

Go download

https://go.dev/dl/https://golang.google.cn/dl/...

2023年Java核心技术面试第四篇(篇篇万字精讲)

目录 八. 对比Vector&#xff0c;ArrayList, LinkedList有何区别&#xff1f; 8.1 典型回答 8.1.1 Vector&#xff1a; 8.1.2 ArrayList &#xff1a; 8.1.3 LinkedList 8.2 考察点分析&#xff1a; 8.2.1 不同容器类型适合的场景 八. 对比Vector&#xff0c;ArrayList, Linke…...

数字化时代,数据仓库和商业智能BI系统演进的五个阶段

数字化在逐渐成熟的同时&#xff0c;社会上也对数字化的性质有了进一步认识。当下&#xff0c;数字化除了前边提到的将复杂的信息、知识转化为可以度量的数字、数据&#xff0c;在将其转化为二进制代码&#xff0c;引入计算机内部&#xff0c;建立数据模型&#xff0c;统一进行…...

【【Verilog典型电路设计之FIFO设计】】

典型电路设计之FIFO设计 FIFO (First In First Out&#xff09;是一种先进先出的数据缓存器&#xff0c;通常用于接口电路的数据缓存。与普通存储器的区别是没有外部读写地址线&#xff0c;可以使用两个时钟分别进行写和读操作。FIFO只能顺序写入数据和顺序读出数据&#xff0…...

JAVA设计模式总结之23种设计模式

一、什么是设计模式 设计模式&#xff08;Design pattern&#xff09;是一套被反复使用、多数人知晓的、经过分类编目的、代码设计…...

Flutter 测试小结

Flutter 项目结构 pubspec.yaml 类似于 RN 的 package.json&#xff0c;该文件分别在最外层及 example 中有&#xff0c;更新该文件后&#xff0c;需要执行的 Pub get lib 目录下的 dart 文件为 Flutter 插件封装后的接口源码&#xff0c;方便在其他 dart 文件中调用 example 目…...

docker build -t 和 docker build -f 区别

docker build 是用于构建Docker镜像的命令&#xff0c;它允许你基于一个Dockerfile来创建一个镜像。在 docker build 命令中&#xff0c;有两个常用的选项 -t 和 -f&#xff0c;它们有不同的作用。 -t’选项&#xff1a; -t’选项用于指定构建出来的镜像的名称和标签。格式为 &…...

Java 项目日志实例基础:Log4j

点击下方关注我&#xff0c;然后右上角点击...“设为星标”&#xff0c;就能第一时间收到更新推送啦~~~ 介绍几个日志使用方面的基础知识。 1 Log4j 1、Log4j 介绍 Log4j&#xff08;log for java&#xff09;是 Apache 的一个开源项目&#xff0c;通过使用 Log4j&#xff0c;我…...

K8S应用笔记 —— 签发自签名证书用于Ingress的https配置

一、需求描述 在本地签发自命名证书&#xff0c;用于K8S集群的Ingress的https配置。 前提条件&#xff1a; 完成K8S集群搭建。完成证书制作机器的openssl服务安装。 二、自签名证书制作 2.1 脚本及配置文件准备 2.1.1 CA.sh脚本准备 注意事项&#xff1a; openssl服务默认CA…...

webpack 和 ts 简单配置及使用

如何使用webpack 与 ts结合使用 新建项目 &#xff0c;执行项目初始化 npm init -y会生成 {"name": "tsdemo01","version": "1.0.0","description": "","main": "index.js","scripts&…...

MATLAB算法实战应用案例精讲-【图像处理】交并比

目录 交并比 非极大值抑制 Soft NMS Soft NMS 提出背景 Soft NMS 算法流程 Soft NMS 算法示例...

[Machine Learning] decision tree 决策树

&#xff08;为了节约时间&#xff0c;后面关于机器学习和有关内容哦就是用中文进行书写了&#xff0c;如果有需要的话&#xff0c;我在目前手头项目交工以后&#xff0c;用英文重写一遍&#xff09; &#xff08;祝&#xff0c;本文同时用于比赛学习笔记和机器学习基础课程&a…...

【数学建模】-- 数学规划模型

概述&#xff1a; 什么是数学规划&#xff1f; 数学建模中的数学规划是指利用数学方法和技巧对问题进行数学建模&#xff0c;并通过数学规划模型求解最优解的过程。数学规划是一种数学优化方法&#xff0c;旨在找到使目标函数达到最大值或最小值的变量取值&#xff0c;同时满足…...

SpringBoot使用RabbitMQ自动创建Exchange和Queue

背景 小项目&#xff0c;使用RabbitMQ作为消息队列&#xff0c;发布到不同的新环境时&#xff0c;由于新搭建的MQ中不存在Exchange和Queue&#xff0c;就会出错&#xff0c;还得手动去创建&#xff0c;比较麻烦&#xff0c;于是想在代码中将这些定义好后&#xff0c;自动控制M…...

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…...

云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?

大家好&#xff0c;欢迎来到《云原生核心技术》系列的第七篇&#xff01; 在上一篇&#xff0c;我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在&#xff0c;我们就像一个拥有了一块崭新数字土地的农场主&#xff0c;是时…...

【kafka】Golang实现分布式Masscan任务调度系统

要求&#xff1a; 输出两个程序&#xff0c;一个命令行程序&#xff08;命令行参数用flag&#xff09;和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽&#xff0c;然后将消息推送到kafka里面。 服务端程序&#xff1a; 从kafka消费者接收…...

Docker 运行 Kafka 带 SASL 认证教程

Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明&#xff1a;server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...

P3 QT项目----记事本(3.8)

3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...

【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具

第2章 虚拟机性能监控&#xff0c;故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令&#xff1a;jps [options] [hostid] 功能&#xff1a;本地虚拟机进程显示进程ID&#xff08;与ps相同&#xff09;&#xff0c;可同时显示主类&#x…...

力扣-35.搜索插入位置

题目描述 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...

Java 二维码

Java 二维码 **技术&#xff1a;**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...

面向无人机海岸带生态系统监测的语义分割基准数据集

描述&#xff1a;海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而&#xff0c;目前该领域仍面临一个挑战&#xff0c;即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...

MySQL JOIN 表过多的优化思路

当 MySQL 查询涉及大量表 JOIN 时&#xff0c;性能会显著下降。以下是优化思路和简易实现方法&#xff1a; 一、核心优化思路 减少 JOIN 数量 数据冗余&#xff1a;添加必要的冗余字段&#xff08;如订单表直接存储用户名&#xff09;合并表&#xff1a;将频繁关联的小表合并成…...