Makefile基础使用和实战详解
Makefile基础使用和实战详解
- 一、基础
- 1.1、简单的Makefile
- 1.2、多文件编译
- 1.3、伪对象.PHONY
- 二、变量
- 2.1、自动变量
- 2.2、特殊变量
- 2.3、变量的类别
- 2.4、变量及其值的来源
- 2.5、变量引用的高级功能
- 2.6、override 指令
- 三、模式
- 四、函数
- 4.1、addprefix 函数
- 4.2、filter函数
- 4.3、filter-out函数
- 4.4、patsubst 函数
- 4.5、strip函数
- 4.6、wildcard 函数
- 总结
一、基础
Makefile 其实只是一个指示 make 程序如何为我们工作的命令文件,我们说 Makefile 其实是在说 make。而对于项目来说,Makefile 是指软件项目的编译环境。
Makefile 的好坏对于项目开发有些什么影响呢?设计得好的 Makefile,当我们重新编译时,只需编译那些上次编译成功后修改过的文件,也就是说编译的是一个 delta,而不是整个项目。反之,如果一个不好的 Makefile 环境,可能对于每一次的编译先要 clean,然后再重新编译整个项目。两种情况的差异是显然的,后者将耗费开发人员大量的时间用于编译,也就意味着低效率。
最为重要的是掌握二个概念,一个是目标(target),另一个就是依赖(dependency)。目标就是指要干什么,或说运行 make 后生成什么,而依赖是告诉 make 如何去做以实现目标。在 Makefile 中,目标和依赖是通过规则(rule)来表达的。我们最为熟悉的是采用make 来进行软件产品的代码编译,但它可以被用来做很多很多的事情。驾驭 Makefile,最为重要的是要学会采用目标和依赖关系来思考所需解决的问题。
Makefile三要素:
Makefile工作原理:
1.1、简单的Makefile
(1)示例一:
all:@echo "hello all"
test:@echo "hello test"
执行结果:
$ make
hello all
$ make test
hello test
$ make all
hello all
需要注意的是echo 前面必须只有 TAB(即键盘TAB键),且至少有一个 TAB,而不能用空格代替。对于很多初学者,最为容易犯的就是这种“低级”错误。 这种错误往往在对 Makefile 进行调试时,还不大容易发现,因为,从文本编辑器中看来,TAB 与空格有时没有太明显的区别。
Makefile 中的 all 就是目标,目标放在‘:’的前面,其名字可以是由字母和下划线‘_’组成 。echo “hello all”就是生成目标的命令,这些命令可以是任何你可以在你的环境中运行的命令以及 make 所定义的函数等等。all 目标在这里就是代表希望在终端上打印出“hello all”,有时目标会是一个比较抽象的概念。all 目标的定义,其实是定义了如何生成 all 目标,这我们也称之为规则。
(2)示例二:在示例一的基础上调换目标位置。
test:@echo "hello test"
all:@echo "hello all"
执行结果:
$ make
hello test
$ make test
hello test
$ make all
hello all
可知:
一个 Makefile 中可以定义多个目标。
调用 make 命令时,我们得告诉它我们的目标是什么,即要它干什么。当没有指明具体的目标是什么时,那么 make 以 Makefile 文件中定义的第一个目标作为这次运行的目标。这“第一个”目标也称之为默认目标(和是不是all没有关系)。
当 make 得到目标后,先找到定义目标的规则,然后运行规则中的命令来达到构建目标的目的。现在所示例的 Makefile 中,每一个规则中都只有一条命令,而实际的 Makefile,每一个规则可以包含很多条命令。
注意,命令前加了一个‘@’, 这一符号告诉 make,在运行时不要将这一行命令显示出来。
(3)示例三:
all:test@echo "hello all"
test:@echo "hello test"
执行结果:
$ make
hello test
hello all
$ make test
hello test
$ make all
hello test
hello all
会发现当运行 make 时,test 目标也被构建了。这里需要引入 Makefile 中依赖关系的概念,all 目标后面的 test 是告诉 make,all 目标依赖 test 目标,这一依赖目标在 Makefile 中又被称之为先决条件。出现这种目标依赖关系时,make工具会按从左到右的先后顺序先构建规则中所依赖的每一个目标。如果希望构建 all 目标,那么make 会在构建它之前得先构建 test 目标,这就是为什么我们称之为先决条件的原因。
1.2、多文件编译
目前有两个源文件,需要编译成一个应该程序:
foo.c
#include <stdio.h>
void foo()
{printf("This is foo() \n");
}
main.c
extern void foo();
int main()
{foo();return 0;
}
它们的依赖关系可总结为如下:
我们就可以根据依赖关系写Makefile了:
all:main.o foo.ogcc -o simple main.o foo.o
main.o:gcc -o main.o -c main.c
foo.o:gcc -o foo.o -c foo.c
clean:rm simple main.o foo.o
增加了一个 clean 目标用于删除所生成的文件,包括目标文件和 simple 可执行程序,这在现实的项目中很是常见。
值得注意的是,如果执行两次make会怎么样?执行效果如下:
$make
gcc -c main.c -o main.o
gcc -c foo.c -o foo.o
gcc -o simple main.o foo.o$make
gcc -o simple main.o foo.o
注意到第二次编译并没有构建目标文件的动作,但有构建simple可执行程序的动作,我们需要了解 make 是如何决定哪些目标(这里是文件)是需要重新编译的。为什么 make会知道我们并没有改变 main.c 和 foo.c 呢?通过文件的时间戳。当 make 在运行一个规则时,我们前面已经提到了目标和先决条件之间的依赖关系,make 在检查一个规则时,采用的方法是:如果先决条件中相关的文件的时间戳大于目标的时间戳,即先决条件中的文件比目标更新,则知道有变化,那么需要运行规则当中的命令重新构建目标。这条规则会运用到所有与我们在 make时指定的目标的依赖树中的每一个规则。比如,对于 simple 项目,其依赖树中包括三个规则,make 会检查所有三个规则当中的目标(文件)与先决条件(文件)之间的时间先后关系,从而来决定是否要重新创建规则中的目标。
那为什么会执行一次gcc -o simple main.o foo.o呢?因为all文件在我们的编译过程中并不生成,即 make 在第二次编译时找不到,所以又重新编译了一遍。如果把all改为simple,那就是我们所期望的结果:
simple:main.o foo.ogcc -o simple main.o foo.o
main.o:gcc -o main.o -c main.c
foo.o:gcc -o foo.o -c foo.c
clean:rm simple main.o foo.o
执行结果:
$make
gcc -c main.c -o main.o
gcc -c foo.c -o foo.o
gcc -o simple main.o foo.o$make
make: `simple` is up to date.
另外,对于make 工具,一个文件是否改动不是看文件大小,而是其时间戳。比如用 touch 命令来改变文件的时间戳就行了,这相当于模拟了对文件进行了一次编辑,而不需真正对其进行编辑。make 发现了 foo.c 需要重新被编译,而这,最终也导致了 simple 需要重新被编译。
$ls -l foo.c
-rw-rw-r-- 1 fly fly 65 1月 30 14:15 foo.c$touch foo.c
$ls -l foo.c
-rw-rw-r-- 1 fly fly 65 1月 30 15:42 foo.c$make
gcc -c foo.c -o foo.o
gcc -o simple main.o foo.o
1.3、伪对象.PHONY
在前面的示例项目中,现在假设在程序所在的目录下面有一个 clean 文件,这个文件也可以通过 touch 命令来创建。创建以后,运行 make clean 命令:
$ls -l clean
ls: cannot access clean: No such file or directory$touch clean
$ls -l clean
-rw-rw-r-- 1 fly fly 65 1月 30 16:42 clean$make clean
make: `clean' is up to date.
会发现 make 总是提示 clean 文件是最新的,而不是按我们所期望的那样进行文件删除操作。这是因为 make 将 clean 当作文件,且在当前目录找到了这个文件,加上 clean 目标没有任何先决条件,所以,当我们要求 make 为我们构建 clean 目标时,它就会认为 clean 是最新的。
对于这种情况,在现实中也难免存在所定义的目标与所存在的文件是同名的,采用 Makefile如何处理这种情况呢?Makefile 中的假目标(phony target)可以解决这个问题。假目标可以采用.PHONY 关键字来定义,需要注意的是其必须是大写字母。
.PHONY:clean
simple:main.o foo.ogcc -o simple main.o foo.o
main.o:gcc -o main.o -c main.c
foo.o:gcc -o foo.o -c foo.c
clean:rm simple main.o foo.o
将 clean 变为假目标后的Makefile,更改后运用 make clean 命令的结果:
$make clean
rm simple main.o foo.o
采用.PHONY 关键字声明一个目标后,make 并不会将其当作一个文件来处理,而只是当作一个概念上的目标。对于假目标,我们可以想像的是由于并不与文件关联,所以每一次 make 这个假目标时,其所在的规则中的命令都会被执行。
二、变量
在 Makefile 中通过使用变量来使得它更简洁、更具可维护性。
.PHONY:clean
CC=gcc
RM=rm
EXE=simple
OBJS=main.o foo.o$(EXE):$(OBJS)$(CC) -o $(EXE) $(OBJS)
main.o:$(CC) -o main.o -c main.c
foo.o:$(CC) -o foo.o -c foo.c
clean:$(RM) $(EXE) $(OBJS)
一个变量的定义很简单,就是一个名字(变量名)后面跟上一个等号,然后在等号的后面放这个变量所期望的值。对于变量的引用,则需要采用(变量名)或者(变量名)或者(变量名)或者{变量名}这种模式。采用变量的话,当我们需要更改编译器时,只需更改变量赋值的地方,非常方便,如果不采用变量,那我们得更改每一个使用编译器的地方,很是麻烦。显然,变量的引入增加了 Makefile 的可维护性。既然定义了一个 CC 变量,当然也可以将-o 或是-c 命令参数也定义成为一个变量,因为如果我们更改了一个编译器,那么很有可能其使用参数也得跟着改变。
2.1、自动变量
有时候目标和先决条件的名字会在规则的命令中多次出,如果改变了目标或是依赖的名,那得在命令中全部跟着改。这就需要用到 Makefile 中的自动变量,它们包括:
- $ @ 用于表示一个规则中的目标。当我们的一个规则中有多个目标时,$@所指的是其中任何造成命令被运行的目标。
- $^则表示的是规则中的所有先决条件。
- $<表示的是规则中的第一个先决条件。
除了上面的两个自动变量,在 Makefile 中还有其它的动变量。如下是测试上面三个自动变量的值的 Makefile:
.PHONY:all
all:first second thrid@echo "\$$@=$@"@echo "$$^=$^"@echo "$$<=$<"
first second thrid:
运行结果:
$ make$@=all
$^=first second thrid
$<=first
需要注意的是,在 Makefile 中‘’具有特殊的意思,因此,如果想采用echo输出‘’具有特殊的意思,因此,如果想采用 echo 输出‘’具有特殊的意思,因此,如果想采用echo输出‘’,则必需用两个连着的‘’。还有就是,’。还有就是,’。还有就是,@对于 Shell 也有特殊的意思,需要在“$$@”之前再加一个脱字符‘\’。
我们就可以将simple项目的Makefile改为如下:
.PHONY:clean
CC=gcc
RM=rm
EXE=simple
OBJS=main.o foo.o$(EXE):$(OBJS)$(CC) -o $@ $^
main.o:main.c$(CC) -o $@ -c $^
foo.o:foo.c$(CC) -o $@ -c $^
clean:$(RM) $(EXE) $(OBJS)
自动变量在对它们还不熟悉时,看起来可能有那么一点吃力,但熟悉了你就会觉得其简捷(洁),那时也会觉得它们好用。
2.2、特殊变量
在 Makefile 中有几个特殊变量,可能经常需要用到。
(1)第一个就是 MAKE 变量,它表示的是make 命令名是什么。当我们需要在 Makefile 中调用另一个 Makefile 时需要用到这个变量,采用这种方式,有利于写一个容易移植的 Makefile。
.PHONY: all
all:
@echo "MAKE = $(MAKE)"
执行:
$make
MAKE = make
(2)第二个特殊变量则是 MAKECMDGOALS,它表示的是当前用户所输入的 make 目标是什么。
.PHONY: all clean
all clean:
@echo "\$$@ = $@"
@echo "MAKECMDGOALS = $(MAKECMDGOALS)"
执行:
$make
$@ = all
MAKECMDGOALS =$make all
$@ = all
MAKECMDGOALS = all$make clean
$@ = clean
MAKECMDGOALS = clean$make all clean
$@ = all
MAKECMDGOALS = all clean
$@ = clean
MAKECMDGOALS = all clean
MAKECMDGOALS 指的是用户输入的目标,当只运行 make 命令时,虽然根据 Makefile 的语法,第一个目标将成为缺省目标,即 all 目标,但 MAKECMDGOALS 仍然是空,而不是 all,这一点需要注意。
2.3、变量的类别
(1)只用一个“=”符号定义的变量,称之为递归扩展变量(recursively expanded variable)。
.PHONY:all
foo=$(foo2)
foo2=$(foo3)
foo3= FLY.
all:@echo $(foo)
执行:
$ make
FLY.
递归扩展变量的引用是递归的。这种递归性有利也有弊。利的方面是最后foo将会被展开。但也存在弊,那就是我们不能对foo变量再采用赋值操作。如下的方式会出现一个死循环:
foo=$(foo) -O
(2)除了递归扩展变量还有一种变量称之为简单扩展变量(simply expanded variables),是用“:=”操作符来定义的。对于这种变量,make 只对其进行一次扫描和替换。
.PHONY:all
x=fly
y=$(x) FLY
x=later
xx:=fly
yy:=$(xx) FLY
xx:=fly
all:@echo "x=$(y),xx=$(yy)"
执行:
$ make
x=later FLY,xx=fly FLY
可以明显的看出 make 是如何处理递归扩展变量和简单扩展变量的。
(3)Makefile中还存在一种条件赋值符“?=”。
.PHONY:all
foo=x
foo?=y
bar?=y
all:@echo "foo=$(foo),bar=$(bar)"
执行:
$ make
foo=x,bar=y
条件赋值的意思是当变量以前没有定义时,就定义它并且将左边的值赋值给它,如果已经定义了那么就不再改变其值。条件赋值类似于提供了给变量赋缺省值的功能。
对于前面所说的变量类别,是针对一个赋值操作而言的。
2.4、变量及其值的来源
在 Makefile 中我们可以对变量进行定义。此外,还有其它的地方让 Makefile 获得变量及其值。比如:
(1)对于前面所说到的自动变量,其值是在每一个规则中根据规则的上下文自动获得变量值的。
(2)可以在运行 make 时,在 make 命令行上定义一个或多个变量。在 make 命令行中定义的变量及其值同样在 Makefile 中是可见的。其实,我们可以通过在 make 命令行中定义变量的方式从而覆盖 Makefile 中所定义的变量的值。
$ make bar=x
(3)变量还可以来自于 Shell 环境,例如采用 Shell 中的 export 命令定义了一个变量后再执行Makefile。
$ export bar=x
$ make
foo = x, bar = x
(4)Makefile 还可以采用“+=”操作符对变量进行赋值的方法。
.PHONY: all
objects = main.o foo.o bar.o utils.o
objects += another.o
all:@echo $(objects)
等价于
.PHONY:all
objects = main.o foo.o bar.o utils.o
objects:= $(objects) another.o
all:@echo $(objects)
2.5、变量引用的高级功能
在赋值的同时完成后缀替换操作。
.PHONY:all
foo= a.o b.o c.o
bar:=$(foo:.o=.c)
all:@echo "bar=$(bar)"
执行结果:
$ make
bar=a.c b.c c.c
bar 变量中的文件名从.o 后缀都变成了.c。这种功能也可以采用 patsubst 函数来实现,与函数相比,这种功能更加的简洁。当然,patsubst 功能更强,而不只是用于替换后缀。
2.6、override 指令
前面了解到,我们可以采用在 make 命令行上定义变量的方式,使得 Makefile 中定义的变量覆盖掉,从而不起作用。可能,在设计 Makefile 时,我们并不希望用户将我们在 Makefile 中定义的某个变量覆盖掉,那就得用 override 指令了。
.PHONY:all
override foo= a.o b.o c.o
bar:=$(foo:.o=.c)
all:@echo "bar=$(bar)"
执行:
$ make foo="bb.o cc.o"
bar=a.c b.c c.c
三、模式
对于前面的 Makefile,其中存在多个规则用于构建目标文件。比如,main.o 和 foo.o 都是采用不同的规则进行描述的。如果对于每一个目标文件都得写一个不同的规则来描述,太繁了!对于一个大型项目,就更不用说了。Makefile 中的模式就是用来解决这种烦恼的。我们可以把之前的simple项目的Makefile改成这样:
.PHONY:clean
CC=gcc
RM=rm
EXE=simple
OBJS=main.o foo.o$(EXE):$(OBJS)$(CC) -o $@ $^
%.o:%.c$(CC) -o $@ -c $^
clean:$(RM) $(EXE) $(OBJS)
与 前一版本的 Makefile 相比,最为直观的改变就是从二条构建目标文件的规则变成了一条。模式类似于 Windows 操作系统中所使用的通配符,当然是用“ % ”而不是“ * ”。采用了模式以后,不论有多少个源文件要编译,我们都是应用同一个模式规则的,很显然,这大大的简化了我们的工作。使用了模式规则以后,同样可以用这个 Makefile 来编译或是清除 simple 项目,这与前一版本在功能上是完全一样的。
四、函数
函数是 Makefile 中的另一个利器,现在看一看采用函数如何来简化 simple 项目的 Makefile。对于 simple 项目的 Makefile,尽管使用了模式规则,但还有一件比较恼人的事,得在这个Makefile 中指明每一个需要被编译的源程序。对于一个源程序文件比较多的项目,如果每增加或是删除一个文件都得更新 Makefile,其工作量也不可小视!
下面是采用了 wildcard 和 patsubst 两个函数后 simple 项目的 Makefile。需要注意的是函数的语法形式很特别,不过只要记住其形式就行了。
.PHONY:clean
CC=gcc
RM=rm
EXE=simple
SRCS=$(wildcard *.c)# 把.c替换为.o
OBJS=$(patsubst %.c,%.o,$(SRCS))$(EXE):$(OBJS)$(CC) -o $@ $^
%.o:%.c$(CC) -o $@ -c $^
clean:$(RM) $(EXE) $(OBJS)
现在,来模拟增加一个源文件的情形,看一看如果增加一个文件,在 Makefile 不做任何更改的情况下其是否仍能正常的工作。增加文件的方式仍然是采用 touch 命令,通过 touch 命令生成一个内容是空的 foo2.c 源文件,然后再运行 make 和 make clean。
$ touch foo2.c
$ make
gcc -o foo.o -c foo.c
gcc -o main.o -c main.c
gcc -o foo2.o -c foo2.c
gcc -o simple foo.o main.o foo2.o$ make clean
rm simple foo.o main.o foo2.o
从结果来看函数真的起作用了!这功能很酷!更多内容可以看一看《GUN make》以了解 Makefile 中到底有些什么函数,这样的话,当在碰到具体的问题时就会想到它们。
4.1、addprefix 函数
addprefix 函数是用来在给字符串中的每个子串前加上一个前缀,其形式是:
$(addprefix prefix, names...)
示例:
.PHONY:all
no_dir=foo.c foo2.c main.o
no_dir:=$(addprefix objs/,$(no_dir))
all:@echo $(no_dir)
执行:
$ make
objs/foo.c objs/foo2.c objs/main.o
4.2、filter函数
filter 函数用于从一个字符串中,根据模式得到满足模式的字符串,其形式是:
$(filter pattern..., text)
示例:
.PHONY:all
srcs=foo.c foo2.c main.s main.h
srcs:=$(filter %.c %.s,$(srcs))
all:@echo $(srcs)
执行:
$ make
foo.c foo2.c main.s
从结果来看,经过 filter 函数的调用以后,source变量中只存在.c 文件和.s 文件了,而.h 文件则被过滤掉了。
4.3、filter-out函数
filter-out 函数用于从一个字符串中根据模式滤除一部分字符串,其形式是:
$(filter-out pattern..., text)
示例:
.PHONY:all
srcs=foo.c foo2.c main1.c main2.c main.h
srcs:=$(filter-out main%.c,$(srcs))
all:@echo $(srcs)
执行:
$ make
foo.c foo2.c main.h
从结果来看,filter-out 函数将 main1.c 和 main2.c从 src变量中给滤除了。filter 与 filter-out 是互补的。
4.4、patsubst 函数
patsubst 函数是用来进行字符串替换的,其形式是:
$(patsubst pattern, replacement, text)
示例:
.PHONY:all
srcs=foo.c foo2.c main1.c main2.c
objs:=$(patsubst %.c,%.o,$(srcs))
all:@echo $(objs)
执行:
$ make
foo.o foo2.o main1.o main2.o
可以看出采用patsubst 函数进行字符串替换时,我们希望将所有的.c 文件都替换成.o 文件。当然,由于patsubst 函数可以使用模式,所以其也可以用于替换前缀等等,功能更加的强。
4.5、strip函数
strip 函数用于去除变量中的多余的空格,其形式是:
$(strip string)
示例:
.PHONY:all
srcs=foo.c foo2.c main1.c main2.c
objs:=$(strip $(srcs))
all:@echo $(srcs)@echo $(objs)
执行:
$ make
foo.c foo2.c main1.c main2.c
foo.c foo2.c main1.c main2.c
从结果来看,strip 函数将 foo.c 和 bar.c 之间的多余的空格给去除了。
4.6、wildcard 函数
wildcard 是通配符函数,通过它可以得到我们所需的文件,这个函数如果我们在 Windows 或是Linux 命令行中的“*”。其形式是:
$(wildcard pattern)
示例:
.PHONY:all
srcs=$(wildcard *.c)
all:@echo $(srcs)
执行:
$ make
foo.c main.c foo2.c
从当前 Makefile 所在的目录下通过 wildcard 函数得到所有的 C 程序源文件。
总结
- Makefile的一个规则是由目标(targets)、先决条件(prerequisites)以及命令(commands)所组成的。需要指出的是,目标和先决条件之间表达的就是依赖关系(dependency),这种依赖关系指明在构建目标之前,必须保证先决条件先满足(或构建)。而先决条件可以是其它的目标,当先决条件是目标时,其必须先被构建出来。还有就是一个规则中目标可以有多个,当存在多个目标,且这一规则是Makefile 中的第一个规则时,如果运行make 命令不带任何目标,那么规则中的第一个目标将被视为是缺省目标。
- 掌握如果在头脑中勾画出我们想让 make 做的事的“依赖树”是编写 Makefile 最为重要和关键的一步。
- 编译时出现“undefined reference to … ”时,有两个原因:第一种是源码没有被编译进去;第二种是对应库没有被引用。
相关文章:

Makefile基础使用和实战详解
Makefile基础使用和实战详解一、基础1.1、简单的Makefile1.2、多文件编译1.3、伪对象.PHONY二、变量2.1、自动变量2.2、特殊变量2.3、变量的类别2.4、变量及其值的来源2.5、变量引用的高级功能2.6、override 指令三、模式四、函数4.1、addprefix 函数4.2、filter函数4.3、filte…...
Go基础-变量
文章目录1 Go中的变量2 声明一个变量3 声明变量并初始化4 变量推断5 声明多个变量5.1 多个变量相同类型5.2 多个变量不同类型6 简短声明7 Go语言变量不能把一种类型赋值给其他类型1 Go中的变量 Go中变量指定了某存储单元的名称,该存储单元会存储特定类型的值&#…...

【算法】三道算法题目单词拆分,填充每个节点的下一个右侧节点指针以及组合总和
算法第一道算法题:单词拆分java解答参考第二道算法题:填充每个节点的下一个右侧节点指针java 解答参考第三道算法题:组合总和java解答参考大家好,我是小冷。 今天还是继续学习算法技术知识吧 第一道算法题:单词拆分 …...
【算法】刷题路线(系统+全面)
本系列基于当前各大公司对大公司的考察情况,给大家规划一条可行的算法刷题路线,大概会规划 200 道自认为有用的题,并且争取让初学者,能够刷起来更加丝滑,而且每个阶段都会进行相对应的说明。 当然,无论是面…...

Fiddler的报文分析
目录 1.Statistics请求性能数据 2.检测器(Inspectors) 3.自定义响应(AutoResponder) 1.Statistics请求性能数据 报文分析: Request Count: 1 请求数,该session总共发的请求数 Bytes …...

Spring 中,有两个 id 相同的 bean,会报错吗
我们知道,spring容器里面的bean默认是单例的,所以id是唯一的。但是需要注意,同一类型的bean可以有不同的id,比如有id1->bean,也可以有id2->bean。 下面再来详细回答一下文章的问题。 首先,在同一个…...
Mysql数据库的时间(4)一查询数据库时间注意点
一.select根据时间段查询 1.原始的sql根据时间段查询 select * from stu where time between "1998-09-01" and "1999-09-01"; //查询从1998-09-01到1999-09-01时间段的数据 等同于select * from stu where time >"1998-09-01" and time &l…...

一起学 pixijs(2):修改图形属性
大家好,我是前端西瓜哥。 我们做动画、游戏、编辑器,需要根据用户的交互等操作,去实时地改变图形的属性,比如位置,颜色等信息。今天西瓜哥带大家来看看在 pixijs 怎么修改图形的属性。 因为 pixijs 的底层维护了图形…...
LeetCode 121. 买卖股票的最佳时机
原题链接 难度:easy\color{Green}{easy}easy 题目描述 给定一个数组 pricespricesprices ,它的第 iii 个元素 prices[i]prices[i]prices[i] 表示一支给定股票第 iii 天的价格。 你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同…...

shell脚本内调用另外一个shell脚本的几种方法
有时会在一个shell脚本(如test_call_other_shell.sh)中调用另外一个shell脚本(如parameter_usage.sh),这里总结几种可行的方法,这些方法在linux上和windows上(通过Git Bash)均适用: 1.通过source: 运行在相同的进程,在test_…...
Linux C++ 多进程下write写日志问题思考
文章目录多个进程(父子)同时通过write像日志文件中写,是否会出现数据混乱情况?需要满足以下条件: 1、通过open打开文件,子进程都是复制父进程的文件描述符去操作这个文件,不会造成文件混乱&…...

MySQL的四种事务隔离级别
目录一、事务的基本要素(ACID)1、原子性(Atomicity):2、一致性(Consistency):3、隔离性(Isolation):4、持久性(Durability)…...

方法区和元空间有什么关系?
一.什么是方法区? 方法区属于是 JVM 运行时数据区域的一块逻辑区域,是各个线程共享的内存区域。 《Java 虚拟机规范》只是规定了有方法区这么个概念和它的作用,方法区到底要如何实现那就是虚拟机自己要考虑的事情了。也就是说,在…...

2023VNCTF的两道(暂时)
from http://v2ish1yan.top/2023/02/19/%E6%AF%94%E8%B5%9Bwp/2023vnctf/ 比赛的时候在回学校的路上,所以没有打,听说质量挺高,赛后做一下 象棋王子 一个普通的js游戏,玩过关了就给flag,所以flag肯定在前端源码里 这…...

JDK版本区别
1. 泛型 ArrayList listnew ArrayList()------>ArrayList<Integer>listnew ArrayList<Integer>(); 2 自动装箱/拆箱 nt ilist.get(0).parseInt();-------->int ilist.get(0);原始类型与对应的包装类不用显式转换 3 for-each i0;i<a.length;i------------&…...

Android 基础知识4-2.8 TableLayout(表格布局)详解
一、TableLayout的概述 表格布局是以行数和列数来确定位置进行排列。就像一间教室,确定好行数与列数就能让同学有序入座。 注意:我们需要先添加<TableRow容器,每添加一个就会多一行,然后再往<TableRow容器中添加其它组件。…...

SQL代码编码原则和规范
目录1、先了解MySQL的执行过程2、数据库常见规范3、所有表必须使用Innodb存储引擎4、每个Innodb表必须有个主键5、数据库和表的字符集统一使用UTF86、查询SQL尽量不要使用select *,而是具体字段7、避免在where子句中使用 or 来连接条件8、尽量使用数值替代字符串类型…...
【博客627】gobgp服务无损变更:graceful restart特性
gobgp服务无损变更:graceful restart特性 场景 当我们的bgp网关在对外宣告bgp路由的时候,如果我们网关有新的特性要发布,那么此时如果把网关停止再启动新版本,此时bgp路由会有短暂撤回再播出的过程,会有网络抖动 期待…...

一起学 pixijs(1):常见图形的绘制
大家好,我是前端西瓜哥。 pixijs 是一个强大的 Web Canvas 2D 库,以其强大性能而著称。其底层使用了 WebGL 实现了硬件加速,当然如果不支持的话,也能回退为 Canvas。 本文使用的 pixijs 版本为 7.1.2。 Application Applicati…...

2023年PMP考试教材有哪些?(含pmp资料)
PMP考试教材是《PMBOK指南》,但这次的考试因为大纲的更新,而需要另外的敏捷书籍来备考。且官方发了通知,3、5月还是第六版指南,8月及8月之后,使用第七版教材。 新版考纲将专注于以下三个新领域: 人 – 强调与有效领导项…...
C++:std::is_convertible
C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...

黑马Mybatis
Mybatis 表现层:页面展示 业务层:逻辑处理 持久层:持久数据化保存 在这里插入图片描述 Mybatis快速入门 
安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件
在选煤厂、化工厂、钢铁厂等过程生产型企业,其生产设备的运行效率和非计划停机对工业制造效益有较大影响。 随着企业自动化和智能化建设的推进,需提前预防假检、错检、漏检,推动智慧生产运维系统数据的流动和现场赋能应用。同时,…...

PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建
制造业采购供应链管理是企业运营的核心环节,供应链协同管理在供应链上下游企业之间建立紧密的合作关系,通过信息共享、资源整合、业务协同等方式,实现供应链的全面管理和优化,提高供应链的效率和透明度,降低供应链的成…...
条件运算符
C中的三目运算符(也称条件运算符,英文:ternary operator)是一种简洁的条件选择语句,语法如下: 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true,则整个表达式的结果为“表达式1”…...

ios苹果系统,js 滑动屏幕、锚定无效
现象:window.addEventListener监听touch无效,划不动屏幕,但是代码逻辑都有执行到。 scrollIntoView也无效。 原因:这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作,从而会影响…...
Java多线程实现之Thread类深度解析
Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...

使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...
jmeter聚合报告中参数详解
sample、average、min、max、90%line、95%line,99%line、Error错误率、吞吐量Thoughput、KB/sec每秒传输的数据量 sample(样本数) 表示测试中发送的请求数量,即测试执行了多少次请求。 单位,以个或者次数表示。 示例:…...

HubSpot推出与ChatGPT的深度集成引发兴奋与担忧
上周三,HubSpot宣布已构建与ChatGPT的深度集成,这一消息在HubSpot用户和营销技术观察者中引发了极大的兴奋,但同时也存在一些关于数据安全的担忧。 许多网络声音声称,这对SaaS应用程序和人工智能而言是一场范式转变。 但向任何技…...