GNU make 中文手册 第一二章 概述与介绍
一、第一章:概述
准备知识
在开始我们关于 make 的讨论之前,首先需要明确一些基本概念:
编译:把高级语言书写的代码,转换为机器可识别的机器指令。编译高级语言后生成的指令虽然可被机器识别,但是还不能被执行。编译时,编译器检查高级语言的语法、函数与变量的声明是否正确。只有所有的语法正确、相关变量定义正确,编译器就可以编译出中间目标文件。通常,一个高级语言的源文件都可对应一个目标文件。目标文件在 Linux 中默认后缀为 “.o”(如 “foo.c” 的目标文件为 “foo.o” )。为了和规则的目标文件相区别。本文将编译高级语言后生成的目标文件成为.o 文件。
链接:将多 .o 文件,或者 .o 文件和库文件链接成为可被操作系统执行的可执行程序(Linux 环境下,可执行文件的格式为 “ELF” 格式)。链接器不检查函数所在的源文件,只检查所有.o 文件中的定义的符号。将 .o 文件中使用的函数和其它 .o 或者库文件中的相关符号进行合并,对所有文件中的符号进行重新安排(重定位),并链接系统相关文件(程序启动文件等)最终生成可执行程序。链接过程使用 GNU 的 “ld” 工具。
静态库:又称为文档文件(Archive File)。它是多个 .o 文件的集合。Linux 中静态库文件的后缀为 “.a”。静态库中的各个成员(.o 文件)没有特殊的存在格式,仅仅是一个 .o 文件的集合。使用 “ar” 工具维护和管理静态库。
共享库:也是多个 .o 文件的集合,但是这些 .o 文件时有编译器按照一种特殊的方式生成(Linux 中,共享库文件格式通常为 “ELF” 格式。共享库已经具备了可执行条件)。模块中各个成员的地址(变量引用和函数调用)都是相对地址。使用此共享库的程序在运行时,共享库被动态加载到内存并和主程序在内存中进行连接。多个可执行程序可共享库文件的代码段(多个程序可以共享的使用库中的某一个模块,共享代码,不共享数据)。另外共享库的成员对象可被执行(由 libdl.so 提供支持)。
参考 info ld 了解更加详细的关于 ld 的说明和用法。
二、第二章 GNU make 介绍
2 GNU make 介绍
make 在执行时,需要一个命名为 Makefile 的文件。这个文件告诉 make 以何种方式编译源代码和链接程序。典型地,可执行文件可由一些 .o 文件按照一定的顺序生成或者更新。如果在你的工程中已经存在一个或者多个正确的 Makefile。当对工程中的若干源文件修改以后,需要根据修改来更新可执行文件或者库文件,正如前面提到的你只需要在 shell 下执行 “make”。make 会自动根据修改情况完成源文件的对应.o 文件的更新、库文件的更新、最终的可执行程序的更新。
make 通过比较对应文件(规则的目标和依赖,)的最后修改时间,来决定哪些文件需要更新、哪些文件不需要更新。对需要更新的文件 make 就执行数据库中所记录的相应命令(在 make 读取 Makefile 以后会建立一个编译过程的描述数据库。此数据库中记录了所有各个文件之间的相互关系,以及它们的关系描述)来重建它,对于不需要重建的文件 make 什么也不做。
而且可以通过 make 的命令行选项来指定需要重新编译的文件。可参考 9.2 指定终极目标 一节。
2.1 Makefile 简介
在执行 make 之前,需要一个命名为 Makefile 的特殊文件(本文的后续将使用 Makefile 作为这个特殊文件的文件名)来告诉 make 需要做什么(完成什么任务),该怎么做。通常,make 工具主要被用来进行工程编译和程序链接。
本节将分析一个简单的 Makefile,它对一个包含 8 个 C 的源代码和三个头文件的工程进行编译和链接。这个 Makefile 提供给了 make 必要的信息,make 程序根据 Makefile 中的规则描述执行相关的命令来完成指定的任务(如:编译、链接和清除编译过程文件等)。复杂的 Makefile 我们将会在本文后续进行讨论。
当使用 make 工具进行编译时,工程中以下几种文件在执行 make 时将会被编译(重新编译):
1. 所有的源文件没有被编译过,则对各个 C 源文件进行编译并进行链接,生成最后的可执行程序;
2. 每一个在上次执行 make 之后修改过的 C 源代码文件,在本次执行 make 时将会被重新编译;
3. 头文件在上一次执行 make 之后被修改。则所有包含此头文件的 C 源文件在本次执行 make 时将会被重新编译。
后两种情况是 make 只将修改过的 C 源文件重新编译生成 .o 文件,对于没有修改的文件不进行任何工作。重新编译过程中,任何一个源文件的修改,将产生新的对应的.o 文件,新的 .o 文件将和以前的已经存在、此次没有重新编译的.o 文件,重新连接生成最后的可执行程序。
首先让我们先来看一些 Makefile 相关的基本知识。
2.2 Makefile 规则介绍
一个简单的 Makefile 描述规则组成:
TARGET... : PREREQUISITES... COMMAND ... ...
target:规则的目标。通常是最后需要生成的文件名或者为了实现这个目的而必需的中间过程文件名。可以是 .o文件、也可以是最后的可执行程序的文件名等。另外,目标也可以是一个 make 执行的动作的名称,如目标 “clean”,我们称这样的目标是 “伪目标”。参考4.6 Makefile伪目标 一节。
prerequisites:规则的依赖。生成规则目标所需要的文件名列表。通常一个目标依赖于一个或者多个文件。
command:规则的命令行。是规则所要执行的动作(任意的 shell 命令或者是可在 shell 下执行的程序)。它限定了 make 执行这条规则时所需要的动作。
一个规则可以有多个命令行,每一条命令占一行。注意:每一个命令行必须以 [Tab] 字符开始,[Tab] 字符告诉 make 此行是一个命令行。make 按照命令完成相应的动作。这也是书写 Makefile 中容易产生,而且比较隐蔽的错误。
命令就是在任何一个目标的依赖文件发生变化后重建目标的动作描述。一个目标可以没有依赖而只有动作(指定的命令)。比如 Makefile 中的目标 “clean”,此目标没有依赖,只有命令。它所定义的命令用来删除 make 过程产生的中间文件(进行清理工作)。
在 Makefile 中 “规则” 就是描述,在什么情况下、如何重建规则的目标文件,通常规则中包括了目标的依赖关系(目标的依赖文件)和重建目标的命令。make 执行重建目标的命令,来创建或者重建规则的目标(此目标文件也可以是触发这个规则的上一个规则中的依赖文件)。规则包含了文件之间的依赖关系
,和更新此规则目标
所需要的命令
。
一个 Makefile 文件中通常还包含了除规则以外的很多东西(后续我们会一步一步的展开)。一个最简单的 Makefile 可能只包含规则。规则在有些 Makefile 中可能看起来非常复杂,但是无论规则的书写是多么的复杂,它都符合规则的基本格式。
make 程序根据规则的依赖关系,决定是否执行规则所定义的命令的过程我们称之为执行规则。
2.3 简单的示例
本小节开始我们在第一小节中提到的例子。此例子由 3 个头文件和 8 个 C 文件组成。我们将书写一个简单的 Makefile,来描述如何创建最终的可执行文件 “edit”,此可执行文件依赖于8个C源文件和3个头文件。Makefile文件的内容如下:
edit : main.o kbd.o command.o display.o \insert.o search.o files.o utils.o cc -o edit main.o kbd.o command.o display.o \insert.o search.o files.o utils.o
main.o : main.c defs.h cc -c main.c
kbd.o : kbd.c defs.h command.h cc -c kbd.c
command.o : command.c defs.h command.h cc -c command.c
display.o : display.c defs.h buffer.h cc -c display.c
insert.o : insert.c defs.h buffer.h cc -c insert.c
search.o : search.c defs.h buffer.h cc -c search.c
files.o : files.c defs.h buffer.h command.h cc -c files.c
utils.o : utils.c defs.h cc -c utils.c
clean : rm edit main.o kbd.o command.o display.o \insert.o search.o files.o utils.o
首先书写时,可以将一个较长行使用反斜线(\)来分解为多行,这样可以使我们的 Makefile 书写清晰、容易阅读理解。但需要注意:反斜线之后不能有空格(这也是大家最容易犯的错误,错误比较隐蔽)。我们推荐将一个长行分解为使用反斜线连接得多个行的方式。在完成了这个 Maekfile 以后;需要创建可执行程序 “edit”,所要做的就是在包含此 Makefile 的目录(当然也在代码所在的目录)下输入命令 “make”。删除已经此目录下之前使用 “make” 生成的文件(包括那些中间过程的 .o 文件),也只需要输入命令 “make clean” 就可以了。
在这个 Makefile 中,我们的目标(target)就是可执行文件 “edit” 和那些 .o文件(main.o,kbd.o….);依赖(prerequisites)就是冒号后面的那些 .c 文件和 .h 文件。所有的 .o 文件既是依赖(相对于可执行程序edit)又是目标(相对于.c 和 .h 文件)。命令包括 “cc –c maic.c”、“cc –c kbd.c”……
当规则的目标是一个文件,在它的任何一个依赖文件被修改以后,在执行 “make” 时,这个目标文件将会被重新编译或者重新连接。当然,此目标的任何一个依赖文件如果有必要则首先会被重新编译。在这个例子中,“edit” 的依赖为 8 个 .o 文件;而 “main.o” 的依赖文件为 “main.c” 和 “defs.h” 。当 “main.c” 或者 “defs.h” 被修改以后,再次执行 “make”,“main.o” 就会被更新(其它的 .o 文件不会被更新),同时 “main.o” 的更新将会导致 “edit” 被更新。
在描述依赖关系行之下,通常就是规则的命令行(存在一些些规则没有命令行),命令行定义了规则的动作(如何根据依赖文件,来更新目标文件)。命令行必需以 [Tab] 键开始,以和 Makefile 其他行区别。就是说所有的命令行必需以 [Tab] 字符开始,但并不是所有的以 [Tab] 键出现行都是命令行。但 make 程序会把出现在第一条规则之后的所有以 [Tab] 字符开始的行都作为命令行来处理。(记住:make 程序本身并不关心命令是如何工作的,对目标文件的更新需要你在规则描述中提供正确的命令。“make” 程序所做的就是当目标程序需要更新时,执行规则所定义的命令)。
目标 “clean” 不是一个文件,它仅仅代表执行一个动作的标识。正常情况下,不需要执行这个规则所定义的动作,因此目标 “clean” 没有出现在其它任何规则的依赖列表中。因此在执行 make 时,它所指定的动作不会被执行。除非在执行 make 时明确地指定它。而且目标 “clean” 没有任何依赖文件,它只有一个目的,就是通过这个目标名来执行它所定义的命令。Makefile 中把那些没有任何依赖,只有执行动作的目标,称为 “伪目标”(phony targets)。参考 4.6 Makefile 伪目标 一节。需要执行 “clean” 目标所定义的命令,可在shell下输入:make clean。
2.4 make 如何工作
默认的情况下,make 执行的是 Makefile 中的第一个规则,此规则的第一个目标称之为“最终目的”或者“终极目标”(就是一个 Makefile 最终需要更新或者创建的目标,参考9.2 指定终极目标 一节)。
上例的 Makefile,目标 “edit” 在 Makefile 中是第一个目标,因此它就是 make 的 “终极目标”。当修改了任何 C 源文件或者头文件后,执行 make 将会重建终极目标“edit”。
当在 shell 提示符下输入“make” 命令以后。make 读取当前目录下的 Makefile 文件,并将 Makefile 文件中的第一个目标作为其执行的“终极目标”,开始处理第一个规则(终极目标所在的规则)。在我们的例子中,第一个规则就是目标 “edit” 所在的规则。规则描述了 “edit” 的依赖关系,并定义了链接 .o 文件生成目标 “edit” 的命令; make 在执行这个规则所定义的命令之前,首先处理目标 “edit” 的所有的依赖文件(例子中的那些 .o 文件)的更新规则(以这些 .o 文件为目标的规则)。对这些 .o 文件为目标的规则处理有下列三种情况:
1. 目标.o 文件不存在,使用其描述规则创建它;
2. 目标 .o 文件存在,目标 .o 文件所依赖的.c 源文件、.h 文件中的任何一个比目标 .o 文件“更新”(在上一次 make 之后被修改)。则根据规则重新编译生成它;
3. 目标 .o 文件存在,目标.o 文件比它的任何一个依赖文件(的.c 源文件、.h 文件)“更新”(它的依赖文件在上一次 make 之后没有被修改),则什么也不做。
这些 .o 文件所在的规则之所以会被执行,是因为这些 .o 文件出现在“终极目标”的依赖列表中。在 Makefile 中一个规则的目标如果不是 “终极目标” 所依赖的(或者 “终极目标” 的依赖文件所依赖的),那么这个规则将不会被执行,除非明确指定执行这个规则(可以通过 make 的命令行指定重建目标,那么这个目标所在的规则就会被执行,例如 “make clean”)。在编译或者重新编译生成一个 .o 文件时,make 同样会去寻找它的依赖文件的重建规则(是这样一个规则:这个依赖文件在规则中作为目标出现),在这里就是 .c 和.h 文件的重建规则。在上例的 Makefile 中没有哪个规则的目标是 .c 或者 .h 文件,所以没有重建 .c 和 .h 文件的规则。不过 C 语言源程序文件可以使用工具 Bison 或者 Yacc 来生成(具体用法可参考相应的手册)。
完成了对 .o 文件的创建(第一次编译)或者更新之后,make 程序将处理终极目标 “edit” 所在的规则,分为以下三种情况:
1. 目标文件 “edit” 不存在,则执行规则,以创建目标 “edit”。
2. 目标文件 “edit” 存在,其依赖文件中有一个或者多个文件比它“更新”,则根据规则重新链接生成 “edit”。
3. 目标文件 “edit” 存在,它比它的任何一个依赖文件都“更新”,则什么也不做。
上例中,如果更改了源文件 “insert.c” 后执行 make,“insert.o” 将被更新,之后终极目标 “edit” 将会被重生成;如果我们修改了头文件 “command.h” 之后运行 “make”,那么 “kbd.o”、“command.o” 和 “files.o” 将会被重新编译,之后同样终极目标 “edit” 也将被重新生成。
以上我们通过一个简单的例子,介绍了 Makefile 中目标和依赖的关系。我们简单总结一下:
对于一个 Makefile 文件,“make” 首先解析终极目标所在的规则(上节例子中的第一个规则),根据其依赖文件(例子中第一个规则的 8 个.o 文件)依次(按照依赖文件列表从左到右的顺序)寻找创建这些依赖文件的规则。
首先为第一个依赖文件(main.o)寻找创建规则,如果第一个依赖文件依赖于其它文件(main.c、defs.h),则同样为这个依赖文件寻找创建规则(创建 main.c 和 defs.h 的规则,通常源文件和头文件已经存在,也不存在重建它们的规则)……,直到为所有的依赖文件找到合适的创建规则。之后 make 从最后一个规则(上例目标为 main.o 的规则)回退开始执行,最终完成终极目标的第一个依赖文件的创建和更新。
之后对第二个、第三个、第四个……终极目标的依赖文件执行同样的过程(上例的的顺序是“main.o”、“kbd.o”、“command.o”……)
创建或者更新每一个规则的依赖文件的过程,都是这样的一个过程(类似于 c 语言中的递归过程
)。对于任意一个规则执行的过程,都是按照依赖文件列表顺序,对于规则中的每一个依赖文件,使用同样方式(按照同样的过程)去重建它,在完成对所有依赖文件的重建之后,最后一步才是重建此规则的目标。
更新(或者创建)终极目标的过程中,如果任何一个规则执行出现错误, make 就立即报错并退出。整个过程 make 只是负责执行规则,而对具体规则所描述的依赖关系的正确性、规则所定义的命令的正确性不做任何判断。就是说,一个规则的依赖关系是否正确、描述重建目标的规则命令行是否正确,make 不做任何错误检查。
因此,需要正确的编译一个工程。需要在提供给 make 程序的 Makefile 中来保证其依赖关系的正确性、和执行命令的正确性。
2.5 指定变量
同样是上边的例子,我们来看一下终极目标 “edit” 所在的规则:
edit : main.o kbd.o command.o display.o \ insert.o search.o files.o utils.o cc -o edit main.o kbd.o command.o display.o \ insert.o search.o files.o utils.o
在这个规则中,.o 文件列表出现了两次;第一次:作为目标 “edit” 的依赖文件列表出现,第二次:规则命令行中作为 “cc” 的参数列表。这样做所带来的问题是:如果我们需要为目标 “edit” 增加一个依赖文件,我们就需要在两个地方添加(依赖文件列表和规则的命令中)。添加时可能在 “edit” 的依赖列表中加入了、但却忘记了给命令行中添加,或者相反。这就给后期的维护和修改带来了很多不方便,添加或修改时出现遗漏。
为了避免这个问题,在实际工作中大家都比较认同的方法是,使用一个变量 “objects”、“OBJECTS”、“objs”、“OBJS”、“obj” 或者 “OBJ” 来作为所有的 .o 文件的列表的替代。在使用到这些文件列表的地方,使用此变量来代替。在上例的 Makefile 中我们可以添加这样一行:
objects = main.o kbd.o command.o display.o \ insert.o search.o files.o utils.o
“objects” 作为一个变量,它代表所有的 .o 文件的列表。在定义了此变量后,我们
就可以在需要使用这些 .o 文件列表的地方使用 $(objects)
来表示它,而不需要罗列
所有的 .o 文件列表(变量可参考 第六章 使用变量)。因此上例的规则就可以这样写:
objects = main.o kbd.o command.o display.o \insert.o search.o files.o utils.o edit : $(objects)cc -o edit $(objects)main.o : main.c defs.h cc -c main.c
kbd.o : kbd.c defs.h command.h cc -c kbd.c
command.o : command.c defs.h command.h cc -c command.c
display.o : display.c defs.h buffer.h cc -c display.c
insert.o : insert.c defs.h buffer.h cc -c insert.c
search.o : search.c defs.h buffer.h cc -c search.c
files.o : files.c defs.h buffer.h command.h cc -c files.c
utils.o : utils.c defs.h cc -c utils.c
clean : rm edit $(objects)
当我们需要为终极目标 “edit” 增加或者去掉一个 .o 依赖文件时,只需要改变 “objects”的定义(加入或者去掉若干个 .o 文件)。这样做不但减少书写的工作量,而且可以减少修改而产生错误的可能。
2.6 自动推导规则
在使用 make 编译 .c 源文件时,编译 .c 源文件规则的命令可以不用明确给出。这是因为 make 本身存在一个默认的规则,能够自动完成对 .c 文件的编译并生成对应的 .o 文件。它执行命令 “cc -c” 来编译 .c 源文件。
在 Makefile 中我们只需要给出需要重建的目标文件名(一个 .o 文件),make 会自动为这个 .o 文件寻找合适的依赖文件(对应的 .c 文件。对应是指:文件名除后缀外,其余都相同的两个文件),而且使用正确的命令来重建这个目标文件。对于上边的例子,此默认规则就使用命令 “cc -c main.c -o main.o” 来创建文件 “main.o”。对一个目标文件是 “N.o”,依赖文件是 “N.c” 的规则,完全可以省略其规则的命令行,而由 make 自身决定使用默认命令。此默认规则称为 make 的隐含规则(关于隐含规则可参考 第十章 使用隐含规则)
这样,在书写 Makefile 时,我们就可以省略掉描述 .c 文件和 .o 依赖关系的规则,而只需要给出那些特定的规则描述( .o 目标所需要的 .h 文件)。因此上边的例子就可以以更加简单的方式书写,我们同样使用变量“ objects”。Makefile 内容如下:
objects = main.o kbd.o command.o display.o \insert.o search.o files.o utils.o edit : $(objects)cc -o edit $(objects)main.o : defs.h
kbd.o : defs.h command.h
command.o : defs.h command.h
display.o : defs.h buffer.h
insert.o : defs.h buffer.h
search.o : defs.h buffer.h
files.o : defs.h buffer.h command.h
utils.o : defs.h .PHONY : clean
clean : rm edit $(objects)
这种格式的 Makefile 更接近于我们实际应用。(关于目标 “clean” 的详细说明我们在后边。参考4.6 Makefile 伪目标 一节 和 5.4 命令的错误 一节)
make 的隐含规则在实际工程的 make 中会经常使用,它使得编译过程变得方便。几乎在所有的 Makefile 中都用到了 make 的隐含规则,make 的隐含规则是非常重要的一个概念。后续我们会在第十章会有专门的讨论。
2.7 另类风格的 makefile
上一节中我们提到过,Makefile 中,所有的 .o 目标文件都可以使用隐含规则由 make 自动重建,我们可以根据这一点书写更加简洁的 Makefile。而且在这个 Makefile 中,我们是根据依赖而不是目标对规则进行分组。形成另外一种风格的 Makefile。实现如下:
#sample Makefile
objects = main.o kbd.o command.o display.o \ insert.o search.o files.o utils.o
edit : $(objects) cc -o edit $(objects)
$(objects) : defs.h
kbd.o command.o files.o : command.h
display.o insert.o search.o files.o : buffer.h
本例中,我们以三个头文件为出发点,对依赖于每一个头文件的目标进行合并。书写出一个多目标的规则,规则中多个目标同时依赖于对应的头文件,而且同一个文件可能同时存在多个规则中。例子中头文件 “defs.h” 作为所有 .o 文件的依赖文件。其它两个头文件作为规则所有目标文件(多个 .o 文件)的依赖文件。
这种风格的 Makefile 并不值得我们借鉴。问题在于:同时把多个目标文件的依赖放在同一个规则中进行描述(一个规则中含有多个目标文件),这样导致规则定义不明了,比较混乱。建议大家不要在 Makefile 中采用这种方式了书写。否则后期维护将会是一件非常痛苦的事情。
书写规则建议的方式是:单目标,多依赖
。就是说尽量要做到一个规则中只存在一个目标文件,可有多个依赖文件。尽量避免使用多目标,单依赖的方式。 这样书写的好处是后期维护会非常方便,而且这样做会使 Makefile 会更清晰、明了。
2.8 清除工作目录过程文件
规则除了完成源代码编译之外,也可以完成其它任务。例如:前边提到的,为了实现清除当前目录中编译过程中产生的临时文件(edit 和那些 .o 文件)的规则:
clean : rm edit $(objects)
在实际应用时,我们把这个规则写成如下稍微复杂一些的样子。以防止出现始料未及的情况。
.PHONY : clean
clean : -rm edit $(objects)
这两个实现有两点不同: 1. 通过 “.PHONY” 特殊目标,将 “clean” 目标声明为伪目标。避免当磁盘上存在一个名为 “clean” 文件时,目标 “clean” 所在规则的命令无法执行(参考 4.6 Makefile 伪目标 一节)。2. 在命令行之前使用 “-”,意思是忽略命令 “rm” 的执行错误(参考 5.4 命令的错误 一节)。
这样的一个目标在 Makefile 中,不能将其作为终极目标(Makefile 的第一个目标)。因为我们的初衷并不是,当你在命令行上输入 make 以后执行删除动作。而是要创建或者更新程序。在我们上边的例子中。就是在输入 make 以后需要对目标 “edit” 进行创建或者重建。
上例中因为目标 “clean” 没有出现在终极目标 “edit” 依赖关系中(终极目标的直接依赖或者间接依赖),所以我们执行 “make” 时,目标 “clean” 所在的规则将不会被处理。当需要执行此规则,要在 make 的命令行选项中明确指定这个目标(执行 “make clean”)。关于 make 的执行可参考 9.2 指定终极目标 一节。
相关文章:

GNU make 中文手册 第一二章 概述与介绍
一、第一章:概述 准备知识 在开始我们关于 make 的讨论之前,首先需要明确一些基本概念: 编译:把高级语言书写的代码,转换为机器可识别的机器指令。编译高级语言后生成的指令虽然可被机器识别,但是还不能…...
真的了解HashMap、HashSet吗?做一道测试题试试!
本人博客《HashMap、HashSet底层原理分析》,可以了解hashmap的底层源码实现 测试代码 HashSet底层实际就是一个Hashmap。猜猜下面源码每一个打印结果。 注:user对象重写的hashcode方法,保证name和age一样的情况下hashcode是一样的ÿ…...

树莓派下安装OpenEuler
openEuler作为华为开源的应用于嵌入式设备的操作系统,正在受到越来越多的关注。树莓派是一个很好的应用场景,这篇文章就介绍下如何在树莓派上安装openEuler。 ps:openEuler要求树莓派的版本是4B 1.下载openEuler镜像 镜像网址࿱…...

VSCode Remote-SSH配置免密登录踩坑
VSCode Remote-SSH配置免密登录踩坑1. 参考2. 基本流程2.1 机器A(Windows客户端)2.2 机器B(Linux服务器)2.3 机器A(Windows客户端)的VSCode设置3. 踩坑总结相关教程很多,但要么冗余,…...
【Python语言基础】——Python NumPy 数组拆分
Python语言基础——Python NumPy 数组拆分 文章目录 Python语言基础——Python NumPy 数组拆分一、Python NumPy 数组拆分一、Python NumPy 数组拆分 拆分 NumPy 数组 拆分是连接的反向操作。 连接(Joining)是将多个数组合并为一个,拆分(Spliting)将一个数组拆分为多个。…...

虹科资讯| 虹科AR荣获汽车后市场“20佳”维修工具评委会提名奖!
2022 虹科荣获20佳维修工具 评委会提名奖 特大喜讯,在2月16日《汽车维修与保养》杂志主办的第十八届汽车后市场“20佳”评选活动中,虹科的产品“M400智能AR眼镜”凭借在AR领域的专业实力,通过层层筛选,在102款入围产品中脱颖而出…...

Mysql架构与内部模块
Mysql架构与内部模块 演示环境: MySQL 5.7 存储引擎:InnoDB 一、一条查询SQL是如何执行的? 程序或者工具要操作数据库,第一步跟数据库建立连接。 1、通信协议 首先,MySQL 必须要运行一个服务,监听默认的…...

从技术上来看,互联网技术开始衍生和蜕变出更多的新技术
很多人在看待产业互联网的问题上,一味地割裂它与互联网之间的联系,甚至还有人将产业互联网看成是对于传统互联网的颠覆。如果仅仅只是以这样的眼光来看待产业互联网,那么,他们势必是无法完整把握产业互联网的本质内涵和原始奥义的…...
最长不含重复字符的子字符串
今天处理一道算法题目,《剑指Offer》第48题,力扣中等题。 这道题也是面试的高频题! 题目描述 请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。 示例1: 输入: "abcabcbb" …...

git中git push origin master推送远程操作失败,报错解决方案
报错图片如下所示: 解决方案: 使用下面代码进行本地与远程仓库的链接: git remote add origin http://xxxxx///xxx(https://gitee.com/peach-fog/shopping-cart-car-warehouse.git)链接完成之后就会输出:fatal: remote origin already exists. 链接完成之后就需要使用git br…...

服务器部署流程与经验记录
服务器部署流程1.项目部署1.1 重置实例密码1.2 配置安全组规则1.3 远程连接服务器1.4 安装所需软件1.5 安装Tomcat1.6 配置宝塔安全组1.7 导入数据库和项目2. 域名注册3. 网站备案1.项目部署 1.1 重置实例密码 1.2 配置安全组规则 1.3 远程连接服务器 使用VNC远程连接&#…...
超火的情感视频短视频账号,赚钱的路子有多野?
目录 一、情感类视频内容模式 1、线上情感导师型 2、用动画传达人生哲理 3、网抑云式的野生“文案馆” 4、星座式情感账号 二、情感号变现方式 1、首先是可以接广告 2、收徒做情感号培训,知识付费 3、导流粉丝到公众号 4、通过卖号来变现 6、导流微信变现…...

Linux系列 linux 常用命令(笔记)
作者简介:一名云计算网络运维人员、每天分享网络与运维的技术与干货。 座右铭:低头赶路,敬事如仪 个人主页:网络豆的主页 目录 前言 一.linux 常用命令(目录文和件基本操作) 1.命令的分类…...

Cosmos 基础教程(二)-- Run a Node, API, and CLI
有很多不同的方法来运行Cosmos区块链的节点。您将探索如何使用simapp 进行此操作。 1、编译simapp Cosmos SDK存储库包含一个名为 simapp 的文件夹。在这个文件夹中,您可以找到运行Cosmos SDK模拟版本的代码,这样您就可以在不实际与链交互的情况下测试…...
C# 读写xml文件总结 [详细]
C# 读写xml文件总结C#写入xml文件1、XmlDocument2、DataSet对象里的值来生成XML文件3、利用XmlSerializer来将类的属性值转换为XML文件的元素值。示例:写入xml1、创建xml文档2 、增加节点3 、修改节点:4 、删除节点c#读取xml文件C#写入xml文件 1、XmlDo…...

【Java基础】IO流
IO流 最后一定要关闭流,防止资源泄露 字节流 一次读取1字节,8比特 FileInputStream import org.junit.jupiter.api.Test;import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException;public class CopyBytes {pub…...
Boolean,Array,Object数据类型(回顾)
Boolean数据类型范围Boolean(value)Object数据类型特点键值对数组特点类数组特点 Boolean数据类型范围 true,false 链接 Boolean(value) 定义:其他类型转布尔类型 六大假值:false,undefined,null,NaN,0…...
Python常见的数据类型
♥️作者:小刘在C站 ♥️个人主页:小刘主页 ♥️每天分享云计算网络运维课堂笔记,努力不一定有收获,但一定会有收获加油!一起努力,共赴美好人生! ♥️夕阳下,是最美的绽放࿰…...
欠缺知识点罗列
UML五种关系的特点 依赖,关联,组合,聚合,泛化。认识UML类关系——依赖、关联、聚合、组合、泛化 - 腾讯云开发者社区-腾讯云 数据结构- 生成树的定义。 每周学点大数据 | No.17最小生成树 - 腾讯云开发者社区-腾讯云 有向图。 …...

基于springboot+vue的校园社团管理系统(前后端分离)
博主主页:猫头鹰源码 博主简介:Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容:毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目介绍…...
线程同步:确保多线程程序的安全与高效!
全文目录: 开篇语前序前言第一部分:线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分:synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分ÿ…...
测试markdown--肇兴
day1: 1、去程:7:04 --11:32高铁 高铁右转上售票大厅2楼,穿过候车厅下一楼,上大巴车 ¥10/人 **2、到达:**12点多到达寨子,买门票,美团/抖音:¥78人 3、中饭&a…...
Rust 异步编程
Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...

Java面试专项一-准备篇
一、企业简历筛选规则 一般企业的简历筛选流程:首先由HR先筛选一部分简历后,在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如:Boss直聘(招聘方平台) 直接按照条件进行筛选 例如:…...
CSS设置元素的宽度根据其内容自动调整
width: fit-content 是 CSS 中的一个属性值,用于设置元素的宽度根据其内容自动调整,确保宽度刚好容纳内容而不会超出。 效果对比 默认情况(width: auto): 块级元素(如 <div>)会占满父容器…...

C/C++ 中附加包含目录、附加库目录与附加依赖项详解
在 C/C 编程的编译和链接过程中,附加包含目录、附加库目录和附加依赖项是三个至关重要的设置,它们相互配合,确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中,这些概念容易让人混淆,但深入理解它们的作用和联…...
面试高频问题
文章目录 🚀 消息队列核心技术揭秘:从入门到秒杀面试官1️⃣ Kafka为何能"吞云吐雾"?性能背后的秘密1.1 顺序写入与零拷贝:性能的双引擎1.2 分区并行:数据的"八车道高速公路"1.3 页缓存与批量处理…...

一些实用的chrome扩展0x01
简介 浏览器扩展程序有助于自动化任务、查找隐藏的漏洞、隐藏自身痕迹。以下列出了一些必备扩展程序,无论是测试应用程序、搜寻漏洞还是收集情报,它们都能提升工作流程。 FoxyProxy 代理管理工具,此扩展简化了使用代理(如 Burp…...
二维FDTD算法仿真
二维FDTD算法仿真,并带完全匹配层,输入波形为高斯波、平面波 FDTD_二维/FDTD.zip , 6075 FDTD_二维/FDTD_31.m , 1029 FDTD_二维/FDTD_32.m , 2806 FDTD_二维/FDTD_33.m , 3782 FDTD_二维/FDTD_34.m , 4182 FDTD_二维/FDTD_35.m , 4793...
深入浅出WebGL:在浏览器中解锁3D世界的魔法钥匙
WebGL:在浏览器中解锁3D世界的魔法钥匙 引言:网页的边界正在消失 在数字化浪潮的推动下,网页早已不再是静态信息的展示窗口。如今,我们可以在浏览器中体验逼真的3D游戏、交互式数据可视化、虚拟实验室,甚至沉浸式的V…...