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

GCC/Make/CMake 工具链

阅读前可以思考的问题:(答案在文章的最后面,小白可以略过)

  • GCC/Make/CMake是什么关系?

  • 一个C++程序编译为一个可执行文件,需要哪些过程?

  • #include语句所引入的库,如何才能找到对应的完整源代码文件?

  • 静态链接和动态链接有什么区别?

  • 为什么要用make,直接使用shell脚本不能编译吗?

--------------正文开始----------

GCC/Make/CMake

              cmake           make       gcc
CMakelist.txt -----> Makefile ----> Cmds ---> Binary

开发者需要编写CMakelist.txt文件,来配置项目相关的CMake参数。 通过运行cmake命令,自动生成对应平台的Make工具自动构建脚本Makefile文件。 当然,CMake也支持生成其他的构建工具的配置文件,比如Xcode的xxxx.xcodeproj,Visual Studio的xxxx.sln,Ninja的xxxx.ninja等等。 目前,大多数开源的C/C++项目都支持使用CMake生成Makefile文件,再调用make命令,使用Make工具进行自动构建。 Makefile文件可以看成是一系列依赖于文件的Shell命令。 它基于文件修改的时间戳来实现增量式处理。 具体规则大致如下,若生成的目标文件的时间戳早于依赖文件的时间戳时,则执行对应的命令,重新生成目标文件。 这实际上暗示了,Make工具不只用于编译,还可以用于其他的增量式文件生成任务。 使用Make工具来编译C/C++项目时,一般会使用Shell命令来调用gcc自动化增量式地实现C/C++源代码的编译链接等一系列工作。

1. GCC

基本全部引用自GCC/Make/CMake 之 GCC - 知乎 (zhihu.com)。感谢原作者FRONTIERS。

在早期,GCC为GNU C Compiler的简写,即GNU计划中的C语言编译器。 但经过多年的扩展和迭代,GCC逐渐支持C、C++、Objective-C、Fortran、Java、Ada和Go等越来越多语言的编译。 因此,其GCC被重新定义为GNU Compiler Collection,即 GNU编译器套件。 在本篇中,我们仅介绍使用GCC编译C/C++项目。

使用gcc编译C/C++程序时,主要的编译流程如下,包含预处理编译汇编链接等四个步骤。 以输入C语言程序源码文件b.c为例,直接调用命令gcc b.c,将会完整执行以下流程,并生成对应的可执行二进制文件a.out。 注意,这里gcc的默认输出就是固定的a.out。 在GCC工具链中,汇编由工具as完成,链接则由工具ld完成。

      -E          -S          -c          
b.c ------> b.i ------> b.s ------> b.o ------> a.outgcc         gcc         as          ld

gcc使用以下指令,将会使其编译流程停止在对应位置:

  • -E,(prEprocessing),执行到预处理步骤之后,即处理C/C++源码中#开头的指令,包括宏展开以及#include头文件引入等等。 该指令默认不输出文件,可以使用-o指令输出约定后缀为*.i的文件。
  • -S,(aSsembly),执行到编译步骤之后,生成汇编文件,但不生成二进制机器码。 该指令默认的输出文件后缀为*.s
  • -c,(compilation),执行到汇编步骤之后,调用工具as,从汇编码生成二进制机器码,但不进行链接。 该指令默认的输出文件后缀为*.oobject)。
  • 不带以上参数调用gcc将会完整执行以上流程,即执行到到链接(linking)步骤之后。 链接步骤实际上调用链接工具ld来执行,会将源码生成的二进制文件,库文件,以及程序的启动部分进行组合,从而形成一个完整的二进制可执行文件。

特别的,使用指令-o,(output),可以指定输出文件的名称。 例如gcc b.c -o b.bin,将生成可执行文件b.bin,而不是默认的a.out

以上指令都可以在编译流程任意环节的基础上进行调用,例如:

> gcc -E b.c -o b.i
> ls
b.c b.i
> gcc -S b.i
b.c b.i b.s
> gcc -c b.s
b.c b.i b.o b.s
> gcc b.o
b.c b.i b.o a.out b.s

1.1包管理

后续我们将介绍GCC的主要编译参数,但在这之前,笔者希望介绍的核心内容是「如何从包管理的角度使用GCC编译参数」。 这也是笔者希望向读者介绍的内容。 因此,本小节将首先介绍C/C++项目的包管理方式。

对于一个实际的C/C++项目而言,源文件一般不会只有一个,而且绝大多数情况下会使用到第三方库(Third-party Library)。 由于C/C++没有官方的包管理工具(Package Manager),如Python的pip,Java的maven,Nodejs的npm等等, 所以,在C/C++项目中使用第三方库时,一般使用系统自带的包管理器来进行第三方库的安装,例如Ubuntu下的apt-get,macOS的brew(Homebrew)等等。 对于系统包管理器未收纳的第三方库,我们一般会选择自行编译安装,或者将其作为子项目共同编译。

第三方库主要由两个部分组成,即 a)头文件, b)库文件。 头文件一般是一系列名为xxx.hhead)的文件,相当于暴露出第三方库所提供的API接口(函数签名)。 库文件一般会包含静态库文件动态库文件,相当于第三方库在功能上的二进制实现。 其中,静态库文件是一系列名为libxxx.aarchive)的文件(Windows下为libxxx.liblibrary)。 动态库文件则是一系列名为libxxx.soshared object)的文件(Windows下为libxxx.dlldynamic link library,macOS下为libxxx.dylibdynamic library)。 系统自带的,以及由系统包管理器安装的第三方库,其头文件一般在/usr/include/usr/local/include路径下,库文件一般在/lib/usr/lib/usr/local/lib目录下。

正是由于以上因素的影响,GCC工具链不负责管理第三方库,因此无法判定C/C++项目具体需要使用哪些库,以及这些库的准确信息,如位置、版本等。 所以,仅使用GCC,无法完全自动地解决C/C++项目第三方库的依赖问题。 即无法像Python、Java等语言,仅需要使用import xxx语句导入相应的包,而语言的包管理器能够自动地解决第三方库的依赖关系。 C/C++语言在使用#include "xxx"语句后,我们还需要人工地添加各种编译参数,如-I-l以及-L,将所依赖的第三方库的相关信息,传递给gcc编译器。 其中,-I传递的是「头文件所在的目录」,-l传递的是需要链接的「库的名称」,-L传递的是「库文件所在的目录」。 这三个参数尤其重要,希望读者牢记在心。

1.2编译参数

-I参数

回顾之前所介绍的GCC编译流程,在预处理阶段需要处理#include指令,将包含的头文件替换进源码。 一般来说,在进行预处理时,gcc会自动在当前工程目录下,以及/usr/include目录下寻找对应的头文件。

但对于位于其他目录下的第三方库的头文件,gcc无法自动寻找到所需头文件的位置,会报出形如xxx.h: file not found的错误。 我们需要使用-I参数来指定第三方库头文件的位置。 例如,在macOS下,使用Homebrew包管理器安装llvm,会相应地安装LLVM项目所包含的第三方库,其对应的头文件位于/usr/local/opt/llvm/include目录。

而我们在使用LLVM提供的库时,可以使用-I/usr/local/opt/llvm/include(或者-I /usr/local/opt/llvm/include,加空格)来指定头文件所在的位置。 从而,gcc会额外在-I参数指定的目录下搜索对应的头文件。 -I参数可以重复多次使用,从而指定多个额外的头文件目录。 -I参数一般指定绝对路径,但也可以用相对路径,比如头文件在当前目录,可以用-I.来指定。

需要注意的是,在C/C++源码中,使用#include"xxxx.h"语句时,其中的xxxx.h可以带上路径。 我们甚至可以使用绝对路径来引用头文件。 比如说,存在头文件/usr/local/opt/llvm/include/llvm/Pass.h,我们在使用它时,可以直接通过这样的方式引用#include"/usr/local/opt/llvm/include/llvm/Pass.h"

不过,在C/C++工程中,并不推荐这种做法。 比较推荐的做法是,使用相对路径加参数-I include_dir的方法来引用头文件。 比如以上的例子中,我们会直接在源码中使用#include"llvm/Pass.h",并且将llvm库的头文件所在目录,通过参数-I /usr/local/opt/llvm/include传递给gcc。 这样做能够灵活地管理第三方库版本,也便于不同机器下的多人协作开发,比直接包含绝对路径头文件要好很多。

总而言之,gcc在进行预处理时,会将库文件目录(如-I参数传递进来的目录,以及默认的/usr/include/usr/local/include等目录),与程序源码中#include"xxxx.h"语句的xxxx.h进行组合拼接。 倘若某个组合,得到的路径存在实际的头文件,那么就会将该头文件包含进来。

-l参数

在GCC编译流程的链接阶段,会默认链接标准库,如libc.a,但是对于第三方库,就需要手动添加。 倘若在编译中报出如下的错误: Undefined symbols for architecture x86_64: xxx...xxx ld: symbol(s) not found for architecture x86_64这一般是由未正确指定需要链接的第三方库导致的。

在使用gcc时,一般会选择使用-l参数来指定需要链接的库。 例如,假定我们使用了math库(即#include<math.c>),在进行编译时,便会报出如上的Undefined错误。 这时,我们可以使用-lm(或者-l m)参数来指定需要链接math库。

注意,某些gcc编译器会把math库视为标准库进行自动链接。 这时我们需要加上-nostdlib参数,使其不自动链接标准库,才会报出如上的Undefined错误。

初看-lm参数,可能会感觉有些诡异。 那么,-l参数具体是如何使用的呢? -l参数后需要接库名(如m),而不是库文件名(如libm.so)。 但库名和库文件名之间,存在非常直观的联系。 以math库为例,其库文件名是libm.so,而库名是m。 从中很容易看出,库名就是把库文件名的前缀lib和后缀名.so去掉后得到的。 再比如说,LLVM包含的库文件libLLVMCore.a,其对应的库名就是LLVMCore,而链接它的参数为-lLLVMCore

-L参数

位于/lib/usr/lib/usr/local/lib等目录下的库文件,例如libm.so,在使用-l参数后,可以直接被链接。 但如果库文件不在这些目录里,只用-l参数,进行链接时仍会报错,ld: library not found for -lxxx。 这意味着链接程序ld在当前的库文件路径中,无法找到libxxx.solibxxx.a

这时,我们需要使用-L参数,将所要链接的库文件所在的路径告诉gcc-L参数后需要跟库文件所在的路径。 例如,在macOS下,使用Homebrew包管理器安装llvm,其对应的库文件位于/usr/local/opt/llvm/lib目录。 倘若我们需要使用库LLVMCore,即链接库文件libLLVMCore.a,除了添加-lLLVMCore参数外,还需要使用参数-L/usr/local/opt/llvm/lib,告诉gcc库文件所在的目录。

其他编译参数

除了以上的这些参数外,gcc还有一些其他的参数,也是比较重要的,在此分别简要介绍。

A. 静态链接参数

在前面讲库文件的时候,我们提到了静态链接库文件libxxx.a)和动态链接库文件libxxx.so)。 我们并未提及两者的区别。 其实,我们通过如下的方式简单进行理解。 gcc链接静态库文件,会将静态库文件中用到的部分,拷贝到生成的二进制程序中,从而导致生成的文件比较大; 而链接动态库文件,则不会进行拷贝,所以生成的二进制程序会比较小。 链接动态库文件的缺点是,在其他机器上运行该程序时,要求其上正确安装了对应的动态库文件。 相应的,链接静态库文件生成的程序,则没有这个要求。

在使用gcc进行链接时,默认优先使用动态链接库文件。 仅当动态链接库文件不存在时,才使用静态链接库文件。 如果需要使用静态链接的方式,则需要在编译时加上-static参数,强制使用静态链接库文件。 例如,在/usr/local/opt/llvm/lib目录下,同时存在库文件libunwind.solibunwind.a。 为了让gcc在链接时使用静态链接库文件libunwind.a,我们可以添加-static参数,使用如下编译命令gcc hello.o –static –L/usr/local/opt/llvm/lib –lunwind

B. 优化参数

编译优化也是编译器的重要功能,适当的编译优化能大大加速程序的执行效率。 gcc提供了4级优化参数,分别是-O0-O1-O2-O3。 一般来说,数字越大,所包含的编译优化策略就越多。 此外,gcc还提供了特殊的-Os参数。

  • -O0参数表示不使用任何优化策略,是gcc默认的优化参数。 因为没有使用任何优化策略,编译得到的机器码与程序源码高度对应,两者之间基本可以建立一一对应的关系。所以,-O0优化非常适合用于程序调试,并且通常和生成调试信息的参数-ggenerate debug information)配合使用。-g参数会在编译时给生成的二进制文件附加一些用于代码调试的信息,比如符号表和程序源码。
  • -O1会尽量采用一些不影响编译速度的优化策略,降低生成的二进制文件的大小,以及提高程序执行的速度。
  • -O2使用-O1中的所有优化策划,还会采用一些会降低编译速度的优化策略,以提高程序的执行速度。
  • -O3-O2的基础上,使用更多的优化策略。这些额外的优化策略会进一步降低编译速度,而且会增加生成的二进制文件的大小,但程序的执行速度则会进一步提高
  • -Os则和-O3优化的方向相反。它在-O2的基础上,采用额外的优化策略,尽量的降低生成的二进制文件的大小。

倘若对各优化参数下,所开启的优化策略感兴趣,或者希望了解其他的优化参数,可以参考[1]。

C. 宏相关参数

有时,为了保证C/C++项目的跨平台性,或者在编译时,能比较灵活地在多个相似的库中作出选择,需要在源码中使用条件编译。 条件编译即使用#ifdef M#else#endif(或#ifndef M#else#endif,以及#if#elif#else#endif)等指令,通过宏定义来控制需要编译的代码。

C/C++语言中,可以使用#define M语句在源码中定义宏M。 但是条件编译一般需要从外界,如编译器,传入一个宏定义。 因此,gcc提供了宏定义参数-D以及取消宏定义参数-U。 在使用gcc进行编译时,可以通过如下的方式,来进行相应的宏操作:

  • -Dmacro定义宏macro,默认将其定义为1,相当于在程序源码中使用#define macro语句。
  • -Dmacro=def定义宏macrodef,相当于在程序源码中使用#define macro=def语句。
  • -Umacro取消宏macro的定义,相当于在程序源码中使用#undef macro语句。
  • -undef取消所有非标准宏的定义。
D. 其他

此外,还有一些其他的参数,也很重要,例如:

  • -std参数可以指定编译使用的C/C++标准。例如,-std=c++11表示使用C++11标准,-std=c99表示使用C99标准。特殊的,-ansi表示使用ANSI C标准,一般等同于-std=c90
  • -Werror参数要求gcc将产生的警告(Warning)当成错误(Error)进行显示。
  • -Wall要求gcc显示出尽可能多的警告信息。
  • -w要求gcc不显示警告信息。
  • -Wl参数告诉gcc,将后面跟随的参数传递给链接器ld
  • -v参数可以显示gcc编译过程中一些额外输出信息。

倘若希望了解gcc的其他参数,可以通过gcc --help或者man gcc查看,也可以直接参考[GCC手册1]。

1.3 编译参数自动生成(pkg-config)

一般来说,人工编辑第三方库的编译链接参数是比较麻烦的。 我们需要查找第三方库的头文件、库文件的安装路径,了解第三方库需要链接哪些其他的库,了解第三方库需要哪些编译参数等等。 这些都不利于第三方库的快速集成。 目前,很多现代的第三方库都提供了其对应的编译参数自动生成工具,一般名为xxx-config。 比如llvm就提供了llvm-config工具。 在使用系统包管理器,或者自行编译安装了llvm后,可以直接调用llvm-config命令。 我们以以llvm 10.0为例,进行说明。

  • 执行llvm-config --cxxflags,可以得到-I/usr/local/Cellar/llvm/11.0.0/include -std=c++14 -stdlib=libc++ -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS。 这是编译llvm 10.0提供的库,所需的编译参数。 它说明llvm 10.0的头文件目录是/usr/local/Cellar/llvm/11.0.0/include,并且要求使用C++14标准,使用C++标准库,还定义了一些编译时需要的宏。
  • 执行llvm-config --ldflags,可以得到-L/usr/local/Cellar/llvm/11.0.0/lib -Wl,-search_paths_first -Wl,-headerpad_max_install_names。 这是链接llvm 10.0提供的第三方库所需要的链接参数。 它告诉编译器,第三方库的位置在/usr/local/Cellar/llvm/11.0.0/lib,并会传递一些其他的参数给链接器ld
  • 执行llvm-config --libs会得到-lLLVMXRay -lLLVMWindowsManifest ... -lLLVMDemangle。 这是llvm 10.0可以链接的全部库。 一般我们不会选择链接所有的库。 而是会使用形如以下的命令llvm-config --libs core,得到 -lLLVMCore -lLLVMRemarks -lLLVMBitstreamReader -lLLVMBinaryFormat -lLLVMSupport -lLLVMDemangle。 这是使用core模块所需要链接的库。
  • 执行llvm-config --system-libs会得到-lm -lz -lcurses -lxml2。 这是llvm 10.0所需要用到的系统库。

一般来说,我们会将以上命令的参数进行组合使用,例如调用llvm-config --cxxflags --ldflags --system-libs --libs core,就可以得到我们所需的全部编译参数。

除了第三方库自带的xxx-config以外,很多现代的第三方库都可以使用工具pkg-config来生成编译参数。 我们可以用pkg-config --list-all命令,来查看其所支持的所有第三方库。 pkg-config的一般使用方法是调用形如pkg-config pkg-name --libs --cflags的命令。 例如,倘若要使用gmp库,我们可以执行pkg-config gmp --libs --cflags,得到如下输出 -I/usr/local/Cellar/gmp/6.2.1/include -L/usr/local/Cellar/gmp/6.2.1/lib -lgmp

我们可以直接复制这些输出,再粘贴到gcc命令后,也可以使用形如"gcc a.c pkg-config gmp --libs --cflags"的命令,通过内嵌shell命令的方式,将第三方库的编译参数传递给gcc.

2. make

用gcc编译大型项目,需要很多行shell代码,为了方便复用,可以把代码用sh脚本保存下来。

但是直接使用sh脚本有很多问题,所以make应运而生。

make相比sh脚本,有以下好处:

  • 提升编译性能。
    • 按需编译。如果重新编译项目,不会从头完整编译,而是只编译有变化的部分。
    • 多线程。
  • 自动解决依赖关系
    • 多文件中的头文件依赖类似于一个拓扑排序问题,make可以自动找到最优路径。

makefile举例

Makefile是make所依赖的脚本。

# makefile示例
# 定义变量
CC = g++
CFLAGS = -Wall -g
# 定义目标文件
all: main
# 定义main的依赖文件和生成规则
main: main.o add.o sub.o$(CC) $(CFLAGS) -o main main.o add.o sub.o
# 定义main.o的依赖文件和生成规则
main.o: main.cpp add.h sub.h$(CC) $(CFLAGS) -c main.cpp
# 定义add.o的依赖文件和生成规则
add.o: add.cpp add.h$(CC) $(CFLAGS) -c add.cpp
# 定义sub.o的依赖文件和生成规则
sub.o: sub.cpp sub.h$(CC) $(CFLAGS) -c sub.cpp
# 定义清理规则
clean:rm -f main *.o

3. cmake

Makefile的脚本还是过于复杂了点儿。所以可以使用cmake创建Makefile脚本,并且cmake还支持生成其他的脚本,比如sln(vs的构建脚本)。

不过详细的就不再写了,cmake还是边试边学把。

文首问题答案

(1)GCC/Make/CMake是什么关系?

              cmake           make       gcc
CMakelist.txt -----> Makefile ----> Cmds ---> Binary

gcc直接构建二进制可执行文件。

make指导gcc如何构建(相当于脚本,但是相比shell脚本做了一些优化)

cmake让Makefile脚本更容易写。

(2)一个C++程序编译为一个可执行文件,需要哪些过程?

使用gcc编译C/C++程序时,主要的编译流程如下,包含预处理编译汇编链接等四个步骤。 以输入C语言程序源码文件b.c为例,直接调用命令gcc b.c,将会完整执行以下流程,并生成对应的可执行二进制文件a.out。 注意,这里gcc的默认输出就是固定的a.out。 在GCC工具链中,汇编由工具as完成,链接则由工具ld完成。

      -E          -S          -c          
b.c ------> b.i ------> b.s ------> b.o ------> a.outgcc         gcc         as          ld

(3)#include语句所引入的库,如何才能找到对应的完整源代码文件?

  • 对于系统库,不需要指定,gcc会自动在特定目录中寻找。
  • 对于外部库,需要指定
    • 头文件目录,使用-I指定
    • 源文件目录,使用-L指定
    • 可执行库的名字,使用-l指定

(4)静态库和动态库有什么区别?

从使用上来说,静态库在链接时会被完整地拷贝到目标文件中,动态库则只会添加一个入口信息。

静态链接生成的文件比较大,一旦库发生变化需要重新编译,但优点是不需要执行环境中有该静态库。

动态库则相反。

(5)为什么要用make,直接使用shell脚本不能编译吗?

make相比sh脚本,有以下好处:

  • 提升编译性能。
    • 按需编译。如果重新编译项目,不会从头完整编译,而是只编译有变化的部分。
    • 多线程。
  • 自动解决依赖关系
    • 多文件中的头文件依赖类似于一个拓扑排序问题,make可以自动找到最优路径。

相关文章:

GCC/Make/CMake 工具链

阅读前可以思考的问题&#xff1a;&#xff08;答案在文章的最后面,小白可以略过&#xff09; GCC/Make/CMake是什么关系&#xff1f; 一个C程序编译为一个可执行文件&#xff0c;需要哪些过程&#xff1f; #include语句所引入的库&#xff0c;如何才能找到对应的完整源代码文…...

GO抽象工厂模式

既然工厂模式每个产品都需要实现对应的工厂类去生成相关实例&#xff0c;提取产品的共性&#xff0c;提高代码的内聚性&#xff0c; 就是抽象工厂模式要干的。在抽象工厂中&#xff0c;依然是不同产品对应不同的工厂类&#xff0c;但可以尽可能将具有相同共性的产品类别合在一起…...

Linux 磁盘/分区/修复 命令

目录 1. lsblk&#xff08;list block devices&#xff09; 2. fdisk&#xff08;fragment disk&#xff09; 3. gdisk 4. mkfs&#xff08;make filesystem&#xff09; 5. df&#xff08;display file-system disk space usage&#xff09; 6. du 7. fsck&#xff08;file-sy…...

php一句话木马免杀

php一句话木马免杀 针对于php一句话木马做免杀&#xff1a; 利用php动态函数的特性&#xff0c;将危险函数拆分成字符&#xff0c;最终使用字符串拼接的方式&#xff0c;然后重新拼接&#xff0c;后加括号执行代码&#xff0c;并且可以使用花指令进行包装&#xff0c;如无限i…...

深度学习人体跌倒检测 -yolo 机器视觉 opencv python 计算机竞赛

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; **基于深度学习的人体跌倒检测算法研究与实现 ** 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f947;学长这里给一个题目综合评分(每项满…...

轻松整理文件夹,将视频文件全部归类到另一个文件夹!

如果你需要整理文件夹中的文件&#xff0c;将同一类别的文件归纳到一起&#xff0c;可以更加方便地管理和查找。现在&#xff0c;我们有一个简单而实用的方法&#xff0c;可以将文件夹中的所有视频文件归类到另一个文件夹中&#xff0c;让你的文件管理更加有序和高效。 首先&am…...

存储服务器特征是什么

存储服务器和普通服务器是有差别的&#xff0c;配置方式不同&#xff0c;因为存储服务器是为特定目标设计的&#xff0c;通常存储服务器是独立的单元&#xff0c;大多数时候是被设计成4U机架式&#xff0c;存储服务器一般是单机运作的&#xff0c;不与其他服务器连接。今天小编…...

Conditional GAN

Text-to-Image 对于根据文字生成图像的问题&#xff0c;传统的做法就是训练一个NN&#xff0c;然后输入一段文字&#xff0c;输出对应一个图片&#xff0c;输出图片与目标图片越接近越好。存在的问题就是&#xff0c;比如火车对应的图片有很多张&#xff0c;如果用传统的NN来训…...

OOM问题排查+Jvm优化

OOM问题排查&#xff1a; 1、top命令&#xff1a;查看cpu和内存的使用情况。 2、jstat命令&#xff1a;查看YGC和FGC情况&#xff0c;一般都是老年代不够用。导致OOM 3、jmap命令&#xff1a; 查看哪个类的实例过多,以每个类占用多少了内存。4、jstack 查看线程与线程之间的阻…...

链表:C++实现

引言&#xff1a; 链表是一种常见的数据结构&#xff0c;它由一系列节点组成&#xff0c;每个节点包含一个数据元素和一个指向下一个节点的指针。相比于数组&#xff0c;链表具有动态性和灵活性&#xff0c;可以高效地进行插入和删除操作&#xff0c;但是查找操作的时间复杂度较…...

使用JMX监控ZooKeeper和Kafka

JVM 默认会通过 JMX 的方式暴露基础指标,很多中间件也会通过 JMX 的方式暴露业务指标,比如 Kafka、Zookeeper、ActiveMQ、Cassandra、Spark、Tomcat、Flink 等等。掌握了 JMX 监控方式,就掌握了一批程序的监控方式。本节介绍 JMX-Exporter 的使用,利用 JMX-Exporter 把 JMX…...

蓝桥等考C++组别七级008

第一部分:选择题 1、C++ L7 (15分) 在判断是否满足循环条件之前,至少执行循环体语句一次的是哪种循环结构?( ) for循环while循环do-while循环以上都不是正确答案:C 2、C++ L7 (15分) 执行以下程序,会输出几个“*”?( ) for(int i = 0; i <= 10; i++){…...

sam和mobilesam导出预处理的onnx

一、前言 sam或者mobilesam的python推理都存在一些前处理,如下所示: sam.to(device=cuda) predictor = SamPredictor(sam) predictor.set_image(image) image_embedding = predictor.get_image_embedding().cpu().numpy() checkpoint = "./weights/mobile_sam.pt"…...

开源与闭源:大模型发展的双重走向

目录 前言开源和闭源的优劣势比较开源的优势闭源的优势 开源和闭源对大模型技术发展的影响对技术发展的影响对数据共享的影响对业务拓展的影响 开源与闭源的商业模式比较开源的商业模式闭源的商业模式 处在大模型洪流中&#xff0c;向何处去&#xff1f;结语 前言 随着人工智能…...

c# 逆变 / 协变

个人理解&#xff1a; 1. 逆变in向上兼容类 2. 协变out向下兼容类 在面向对象编程中&#xff0c;尤其是使用泛型时&#xff0c;in和out关键字用于限制类型参数的协变性和逆变性。 in关键字&#xff08;逆变&#xff09;&#xff1a; in关键字用于标记泛型类型参数的逆变性。…...

electron使用better-sqlite3打包失败(electron打包有进程没有界面)

remove *\chrome_100_percent.pak: Access is denied. 解决&#xff1a; 管理员权限执行&#xff1a;taskkill /IM 你的进程名.exe /F&#xff0c;再次执行build electron使用better-sqlite3打包后有进程没有界面 原因是代码及依赖包安装有误&#xff0c;模块丢失。主要分享的…...

2.6文件服务器

2.6文件服务器 一、Ftp 介绍 文件传输协议&#xff08;File Transfer Protocol&#xff0c;FTP&#xff09;&#xff0c;基于该协议FTP客户端与服务端可以实现共享文 件、上传文件、下载文件。 FTP 基于TCP协议生成一个虚拟的连接&#xff0c;主要用于控制FTP连接信息&#x…...

【C++ 学习 ㊴】- 详解 C++ 的 I/O 流

目录 一、C 的 I/O 流 二、C 的标准 I/O 流 三、C 的文件 I/O 流 一、C 的 I/O 流 C 语言有一套完成数据读写&#xff08;I/O&#xff09;的解决方案&#xff1a; 使用 scanf()、gets() 等函数从键盘读取数据&#xff0c;使用 printf()、puts() 等函数向屏幕输出数据&#…...

js算法面试题(附答案)

js算法面试题十道 两数之和 题目&#xff1a;给定一个整数数组 nums 和一个目标值 target&#xff0c;请你在该数组中找出和为目标值的那两个整数&#xff0c;并返回他们的数组下标。 function twoSum(nums, target) {const map new Map();for (let i 0; i < nums.leng…...

2023 年戴森设计大奖得主是谁?给大楼降温、争取救援机会

2023 年戴森设计大奖得主是谁&#xff1f;给大楼降温、争取救援机会 ​编辑拉风的极客2023/11/22 摘要 当今社会除了持续不断对科技创新保持注目&#xff0c;还有很多年轻发明家为了实际场景的难题提供解决方案。 11 月 15 日&#xff0c;2023 年戴森设计大奖国际大奖名单正…...

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...

css实现圆环展示百分比,根据值动态展示所占比例

代码如下 <view class""><view class"circle-chart"><view v-if"!!num" class"pie-item" :style"{background: conic-gradient(var(--one-color) 0%,#E9E6F1 ${num}%),}"></view><view v-else …...

《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》

引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...

linux arm系统烧录

1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 &#xff08;忘了有没有这步了 估计有&#xff09; 刷机程序 和 镜像 就不提供了。要刷的时…...

HTML前端开发:JavaScript 常用事件详解

作为前端开发的核心&#xff0c;JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例&#xff1a; 1. onclick - 点击事件 当元素被单击时触发&#xff08;左键点击&#xff09; button.onclick function() {alert("按钮被点击了&#xff01;&…...

【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)

升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点&#xff0c;但无自动故障转移能力&#xff0c;Master宕机后需人工切换&#xff0c;期间消息可能无法读取。Slave仅存储数据&#xff0c;无法主动升级为Master响应请求&#xff…...

OpenLayers 分屏对比(地图联动)

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能&#xff0c;和卷帘图层不一样的是&#xff0c;分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...

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

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

ArcGIS Pro制作水平横向图例+多级标注

今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作&#xff1a;ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等&#xff08;ArcGIS出图图例8大技巧&#xff09;&#xff0c;那这次我们看看ArcGIS Pro如何更加快捷的操作。…...

项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)

Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败&#xff0c;具体原因是客户端发送了密码认证请求&#xff0c;但Redis服务器未设置密码 1.为Redis设置密码&#xff08;匹配客户端配置&#xff09; 步骤&#xff1a; 1&#xff09;.修…...