Makefile和cmake学习

2023-05-16

一、Makefile

一、什么是Makefile

1、Makefile 可以简单的认为是一个工程文件的编译规则,描述了整个工程的编译和链接等规则。其中包含了那些文件需要编译,那些文件不需要编译,那些文件需要先编译,那些文件需要后编译,那些文件需要重建等等。编译整个工程需要涉及到的,在 Makefile 中都可以进行描述。换句话说,Makefile 可以使得我们的项目工程的编译变得自动化,不需要每次都手动输入一堆源文件和参数。其目的就是调高开发效率。

2、环境准备
Ubuntu、make
查看make版本命令:make -version
在这里插入图片描述
3、makefile文件
makefile文件由目标、依赖和命令三部分构造成,其中,makefile中还提供了变量、函数等。我们在学习的过程中要特别注意变量、函数、多目录和库生成等部分。

二、Makefile工作原理

1、工作原理图
在这里插入图片描述
我们回想一下在Linux下编译程序时是不是需要指定编译器和编译参数,最麻烦的还是要将编译的源文件指出来,如果要编译的源文件不多影响倒不大,但在实际工程中的使用中,要编译的源文件就很多了,在编译时候指明就太不现实了,而且我们在修改了部分文件后,还需要重新开始编译,某些已经编译好的文件及时没有改动也需要重新编译,浪费了大量的开发时间。所以我们需要通过makefile文件解决,makefile文件有一个好处就是再次编译时只会编译发生改变的源文件,这就非常符合我们的开发需求了。

2、makefile文件时如何找到发生改变的源文件的
makefile在查找源文件的时候类似于递归,当查到第一个文件时,如果这个文件需要依赖其他源文件,就会先去判断源文件是否发生改变,这个过程很像个多叉树
在这里插入图片描述
3、如何使用makefile输出“Hello Word”
makefile中提供了echo,这个就类似于printf和cout,是将内容打印到终端上面的,那我们可以试着运行一下下面这段程序

target : all
	@echo "Hello world!" 

make 命令的执行必须到Makefile文件目录下才可以,而且需要特别注意的是,“@echo” 前面的是Tab键,不是空格键哦,我印象中通过vim打开Makefile文件时需要去配置一下vim的属性,将Tab键设置成两个空格。
在这里插入图片描述
我们需要注意的是,make默认编译Makefile的第一个目标,如果不指明生成目标名的话。通过上面简单的文件我们也可以看出来,就是在执行make命令的时候必须指明生成目标名, 如果不指明就默认第一个生成目标。上面的打印就是Makefile三要素中的命令,但是实际使用中,生成目标是需要多条指令的。

三、目标和依赖关系

1、我们首先看下这个makefile文件

cc:test.o
	@echo "Hello World!"
test.o:test.cc
	g++ test.cc -o test

从这个文件我们能看出,cc是最终生成的目标文件,cc的生成需要依赖test,而test的生成又需要依赖test.cc文件。这样,我们在运行make命令的时候,Makefile会生成cc,但因为cc依赖test,那就需要先生产test,而test又依赖test.cc ,所以我们需要首先执行命令去生成目标test,然后才能根据test得到cc。这样我们对于目标、依赖和命令就有比较直观的理解了。

2、假目标
我们在实际书写Makefile不小心会在目录中建立一个与Makefile中同名的文件,这样我们在编译的时候去找这个文件的时候总会提示文件是最新的,这时候我们就需要通过Makefile中的(.PHONY)解决了。

.PHONY cc clean
cc:test.o
	@echo "Hello World!"
test.o:test.cc
	g++ test.cc -o test
clean:
	rm -rf cc main test.o

在这里插入图片描述
2、自动变量

@ 用于表示一个规则中的目标。当我们的一个规则中有多个目标时, @用于表示一个规则中的目标。当我们的一个规则中有多个目标时, @用于表示一个规则中的目标。当我们的一个规则中有多个目标时,@所指的是其中任何造成命令被运行的目标。
● $^则表示的是规则中的所有先择条件。
● $<表示的是规则中的第一个先决条件。

.PHONY: clean
# 代指编译器
CC = gcc
# rm命令
RM = rm
# 生成目标名
EXE = simple
OBJS = main.o foo.o
$(EXE): $(OBJS)
	$(CC) -o $(EXE) $(OBJS)
main.o: main.c
# $@ 代表到的是main.o,因为此处有多个目标,但最终造成下面这条命令执行的是生成test.o这个目标
# $^ 代表的是所有依赖,这里是main.c,如果有多个的话,就是这里的多个
# $< 代表的是所有依赖的第一个
	$(CC) -o $@ $^
foo.o: foo.c
	$(CC) -o $@ -c $^
clean:
	$(RM) $(EXE) $(OBJS)

这里主要记住上面三种自动变量即可,变量还有递归扩展变量和简单扩展变量,上面RM = rm、EXE = simple就是属于递归扩展变量,简单扩展变量RM := rm、EXE := simple,简单变量在make编译时只会进行一次扫描替换。那也就是说我们在第一次编译的时候会将XX替换成mian.o,在后面的再编译过程中,都是以main.o存在的。

3、Makefile模板

# 假目标
.PHONY: clean
# 变量
CC = gcc
RM = rm
EXE = simple
OBJS = main.o foo.o
# 目标名 依赖名
$(EXE): $(OBJS)
	$(CC) -o $@ $^
# 目标是生成所有的.o文件
%.o: %.c
	$(CC) -o $@ -c $^

4、函数
Make 里有一个叫 ‘wildcard’ 的函数,它有一个参数,功能是展开成一列所有符合由其参数描述的文件名,文件间以空格间隔。这行会产生一个所有以 ‘.c’ 结尾的文件的列表,然后存入变量 SOURCES 里。当然你不需要一定要把结果存入一个变量。
另一个有用的函数是 patsubst ( patten substitude, 匹配替换的缩写)函数。它需要3个参数——第一个是一个需要匹配的式样,第二个表示用什么来替换它,第三个是一个需要被处理的由空格分隔的字列。

.PHONY: clean
CC = gcc
RM = rm
EXE = simple
# 将文件夹下所有以.c结尾的文件全部用SRCS代替
SRCS = $(wildcard *.c)
# 将SRCS中的是所有.c文件 替换为.o文件
OBJS = $(patsubst %.c, %.o, $(SRCS))   
$(EXE): $(OBJS)
	$(CC) -o $@ $^
%.o: %.c
	$(CC) -o $@ -c $^
clean:
	$(RM) $(EXE) $(OBJS)

这块内容就介绍这么多。有兴趣的可以参考零声教育Darren老师总结的makefile实战和总结。这里就不过多介绍了。

https://www.yuque.com/docs/share/8495ea21-9fdb-4e7b-aca2-babd86751e39?#m6Cwu

二、CMAKE

一、介绍

我们在实际开发中都是做项目的,而在整个项目周期中,我们有时候更加关注的是如何去组织和管理这些代码。当然是用版本控制工具是可以在一定程度上完成这个需求的。但是版本控制工具更注重协作,在真正源文件的编译上并不能满足实际需求。那这时候有人就会说,那为什么不用makefile呢?

其实,我们是用到Makefile的,cmake是makefile的上层工具,它的目的正是为了产生可移植的makefile,并简化自己动手写makefile时的巨大工作量。那cmake既然是Makefile的上层工具,这也就注定了要比make编写起来要简单很多。

如果你自己动手写过makefile,你会发现,makefile通常依赖于你当前的编译平台,而且编写makefile的工作量比较大,解决依赖关系时也容易出错。

因此,我们在编译时更倾向于使用更加自动化的编译工具cmake。

1、cmake的特点
1,开放源代码,使用类BSD 许可发布。http://cmake.org/HTML/Copyright.html
2,跨平台,并可生成native 编译配置文件,在Linux/Unix 平台,生成 makefile,在苹果平台,可以生成xcode,在 Windows 平台,可以生成 MSVC 的工程文件。
3,能够管理大型项目,KDE4 就是最好的证明。
4,简化编译构建过程和编译过程。Cmake 的工具链非常简单:cmake+make。
5,高效虑,按照KDE 官方说法,CMake 构建KDE4 的 kdelibs 要比使用autotools 来构建KDE3.5.6 的 kdelibs 快40%,主要是因为 Cmake 在工具链中没有libtool。
6,可扩展,可以为cmake 编写特定功能的模块,扩充cmake 功能。

2、cmake打印“Hello World”

# 单个目录实现
# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
# 工程,他不是执行文件名
PROJECT(Ray)
# 手动加入文件 ${变量名}} ,比如${SRC_LIST}
# 其实就是将.c文件换一个名称方便后面使用
SET(SRC_LIST main.c)
SET(SRC_LIST2 main2.c)
# MESSAGE和echo类似 
MESSAGE(STATUS "THIS IS BINARY DIR " ${PROJECT_BINARY_DIR})
MESSAGE(STATUS "THIS IS SOURCE DIR " ${PROJECT_SOURCE_DIR})

# 生产执行文件名
ADD_EXECUTABLE(Ray ${SRC_LIST})
ADD_EXECUTABLE(RayCpp ${SRC_LIST2})

从上面我们可以看到基本语法:
PROJECT:表示项目名,它不是指要编译的这个项目名称,它是指编译CMakeLists.txt这个文件的项目名,或者直接简单来说,这东西可以随意起名,反正不会影响到项目的编译,可以直接理解为没有用处的标识符。除了指明工程名外,还可以指定编译语言,但一般都支持,所以可以忽略不填
SET:这个就是用另一种统一的符号表示源文件。
MESSAGE:他就是个打印输出。
PROJECT_BINARY_DIR:指的是当前工程的路径
ADD_EXECUTABLE:生成可执行程序的名字,这里还需要指明生成程序名所依赖的源文件。
还有一件事,cmake的编译方法是在CMakeLists.txt同级目录下执行cmake … 即可。

3、CMAKE语法
i、变量使用 方式取值( m a k e f i l e 是使用 {}方式取值(makefile是使用 方式取值(makefile是使用() ),但是在IF 控制语句中是直接使用变量名 ,
ii、指令(参数1 参数 2…),参数可以有多个。
a. 参数使用括弧括起,参数之间使用空格或分号分开。
以上面的ADD_EXECUTABLE 指令为例,如果存在另外一个 func.c 源文件,就要写成:
ADD_EXECUTABLE(hello main.c func.c)或者ADD_EXECUTABLE(hello main.c;func.c)
iii、指令是大小写无关的,参数和变量是大小写相关的。但,推荐你全部使用大写指令。
上面的MESSAGE 指令我们已经用到了这条规则:
MESSAGE(STATUS “This is BINARY dir” ${HELLO_BINARY_DIR})
也可以写成:
MESSAGE(STATUS “This is BINARY dir ${HELLO_BINARY_DIR}”)
这里需要特别解释的是作为工程名的HELLO 和生成的可执行文件 hello 是没有任何关系的。还有就是

${HELLO_BINARY_DIR}

指的是当前工程目录,我们在执行可执行程序的时候,会打印出当前工程路径

iv,工程名和执行文件
hello 定义了可执行文件的文件名,你完全可以写成:ADD_EXECUTABLE(t1 main.c)编译后会生成一个t1 可执行文件。

v,关于语法的疑惑
cmake 的语法还是比较灵活而且考虑到各种情况,比如
SET(SRC_LIST main.c)也可以写成 SET(SRC_LIST “main.c”)
是没有区别的,但是假设一个源文件的文件名是 fu nc.c(文件名中间包含了空格)。
这时候就必须使用双引号,如果写成了 SET(SRC_LIST fu nc.c),就会出现错误,提示你找不到fu 文件和nc.c 文件。这种情况,就必须写成:
SET(SRC_LIST “fu nc.c”)

vi、内部构建与外部构建:
上面的例子展示的是“内部构建”,相信看到生成的临时文件比您的代码文件还要多的时候,估计这辈子你都不希望再使用内部构建。
对于cmake,内部编译上面已经演示过了,它生成了一些无法自动删除的中间文件,所以,引出了我们对外部编译的探讨,外部编译的过程如下:
首先,请清除t1 目录中除main.c CmakeLists.txt 之外的所有中间文件,最关键的是CMakeCache.txt。

在 t1 目录中建立build 目录,当然你也可以在任何地方建立build 目录,不一定必须在工程目录中。

进入 build 目录,运行cmake …(注意,…代表父目录,因为父目录存在我们需要的CMakeLists.txt,如果你在其他地方建立了build 目录,需要运行cmake <工程的全路径>),查看一下build 目录,就会发现了生成了编译需要的Makefile 以及其他的中间文件.

运行 make 构建工程,就会在当前目录(build 目录)中获得目标文件 hello。

4、cmake在管理工程中的应用
上面的程序我们可以看到会生成很多的中间文件,这和我们编译的出发点有一点相违背。因为我们只需要关注最终生成的Makefile文件即可,中间那么多文件我们大概率是用不到的,那我们能不能不生成这些东西呢。
首先我们需要做这样一些准备:
本小节的任务是让前面的Hello World 更像一个工程,我们需要作的是:
i,为工程添加一个子目录src,用来放置工程源代码;
ii,添加一个子目录doc,用来放置这个工程的文档hello.txt
iii,在工程目录添加文本文件COPYRIGHT, README;
iv,在工程目录添加一个runhello.sh 脚本,用来调用hello 二进制
v,将构建后的目标文件放入构建目录的bin 子目录;
vi,最终安装这些文件:将hello 二进制与runhello.sh 安装至/usr/bin,将doc
目录的内容以及COPYRIGHT/README 安装到/usr/share/doc/cmake/t2.
在这里插入图片描述
然后我们将src的上一级目录的CMakeLists.txt中添加子目录ADD_SUBDIRECTORY(src)和INSTALL(DIRECTORY doc/ DESTINATION share/doc/cmake/Ray),然后去执行cmake … 和 make命令

# 指得是向当前工程添加存放源文件的子目录
ADD_SUBDIRECTORY(...) 指令
ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL])

5、其它指令的一些使用
INCLUDE_DIRECTORIES:INCLUDE_DIRECTORIES(“${CMAKE_CURRENT_SOURCE_DIR}/dir_1”)

# 代表的是当前执行的CMakeLists.txt所在文件路径
CMAKE_CURRENT_SOURCE_DIR

# 指的是指当前正在处理的列表文件目录
CMAKE_CURRENT_LIST_DIR

# 添加子目录
ADD_SUBDIRECTORY
ADD_SUBDIRECTORY("${CMAKE_CURRENT_SOURCE_DIR}/dir_2")

# 链接库到执行文件上
TARGET_LINK_LIBRARIES
TARGET_LINK_LIBRARIES(darren dir_1 dir_2)

# 找到某个路径下的所有源文件
AUX_SOURCE_DIRECTORY

6、接下来的内容就是关于cmake编译生成动态库和静态库的方法了
生成库

# 设置release版本还是debug版本
# if语句中可以直接使用变量名
if(${CMAKE_BUILD_TYPE} MATCHES "Release")
    MESSAGE(STATUS "Release版本")
    SET(BuildType "Release")
else()
    SET(BuildType "Debug")
    MESSAGE(STATUS "Debug版本")
endif()

#设置lib库目录
# PROJECT_SOURCE_DIR:跟着最近的project文件目录
SET(RELEASE_DIR ${PROJECT_SOURCE_DIR}/release)
# debug和release版本目录不一样
#设置生成的so动态库最后输出的路径
SET(LIBRARY_OUTPUT_PATH ${RELEASE_DIR}/linux/${BuildType})
# -fPIC 动态库必须的选项
ADD_COMPILE_OPTIONS(-fPIC)

# 查找当前目录下的所有源文件
# 并将名称保存到 DIR_LIB_SRCS 变量
AUX_SOURCE_DIRECTORY(. DIR_LIB_SRCS)
# 生成静态库链接库Dir1
#ADD_LIBRARY (Dir1 ${DIR_LIB_SRCS})
# 生成动态库
ADD_LIBRARY (Dir1 SHARED  ${DIR_LIB_SRCS})

生成静态库并安装到指定目录

# 设置release版本还是debug版本
if(${CMAKE_BUILD_TYPE} MATCHES "Release")
    MESSAGE(STATUS "Release版本")
    SET(BuildType "Release")
else()
    SET(BuildType "Debug")
    MESSAGE(STATUS "Debug版本")
endif()

#设置lib库目录
SET(RELEASE_DIR ${PROJECT_SOURCE_DIR}/release)
# debug和release版本目录不一样
#设置生成的so动态库最后输出的路径
SET(LIBRARY_OUTPUT_PATH ${RELEASE_DIR}/linux/${BuildType})
ADD_COMPILE_OPTIONS(-fPIC)

# 查找当前目录下的所有源文件
# 并将名称保存到 DIR_LIB_SRCS 变量
AUX_SOURCE_DIRECTORY(. DIR_LIB_SRCS)
# 生成静态库链接库Dir1
ADD_LIBRARY (Dir1 ${DIR_LIB_SRCS})
# 将库文件安装到lib目录
INSTALL(TARGETS Dir1 DESTINATION lib)
# 将头文件include
INSTALL(FILES dir1.h DESTINATION include) 

编译安装

ubuntu% cmake -DCMAKE_INSTALL_PREFIX=/tmp/usr ..
ubuntu% make
ubuntu% make install
[100%] Built target Dir1
Install the project...
-- Install configuration: ""
-- Up-to-date: /tmp/usr/lib/libDir1.a
-- Installing: /tmp/usr/include/dir1.h

具体内容及实现参考:

http://www.mamicode.com/info-detail-2439626.html

7、调用库文件
调用静态库

# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
# 工程
PROJECT(0VOICE)
# 手动加入文件
SET(SRC_LIST main.c)
MESSAGE(STATUS "THIS IS BINARY DIR " ${PROJECT_BINARY_DIR})
MESSAGE(STATUS "THIS IS SOURCE DIR " ${PROJECT_SOURCE_DIR})

# 头文件路径
INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/lib")
# 库的路径
LINK_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/lib")
# 生成执行文件
ADD_EXECUTABLE(Ray  ${SRC_LIST})
# 引用动态库
TARGET_LINK_LIBRARIES(Ray  Dir1)

首先是在CMakeLists.txt所在的目录下去寻找头文件,然后LINK_DIRECTORES相当于g++、gcc编译中的-L选项,最终生成可执行程序Ray。当然我们需要指明需要连接的库名称。

调用动态库

# 单个目录实现
# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
# 工程
PROJECT(0VOICE)
# 手动加入文件
SET(SRC_LIST main.c)
MESSAGE(STATUS "THIS IS BINARY DIR " ${PROJECT_BINARY_DIR})
MESSAGE(STATUS "THIS IS SOURCE DIR " ${PROJECT_SOURCE_DIR})

INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/lib")

LINK_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/lib")
# 引用动态库
ADD_EXECUTABLE(Ray ${SRC_LIST})
#同时静态库、动态库 优先连接动态库
#TARGET_LINK_LIBRARIES(darren Dir1)
# 强制使用静态库
TARGET_LINK_LIBRARIES(Ray libDir1.a)

这里的流程大致差不多,需要注意的就是同时存在静态库、动态库时他们的调用顺序。今天的内容先这些,后期等我掌握了在继续写,写的太乱了。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Makefile和cmake学习 的相关文章

随机推荐

  • vscode编译器卡顿问题

    最近一段时间使用vscode没有了以前的丝滑的感觉 xff0c 百度了很多种办法 xff0c 比如 xff1a 在文件 gt 首选项 gt 设置 中 xff0c 将 search followSymlinks 设置为false xff0c
  • 问题解决记录集合

    1 解决pytorch下载mnist等数据集速度过慢 失败问题 xff1a https blog csdn net weixin 44414948 article details 109756003 utm medium 61 distri
  • java 通过onvif抓取海康摄像头图片

    java 通过onvif抓取海康摄像头图片 文章目录 java 通过onvif抓取海康摄像头图片前言一 把onvif jar放到自己的maven仓库二 pom文件引入jar包三 测试代码四 运行中的变量五 参考链接地址 前言 网上也有类似的
  • java常见面试题(二)

    java基础二 11 抽象类必须要有抽象方法吗 xff1f 不需要 xff0c 抽象类不一定非要有抽象方法 示例代码 xff1a abstract class Cat public static void sayHi System out
  • 2022高教社杯全国大学生数学建模竞赛B题解析(更新完结)

    2022高教社杯全国大学生数学建模竞赛B题解析 xff08 更新完结 xff09 题目解析前言问题一1 11 21 3问题二 题目 B 题 无人机遂行编队飞行中的纯方位无源定位 无人机集群在遂行编队飞行时 xff0c 为避免外界干扰 xff
  • c++的引用和指针原来是这种关系

    c 43 43 的引用和指针原来是这种关系 关于引用引用的概念 xff1a 引用的三种情况 xff1a 当引用作为返回值的时候 xff1a 引用和指针的区别 xff1a 关于引用 引用的概念 xff1a 引用不是新定义一个变量 xff0c
  • java面试题汇总一(会持续更新)

    不积跬步无以至千里 xff0c 这里会不断收集和更新Java基础相关的面试题 xff0c 目前已收集100题 1 什么是B S架构 xff1f 什么是C S架构 B S Browser Server xff0c 浏览器 服务器程序 C S
  • 【STM32】创建stm32工程中,各个文件夹及部分文件作用

    USER xff1a 存放工程文件 主函数文件 main c 以及其他包括system stm32f10x c等 CORE xff1a 用来存放核心文件和启动文件 OBJ xff1a 是用来存放编译过程文件以及hex 文件 STM32F10
  • Qt4.8类继承关系图(全网最全)

    一 概述 在学习Qt的时候快速的查询了解类的继承关系对我们的学习会有很大的帮助 xff0c 而网上流传的多是较老版本的 xff0c 并且是jpg格式 xff0c 不便于学习使用 xff0c 所以我就花了一些时间整理了这一套Qt类继承图 xf
  • Qt5.9类继承关系图(全网最全)

    一 概述 在学习Qt的时候快速的查询了解类的继承关系对我们的学习会有很大的帮助 xff0c 而网上流传的多是较老版本的 xff0c 并且是jpg格式 xff0c 不便于学习使用 xff0c 所以我就花了一些时间整理了这一套Qt类继承图 xf
  • Qt5.15类继承关系图(全网最全)

    一 概述 在学习Qt的时候快速的查询了解类的继承关系对我们的学习会有很大的帮助 xff0c 而网上流传的多是较老版本的 xff0c 并且是jpg格式 xff0c 不便于学习使用 xff0c 所以我就花了一些时间整理了这一套Qt类继承图 xf
  • Qt6.3类继承关系图(全网最全)

    一 概述 在学习Qt的时候快速的查询了解类的继承关系对我们的学习会有很大的帮助 xff0c 而网上流传的多是较老版本的 xff0c 并且是jpg格式 xff0c 不便于学习使用 xff0c 所以我就花了一些时间整理了这一套Qt类继承图 xf
  • DSPF28335 SCI FIFO串口通讯

    在工作过程中 xff0c 通过串口进行上位机与控制器之间进行数据的传输 xff0c 标准的串口通讯容易造成数据的丢失和内存堆满的现象 xff0c 便使用SCI中的FIFO对数据进行中断处理 一 串口通信基本知识 F28335 处理器共有 3
  • 树莓派4B:控制步进电机

    记录一下驱动两相四线步进电机的过程 文章目录 准备阶段接线阶段树莓派python程序 准备阶段 准备以下物品 xff0c 淘宝都可以买到 57步进电机 xff08 两相四线 xff09 电源开关 xff08 220v转24v xff0c 3
  • 2019全国大学生电子设计竞赛(电赛)回忆录

    我给大家整理了历年电赛的题目和材料清单 xff0c 大家可以对比着看 关注微信公众号 Opencv视觉实践 xff0c 回复 电赛资料 领取 电赛是我一进大学就听学长们无数此提起的一场四天三夜的盛会 xff0c 我也自大一开始便期待着 xf
  • 【网络】HTTP中的GET方法和POST方法

    1 GET方法 xff1a 获取资源 GET方法用来请求访问已被URL识别的资源 指定的资源经服务器端接续后返回内容 也就是说 xff0c 如果请求的资源是文本 xff0c 那就保持原样返回 xff1b 如果像是CGI xff08 Conm
  • 类的6个默认成员函数

    类的成员函数 1 构造函数2 析构函数3 拷贝构造函数4 深浅拷贝5 运算符重载赋值运算符重载的特性 xff1a 1 构造函数 xff08 构造函数的调用发生在对象的创建过程中 xff0c 所以会牵扯到this指针传对象的地址问题 另外创建
  • 通过onvif抓取海康摄像头图片,以及解决海康摄像头抓取图片需要验证问题,实现摄像头一段时间换一个地方的同时抓取一张图片。

    1 实现海康摄像头的图片的抓取 思路 xff1a 1 首先获取图片的url xff0c 2通过java实现图片的下载 1 使用onvif获取图片的url 首先获取OnvifDevice的对象 OnvifDevice od 61 new On
  • 超详细电烙铁如何使用?

    电烙铁是电子硬件工程师的一个必备工具了 它主要用来焊接一些电子元器件到PCB主板上 xff0c 用来做一些维修 xff0c 验证 xff0c 分析等等 那这么一个家伙要如何使用呢 xff1f 首先来看一个电烙铁的基本外观 xff1a 它一般
  • Makefile和cmake学习

    一 Makefile 一 什么是Makefile 1 Makefile 可以简单的认为是一个工程文件的编译规则 xff0c 描述了整个工程的编译和链接等规则 其中包含了那些文件需要编译 xff0c 那些文件不需要编译 xff0c 那些文件需