工程编译那点事:Makefile和cmake(一)

2023-05-16

makefile

  • 前言
  • Makefile介绍
    • Makefile规则
    • make是如何工作的
    • Makefile使用变量
    • 让make自动推导
    • 另类风格的Makefile
    • make中的函数
    • 文件搜索
    • 绝对顶级的大型工程Makefile

前言

  一个项目,拥有成百上千的源程序文件,那如何组织这些源码文件的编译和链接呢?此时就需要确定整个工程的编译链接规则,Makefile就是用来指定规则的。而make是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。

Makefile介绍

  当我们编译工程时,只要输入make就会去编译了,那么这个make命令执行的时候一定需要一个Makefile文件,通过这个Makefile告诉make命令需要怎么样的去编译和链接程序。

  make的工作规则是:

1.如果这个工程没有编译过,那么我们的所有C文件都要编译并被链接。
2.如果这个工程的某几个C文件被修改,那么我们只编译被修改的C文件,并链接目标程序。
3.如果这个工程的头文件被改变了,那么我们需要编译引用了这几个头文件的C文件,并链接目标程序。

  只要我们的Makefile写得够好,所有的这一切,我们只用一个make命令就可以完成,make命令会自动智能地根据当前的文件修改的情况来确定哪些文件需要重编译,从而自己编译所需要的文件和链接目标程序。

Makefile规则

  在讲述这个Makefile之前,还是让我们先来粗略地看一看Makefile的规则。
在这里插入图片描述

	target... : prerequisites ...
	command
	 ...
	 ...
  • target:也就是一个目标文件,可以是Object File,也可以是执行文件,也可以是标签。
  • prerequisites:就是,要生成那个target所需要的文件或是目标。
  • command:也就是make需要执行的命令。(任意的Shell命令)

  这是一个文件的依赖关系,也就是说,target这一个或多个的目标文件依赖于prerequisites中的文件,其生成规则定义在command中。说白一点就是说,prerequisites中如果有一个以上的文件比target文件要新的话,command所定义的命令就会被执行。这就是Makefile的规则。也就是Makefile中最核心的内容。
  说到底,Makefile的东西就是这样一点,好像接下来不用再讲任何东西了。呵呵。还不尽然,这是Makefile的主线和核心,但要写好一个Makefile还不够,我会以后面一点一点地结合我的工作经验给你慢慢到来。内容还多着呢。:)
  我们来看一个示例:

nty_http_server : nty_coroutine.o nty_epoll.o nty_schedule.o nty_socket.o nty_http_server.o
        gcc -o nty_http_server nty_http_server.o nty_coroutine.o nty_epoll.o nty_schedule.o nty_socket.o -lpthread
nty_coroutine.o:nty_coroutine.c nty_coroutine.h
        gcc -c nty_coroutine.c
nty_epoll.o:nty_epoll.c nty_coroutine.h
        gcc -c nty_epoll.c
nty_schedule.o:nty_schedule.c nty_coroutine.h
        gcc -c nty_schedule.c
nty_socket.o:nty_socket.c nty_coroutine.h
        gcc -c nty_socket.c
nty_http_server.o:nty_http_server.c nty_coroutine.h
        gcc -c nty_http_server.c

clean :
        rm -rf *.o

  在定义好依赖关系后,后续的那一行定义了如何生成目标文件的操作系统命令,一定要以一个Tab键作为开头。记住,make并不管命令是怎么工作的,他只管执行所定义的命令。make会比较targets文件和prerequisites文件的修改日期,如果prerequisites文件的日期要比targets文件的日期要新,或者target不存在的话,那么,make就会执行后续定义的命令。

这里要说明一点的是,clean不是一个文件,它只不过是一个动作名字,有点像C语言中的lable一样,其冒号后什么也没有,那么,make就不会自动去找文件的依赖性,也就不会自动执行其后所定义的命令。要执行其后的命令,就要在make命令后明显得指出这个lable的名字。这样的方法非常有用,我们可以在一个makefile中定义不用的编译或是和编译无关的命令,比如程序的打包,程序的备份,等等。

make是如何工作的

  在默认的方式下,也就是我们只输入make命令。那么,

  1. make会在当前目录下找名字叫“Makefile”或“makefile”的文件。
  2. 如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“nty_http_server”这个文件,并把这个文件作为最终的目标文件。
  3. 如果nty_http_server文件不存在,或是nty_http_server所依赖的后面的 .o 文件的文件修改时间要比nty_http_server这个文件新,那么,他就会执行后面所定义的命令来生成nty_http_server这个文件。
  4. 如果nty_http_server所依赖的.o文件也存在,那么make会在当前文件中找目标为.o文件的依赖性,如果找到则再根据那一个规则生成.o文件。(这有点像一个堆栈的过程)
  5. 当然,你的C文件和H文件是存在的啦,于是make会生成 .o 文件,然后再用 .o 文件声明make的终极任务,也就是执行文件nty_http_server了。

  这就是整个make的依赖性,make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么make就会直接退出,并报错,而对于所定义的命令的错误,或是编译不成功,make根本不理。make只管文件的依赖性,即,如果在我找了依赖关系之后,冒号后面的文件还是不在,那么对不起,我就不工作啦。
  通过上述分析,我们知道,像clean这种,没有被第一个目标文件直接或间接关联,那么它后面所定义的命令将不会被自动执行,不过,我们可以显示要make执行。即命令——“make clean”,以此来清除所有的目标文件,以便重编译。
  于是在我们编程中,如果这个工程已被编译过了,当我们修改了其中一个源文件,比如nty_coroutine.c,那么根据我们的依赖性,我们的目标nty_coroutine.o会被重编译(也就是在这个依性关系后面所定义的命令),于是nty_coroutine.o的文件也是最新的啦,于是nty_coroutine.o的文件修改时间要比nty_http_server要新,所以nty_http_serve也会被重新链接了(详见nty_http_serve目标文件后定义的命令)。
  而如果我们改变了“nty_coroutine.h”,那么,nty_coroutine.o、nty_epoll.o、nty_schedule.o、nty_socket.o和nty_http_server.o都会被重编译,并且,nty_http_server会被重链接。

Makefile使用变量

  在上面的例子中,我们可以看到[.o]文件的字符串被重复了两次,如果我们的工程需要加入一个新的[.o]文件,那么我们需要在两个地方加(应该是三个地方,还有一个地方在clean中)。当然,我们的makefile并不复杂,所以在两个地方加也不累,但如果makefile变得复杂,那么我们就有可能会忘掉一个需要加入的地方,而导致编译失败。所以,为了makefile的易维护,在makefile中我们可以使用变量。makefile的变量也就是一个字符串,理解成C语言中的宏可能会更好。

  比如,我们声明一个变量,叫objects, OBJECTS, objs, OBJS, obj, 或是 OBJ,反正不管什么啦,只要能够表示obj文件就行了。我们在makefile一开始就这样定义:

OBJECTS=nty_coroutine.o nty_epoll.o nty_schedule.o nty_socket.o nty_http_server.o

  于是我们的Makefile可以修改成这样子:

OBJECTS=nty_coroutine.o nty_epoll.o nty_schedule.o nty_socket.o nty_http_server.o
nty_http_server : $(OBJECTS)
        gcc -o nty_http_server $(OBJECTS) -lpthread
nty_coroutine.o:nty_coroutine.c nty_coroutine.h
        gcc -c nty_coroutine.c
nty_epoll.o:nty_epoll.c nty_coroutine.h
        gcc -c nty_epoll.c
nty_schedule.o:nty_schedule.c nty_coroutine.h
        gcc -c nty_schedule.c
nty_socket.o:nty_socket.c nty_coroutine.h
        gcc -c nty_socket.c
nty_http_server.o:nty_http_server.c nty_coroutine.h
        gcc -c nty_http_server.c

clean :
        rm -rf $(OBJECTS) nty_http_server

  于是如果有新的 .o 文件加入,我们只需简单地修改一下 OBJECTS 变量就可以了。可是聪明的你应该也发现了这个Makefile还是不好维护,因为每个.o文件都需要显示地写出依赖和生成的命令,导致这个Makefile有很多的雷同,我们有没有更好的维护办法呢?请看下一小节:

让make自动推导

  GNU的make很强大,它可以自动推导文件以及文件依赖关系后面的命令,于是我们就没必要去在每一个[.o]文件后都写上类似的命令,因为,我们的make会自动识别,并自己推导命令。只要make看到一个[.o]文件,它就会自动的把[.c]文件加在依赖关系中,如果make找到一个whatever.o,那么whatever.c,就会是whatever.o的依赖文件。并且 cc -c whatever.c 也会被推导出来,于是,我们的makefile再也不用写得这么复杂。我们的是新的makefile又出炉了。

OBJECTS=nty_coroutine.o nty_epoll.o nty_schedule.o nty_socket.o nty_http_server.o
nty_http_server : $(OBJECTS)
        gcc -o nty_http_server $(OBJECTS) -lpthread
nty_coroutine.o:nty_coroutine.h
nty_epoll.o:nty_coroutine.h
nty_schedule.o:nty_coroutine.h
nty_socket.o:nty_coroutine.h
nty_http_server.o:nty_coroutine.h

.PHONY : clean
clean :
        rm -rf $(OBJECTS) nty_http_server

  这种方法,也就是make的“隐晦规则”。上面文件内容中,“.PHONY”表示,clean是个伪目标文件。
  即然我们的make可以自动推导命令,那么我看到那堆[.o]和[.h]的依赖就有点不爽,那么多的重复的[.h],能不能把其收拢起来?预知后事如何,请听下回分解。
  

另类风格的Makefile

  上一小节提到的问题是没有问题,这个对于make来说很容易,谁叫它提供了自动推导命令和文件的功能呢?来看看最新风格的makefile吧。

OBJECTS=nty_coroutine.o nty_epoll.o nty_schedule.o nty_socket.o nty_http_server.o
nty_http_server : $(OBJECTS)
        gcc -o nty_http_server $(OBJECTS) -lpthread
$(OBJECTS):nty_coroutine.h

.PHONY : clean
clean :
        rm -rf $(OBJECTS) nty_http_server

  这种风格,让我们的makefile变得很简单,但我们的文件依赖关系就显得有点凌乱了。鱼和熊掌不可兼得。还看你的喜好了。
  另外即使这个Makefile比较简单,但是它依然比较笨拙,比如如果我们需要添加新的源码文件,都要来修改OBJECTS变量的定义,那怎么办呢?

make中的函数

先来看我们的Makefile:

SOURCES=$(wildcard *.c)
OBJECTS=$(patsubst %.c,%.o,$(SOURCES))
PROGRAM=nty_http_server
CC=gcc
CFLAGS=-lpthread

$(PROGRAM) : $(OBJECTS)
        $(CC) -o $(PROGRAM) $(OBJECTS) $(CFLAGS)
$(OBJECTS):$(SOURCES)

.PHONY : clean
clean :
        rm -rf $(OBJECTS) $(PROGRAM)

  在 GNU Make 里有一个叫 ‘wildcard’ 的函 数,它有一个参数,功能是展开成一列所有符合由其参数描述的文 件名,文件间以空格间隔。你可以像下面所示使用这个命令:

SOURCES= $(wildcard *.c)    

  这行会产生一个所有以 ‘.c’ 结尾的文件的列表,然后存入变量 SOURCES 里。当然你不需要一定要把结果存入一个变量。
  notdir把展开的文件的路径去掉,只显示文件名而不包含其路径信息,例如:

FILES =$(notdir $(SOURCES))

  这行的作用是把上面以'.c'结尾的文件的文件列表中附带的路径去掉,只显示符合条件的文件名。
  patsubst( patten substitude, 匹配替换的缩写)函数。它需要3个参数:第一个是一个需要匹配的式样,第二个表示用什么来替换它,第三个是一个需要被处理的由空格分隔的字列。例如,处理那个经过上面定义后的变量,

OBJS = $(patsubst %.c,%.o,$(SOURCES))

  这行将处理所有在 SOURCES列个中的字(一列文件名),如果它的 结尾是 ‘.c’ ,就用’.o’ 把 ‘.c’ 取代。注意这里的 % 符号将匹配一个或多个字符,而它每次所匹配的字串叫做一个‘柄’(stem) 。在第二个参数里, % 被解读成用第一参数所匹配的那个柄。
make还有很多的函数,比如foreach,origin,call,if then else,addprefix,dir,notdir

  到这里,我们已经把Makefile改了5个版本,这个应该是可以用于大型工程了吧???非也,非也,非也。比如在大型工程中,我们一般会划分模块,不同的目录管理不同的源码文件,此时Makefile如何写呢?

文件搜索

  在一些大的工程中,有大量的源文件,我们通常的做法是把这许多的源文件分类,并存放在不同的目录中。所以,当make需要去找寻文件的依赖关系时,你可以在文件前加上路径,但最好的方法是把一个路径告诉make,让make在自动去找。
  Makefile文件中的特殊变量“VPATH”就是完成这个功能的,如果没有指明这个变量,make只会在当前的目录中去找寻依赖文件和目标文件。如果定义了这个变量,那么,make就会在当当前目录找不到的情况下,到所指定的目录中去找寻文件了。

   VPATH = src:../headers

  上面的的定义指定两个目录,“src”和“…/headers”,make会按照这个顺序进行搜索。目录由“冒号”分隔。(当然,当前目录永远是最高优先搜索的地方)。
  另一个设置文件搜索路径的方法是使用make的“vpath”关键字(注意,它是全小写的),这不是变量,这是一个make的关键字,这和上面提到的那个VPATH变量很类似,但是它更为灵活。它可以指定不同的文件在不同的搜索目录中。这是一个很灵活的功能。它的使用方法有三种:

1. vpath < pattern> < directories>    为符合模式< pattern>的文件指定搜索目录<directories>。

2. vpath < pattern>                              清除符合模式< pattern>的文件的搜索目录。

3. vpath                                                 清除所有已被设置好了的文件搜索目录。

  vapth使用方法中的< pattern>需要包含“%”字符。“%”的意思是匹配零或若干字符,例如,“%.h”表示所有以“.h”结尾的文件。< pattern>指定了要搜索的文件集,而< directories>则指定了的文件集的搜索的目录。例如:

   vpath %.h ../headers

  该语句表示,要求make在“…/headers”目录下搜索所有以“.h”结尾的文件。(如果某文件在当前目录没有找到的话)。
  我们可以连续地使用vpath语句,以指定不同搜索策略。如果连续的vpath语句中出现了相同的< pattern>,或是被重复了的< pattern>,那么,make会按照vpath语句的先后顺序来执行搜索。如:

   vpath %.c foo

   vpath %   blish

   vpath %.c bar

  其表示“.c”结尾的文件,先在“foo”目录,然后是“blish”,最后是“bar”目录。

   vpath %.c foo:bar

   vpath %   blish

  而上面的语句则表示“.c”结尾的文件,先在“foo”目录,然后是“bar”目录,最后才是“blish”目录。
  到这里,我们已经把Makefile改了6个版本,这个应该是可以用于大型工程了吧???非也,非也,非也。比如在大型工程中,我们的目标文件一般会有多个,那如何实现一键式编译,比如我们在编译thrift就用过,还比如我们很多同学搞过嵌入式开发,比如用于路由器开发的openwrt系统,那个系统一键make命令,就能把所有的包里的目标编译完,那这个是多麽神奇啊,Oh my god, what should i do?

绝对顶级的大型工程Makefile

  其实在Makefile中可以包含别的Makefile,被包含的Makefile一般是定义了一些变量,让通过这些变量控制其他的Makefile的编译。也可以再包含多个包,从顶层的Makefile一直调用到所有包的Makefile,完成编译工作。那这到底是如何完成的,大家先移步到
https://github.com/zhiyong0804/RTSP_PullerModule.git
我们看到顶级目录的Makefile,内容如下:

LIVEMEDIA_DIR = liveMedia
GROUPSOCK_DIR = groupsock
USAGE_ENVIRONMENT_DIR = UsageEnvironment
BASIC_USAGE_ENVIRONMENT_DIR = BasicUsageEnvironment

PULLER_MODULE_DIR = PullerModule

all:
	cd $(LIVEMEDIA_DIR) ; $(MAKE)
	cd $(GROUPSOCK_DIR) ; $(MAKE)
	cd $(USAGE_ENVIRONMENT_DIR) ; $(MAKE)
	cd $(BASIC_USAGE_ENVIRONMENT_DIR) ; $(MAKE)

install:
	mkdir -p $(PULLER_MODULE_DIR)/lib
	cd $(LIVEMEDIA_DIR) ; $(MAKE) install
	cd $(GROUPSOCK_DIR) ; $(MAKE) install
	cd $(USAGE_ENVIRONMENT_DIR) ; $(MAKE) install
	cd $(BASIC_USAGE_ENVIRONMENT_DIR) ; $(MAKE) install

module:
	cd $(PULLER_MODULE_DIR) ; $(MAKE)
	cd $(PULLER_MODULE_DIR) ; $(MAKE) test

clean:
	cd $(LIVEMEDIA_DIR) ; $(MAKE) clean
	cd $(GROUPSOCK_DIR) ; $(MAKE) clean
	cd $(USAGE_ENVIRONMENT_DIR) ; $(MAKE) clean
	cd $(BASIC_USAGE_ENVIRONMENT_DIR) ; $(MAKE) clean
	cd $(PULLER_MODULE_DIR) ; $(MAKE) clean

cleanall:
	cd $(PULLER_MODULE_DIR) ; $(MAKE) cleanall

  从该文件我们看到在all里,有cd $(LIVEMEDIA_DIR) ; $(MAKE),这就是要进入到liveMedia子目录执行make操作,当然在该Makefile还需要编译groupsock、UsageEnvironment和BasicUsageEnvironment目录,在module这个伪目标里,再去编译了PullerModule和PullerModule的测试工程。那么我们进入到groupsock目录看看它的Makefile内容如下:

INCLUDES = -Iinclude -I../UsageEnvironment/include

include ../inc.mk

NAME = libgroupsock
ALL = $(NAME).$(LIB_SUFFIX)
all:	$(ALL)

.$(C).$(OBJ):
	$(C_COMPILER) -c $(C_FLAGS) $<
.$(CPP).$(OBJ):
	$(CPLUSPLUS_COMPILER) -c $(CPLUSPLUS_FLAGS) $<

GROUPSOCK_LIB_OBJS = GroupsockHelper.$(OBJ) GroupEId.$(OBJ) inet.$(OBJ) Groupsock.$(OBJ) NetInterface.$(OBJ) NetAddress.$(OBJ) IOHandlers.$(OBJ)

GroupsockHelper.$(CPP):	include/GroupsockHelper.hh
include/GroupsockHelper.hh:	include/NetAddress.hh
include/NetAddress.hh:	include/NetCommon.h
GroupEId.$(CPP):	include/GroupEId.hh
include/GroupEId.hh:	include/NetAddress.hh
inet.$(C):		include/NetCommon.h
Groupsock.$(CPP):	include/Groupsock.hh include/GroupsockHelper.hh include/TunnelEncaps.hh
include/Groupsock.hh:	include/groupsock_version.hh include/NetInterface.hh include/GroupEId.hh
include/NetInterface.hh:	include/NetAddress.hh
include/TunnelEncaps.hh:	include/NetAddress.hh
NetInterface.$(CPP):	include/NetInterface.hh include/GroupsockHelper.hh
NetAddress.$(CPP):	include/NetAddress.hh include/GroupsockHelper.hh
IOHandlers.$(CPP):	include/IOHandlers.hh include/TunnelEncaps.hh

libgroupsock.$(LIB_SUFFIX): $(GROUPSOCK_LIB_OBJS) \
    $(PLATFORM_SPECIFIC_LIB_OBJS)
	$(LIBRARY_LINK)$@ $(LIBRARY_LINK_OPTS) \
		$(GROUPSOCK_LIB_OBJS)

clean:
	-rm -rf *.$(OBJ) $(ALL) core *.core *~ include/*~

install: install1 $(INSTALL2)
install1: libgroupsock.$(LIB_SUFFIX)
	  install -m 644 libgroupsock.$(LIB_SUFFIX) $(DESTDIR)$(LIBDIR)
install_shared_libraries: libgroupsock.$(LIB_SUFFIX)
	  ln -s libgroupsock.$(LIB_SUFFIX) $(DESTDIR)$(LIBDIR)/libgroupsock.$(SHORT_LIB_SUFFIX)
	  ln -s libgroupsock.$(LIB_SUFFIX) $(DESTDIR)$(LIBDIR)/libgroupsock.so

##### Any additional, platform-specific rules come here:

第三行include ../inc.mk包含了上一级目录的inc.mk(对,没有错,我们一般的include的Makefile习惯以.mk命名),为什么要include这个文件呢,这是因为C_FLAGS还有其它的变量都是定义在inc.mk里,而这些变量对于其它的包(比如groupsock、UsageEnvironment和BasicUsageEnvironment)也是需要的,避免重复定义,我们在inc.mk统一定义。OK,inc.mk文件内容如下:

PREFIX = ../PullerModule
LIBDIR = $(PREFIX)/lib

Debug = -g
Release = 

#### Change the following for your environment:
COMPILE_OPTS =	$(Debug)	$(INCLUDES)  -fPIC -I. -O2 -DSOCKLEN_T=socklen_t -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64
C =		c
C_COMPILER =		cc  # replace with "mipsel-linux-cc" for mipsel platform
C_FLAGS =		$(COMPILE_OPTS)
CPP =			cpp
CPLUSPLUS_COMPILER =	g++ # replace with "mipsel-linux-g++" for mipsel platform
CPLUSPLUS_FLAGS =	$(COMPILE_OPTS) -Wall -DBSD=1
OBJ =			o
LINK =			g++ -o  # replace with "mipsel-linux-g++ -o" for mipsel platform
LINK_OPTS =		-L.
CONSOLE_LINK_OPTS =	$(LINK_OPTS)
LIBRARY_LINK =		ar cr #replace with "mipsel-linux-ar cr" for mipsel platform
LIBRARY_LINK_OPTS =	
LIB_SUFFIX =			a
LIBS_FOR_CONSOLE_APPLICATION =
LIBS_FOR_GUI_APPLICATION =
EXE =
##### End of variables to change

写在这一篇的最后:Makefile大致感觉差不多了,当然还有一些细枝末节,比如 @ 、 @、 @^等等。但是大体来讲Makefile就是依赖规则的推导,其余就是一些操作命令了,所以shell语法也很重要。

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

工程编译那点事:Makefile和cmake(一) 的相关文章

  • 如何计算CAN总线的负载率以及每帧耗时和每秒最多传输帧数

    今天遇到一个问题 xff0c 需要计算一下CAN总线上 xff0c 不同的波特率下 xff0c 每秒钟最多能够传输多少帧数据 xff0c 或者说 xff0c 每帧数据需要耗时多少时间 一开始以为这个东西是很基础的确定性数据 xff0c 百度
  • 如何在bat脚本中显示出该脚本文件的相关信息

    今天在研究windows系统下的bat脚本 xff0c 想要在bat脚本运行的过程中 xff0c 输出脚本文件自身的一些信息 xff0c 经过一番查阅和摸索 xff0c 采用以下代码就可以实现 xff1a 64 span class tok
  • 如何在bat脚本中列出指定目录下的所有文件信息

    今天在研究windows系统下的bat脚本 xff0c 想要去遍历一个目录并输出该目录下的所有文件的详细信息 经过一番查找和摸索 xff0c 使用以下代码即可实现 xff1a 64 span class token function ech
  • 解决IDEA导入maven项目Plugin ‘org.apache.maven.pluginsmaven-compiler-plugin‘ not found问题

    问题 xff1a 导入了maven项目 xff0c 报错Plugin org apache maven plugins maven compiler plugin not found等问题 xff0c 不仅仅是 maven compiler
  • ISO11898/ISO14229/ISO14230/ISO15031/ISO15765标准对比简介

    各行各业都有很多的国际标准和规范 xff0c 汽车行业也不例外 xff0c 经常遇到一套一套的标准和规范 xff0c 什么ISO11898 xff0c ISO14229 xff0c ISO14230 xff0c ISO15031 xff0c
  • 如何解决UltraCompare中中文显示乱码的问题

    作为一名程序猿 xff0c 对代码进行对比分析是经常避免不了的 xff0c 这时候就需要一个很好用的对比工具 xff0c 我选择了大名鼎鼎的Ultra Compare 但是 xff0c 使用过程中发现 xff0c 这家伙居然不支持中文 xf
  • 一文简单介绍眼图

    搞通信行业的 xff0c 经常要关注信号的传输质量 xff0c 近几年开始出现了一个新的名词 xff0c 叫眼图 xff0c 可以非常直观的体现出信号传输的情况 那么 xff0c 什么是眼图 xff0c 怎么解读眼图 xff0c 下面就为大
  • 下一代CAN通信技术CAN XL简介

    众所周知 xff0c CAN网络具有易于实现 可扩展 并支持复杂拓扑结构等诸多优点 xff0c 所以目前在各个行业特别是汽车领域 xff0c 得到了非常广泛的应用 但是 xff0c 它也存在着诸多问题 xff0c 其中最大的一点就是总线带宽
  • 聊一下质量管理体系中的DQE/SQE/PQE/CQE

    最近接触了不少质量体系的话题 xff0c 反复的听到SQE DQE等 xff0c 大概知道是跟质量管理相关的一些职位 xff0c 但是具体也不是很清楚 xff0c 于是干脆来理一下 正式开始之前 xff0c 先要介绍一下他们的老祖宗 xff
  • C语言字符串库函数 #include <string.h>

    c语言字符串库函数 include lt string h gt 在头文件 lt string h gt 中定义了两组字符串函数 第一组函数的名字以str开头 xff1b 第二组函数的名字以mem开头 只有函数memmove对重叠对象间的拷
  • gcc编译动态库静态库及Makefile知识汇总;

    gcc编译 动态库静态库及Makefile知识汇总 gcc编译过程gcc的常用选项静态库 xff0c 动态库并制作 xff1b Makefile 基本使用make工具简介 gcc编译过程 我们在Linux下使用vim命令写的 c文件 xff
  • 简单学习一下Linux中的环境变量

    环境变量 最近在学习Linux下的C程序编写 xff0c 在运行可执行程序的时候有个问题就是链接的问题 xff0c 最后通过学习才知道是因为环境变量没有设置好 xff1b 环境变量在进行linux开发过程中经常碰到 xff0c 那什么是环境
  • STM32-ESP8266wifi模块实现

    1 ESP8266WiFi模块介绍 1 1ESP8266wifi 模块 低功耗串口WiFi模块ESP8266内置一个Tensilica xff08 泰思立达 xff09 Xtensa架构的32位处理器L106 xff0c 具有5级流水线 A
  • ESP8266WiFi模块实现代码

    ESP8266WiFi模块实现TCP连接服务器 在前面的博客里 xff08 STM32 ESP8266wifi模块实现 xff09 说到了通过AT命令配置ESP8266实现TCP连接 他的流程是 xff0c 1 xff0c 使能串口 xff
  • 2021-05-14 Redis面试题 redis 部署生产环境

    redis 部署生产环境 redis cluster xff0c 10 台机器 xff0c 5 台机器部署了 redis 主实例 xff0c 另外 5 台机器部署了 redis 的从实例 xff0c 每个主实例挂了一个从实例 xff0c 5
  • 实现Basic认证

    Basic认证是一种较为简单的HTTP认证方式 xff0c 客户端通过明文 xff08 Base64编码格式 xff09 传输用户名和密码到服务端进行认证 xff0c 通常需要配合HTTPS来保证信息传输的安全 Maven依赖 lt par
  • STM32的串口空闲中断

    STM32串口使用DMA方式接收数据可以减小CPU的开销 对于接收定长数据 xff0c 可以将DMA接收缓冲区的长度设定为待接收数据的长度 xff0c 这样利用DMA的传输完成中断DMAx IT TCy就可以知道已经接收了一帧数据 对于接收
  • C#完整的通信代码(点对点,点对多,同步,异步,UDP,TCP)

    C code namespace UDPServer class Program static void Main string args int recv byte data 61 new byte 1024 构建TCP 服务器 得到本机
  • http digest认证(Java server)

    背景 xff1a 服务器接收客户端请求 xff0c 处理并验证 并返回服务器的验证结果 关于digest认证的相关概念及验证原理查看相关的说明 xff0c 此处只对处理进行贴码 CODE import com alibaba fastjso
  • 测试apache时出现[error] [client 192.168.6.1] File does not exist: /etc/httpd/htdocs

    问题 xff1a 测试apache时出现 error client 192 168 6 1 File does not exist etc httpd htdocs 解决方法 xff1a 1 创建文件夹htdocs xff08 etc ht

随机推荐

  • HAL库 STM32 串口通信函数

    HAL UART Receive IT串口 xff01 HAL UART Receive IT amp UART1 Handler u8 aRxBuffer RXBUFFERSIZE HAL UART Receive IT函数使用的时候 简
  • linux环境下安装QT超详细

    QT安装 1 首先下载QT安装包 QT官网 xff1a Index of archive qt 我这里使用的是qt opensource linux x64 5 14 0 run版本 2 打开终端 xff0c 输入命令 xff0c 赋予安装
  • 使用ssh连接虚拟机保姆级教程

    首先安装SSH 安装先检测是否已经安装SSH xff1a service ssh status 如果出现提示 xff1a ssh unrecognized service 说明没有安装openSSH xff0c 则需安装ssh SSH 服务
  • 小游戏2048设计思路超简单

    2048作为一个经典的小游戏 xff0c 对于C语言的逻辑练习是一个比较好的案例了 xff0c 看似很复杂 xff0c 但是如果掌握了设计思路 xff0c 那么就不会觉得难了 xff0c 而且会了这个之后对今后编程的也会有很大的帮助 先分析
  • ES-Elasticsearch查看所有索引及查看某索引下的信息

    1 查看所有索引 xff0c 地址栏直接访问下面的连接 http localhost 9200 cat indicesv amp pretty 2 查看某索引下存的信息 xff0c 查询的信息为索引结构信息 xff08 indexName为
  • 教你十分钟搭建博客,已在多台电脑测试,无坑

    1 前期工作 1 注册Github账号 官网地址 xff1a GitHub 2 下载安装git Git软件下载地址 xff1a Git Downloading Package git scm com 安装的话一直点next就可以了 3 绑定
  • MQTT使用TLS加密

    使用TLS加密在MQTT的使用中是比较常见的 xff0c TLS加密过程在网上有很多说明 xff0c 但是没几个应用教程的 xff0c MQTT软件中的EMQX软件是支持TLS加密的 xff0c 只不过要进行一些设置 安装EMQX软件 首先
  • linux下将QT移植至arm环境

    前言 讲下整个项目流程 xff0c 我们的目标是把qt编出来程序放在arm开发板上面跑 xff0c 首先下载QT源码和tslib源码 xff08 QT源码编译和QT程序运行需要tslib库的支持 xff09 xff0c 在虚拟机里使用交叉编
  • linux下移植onvif至arm环境

    前言 onvif是一种网络摄像头协议 xff0c linux网络摄像头这一块是需要移植onvif协议的 xff0c 整个移植过程是这样的 xff0c 首先onvif协议是依赖于gsoap的 xff0c 所以需要先将gsoap编译安装 xff
  • C语言使用二级指针实现字符串分割

    话不多说 xff0c 直接上代码 include lt stdio h gt int opt char p char ch char res char q 61 p if q 61 61 NULL q 61 61 39 0 39 retur
  • 已include包却提示未定义标识符

    已 include lt string gt xff0c include lt vector gt 却提示string xff0c vector未定义的标识符 因为没定义默认的命名空间 xff0c 改为std string xff0c st
  • jpg和png的区别小结

    png图像的大小是jpg图像大小的数倍 xff0c png为可移植网络图形格式 xff0c 也是一种位图文件存储格式 xff0c 可以进行无损压缩 xff0c jpg是最常见的图像格式 xff0c 图像占用的存储较小 xff0c 但是牺牲了
  • 色彩空间中的HSL、HSV、HSB有什么区别?

    作者 xff1a 大蔚陈 链接 xff1a https www zhihu com question 22077462 answer 29483467 来源 xff1a 知乎 著作权归作者所有 商业转载请联系作者获得授权 xff0c 非商业
  • opencv小程序练习——createBackgroundSubtractorMOG2()实现跟踪

    思想 xff1a 使用createBackgroundSubtractorMOG2 使用运动物体背景分割 使用findContours 查找轮廓并画出 实现一定感官上的跟踪功能 代码如下 xff1a include lt opencv2 o
  • opencv_tutorial_code学习——canny边缘检测后findContours

    tutorial code ShapeDescriptors findContours demo cpp 步骤 xff1a 1 灰度化 2 滤波 3 canny边缘检测 4 findContours canny output contour
  • idea开发工具右侧没有maven工具栏

    一 解决方式一 依次点击 View gt Tool Windows gt Maven xff0c 如下图 xff1a 二 解决方式二 点击如图所示IDEA界面最左下角的按钮 xff0c 如下图 xff1a 三 解决方式三 1 打开项目的 p
  • 语义分割与实例分割的区别

    目前的分割任务主要有两种 xff1a xff08 1 xff09 像素级别的语义分割 xff08 2 xff09 实例分割 这个有意思 xff0c 什么叫实例分割呢 xff1f 它与语义分割有什么区别与联系呢 xff1f 顾名思义 xff0
  • tiny-dnn配置运行

    tiny dnn是一个轻量级的CNN xff08 卷积神经网络 xff09 xff0c 不需要各种依赖和GPU xff0c 由三千多行C 43 43 代码完成 适配android平台的话 xff0c 感觉这个比较好做一点 下载地址 xff1
  • SSM笔记

    进入数据库 xff1a 34 D Program Files x86 mysql 5 7 20 winx64 bin mysql exe 34 u root p admin 展示数据库 xff1a show databases 创建数据库
  • 工程编译那点事:Makefile和cmake(一)

    makefile 前言Makefile介绍 Makefile规则make是如何工作的Makefile使用变量让make自动推导另类风格的Makefilemake中的函数文件搜索绝对顶级的大型工程Makefile 前言 一个项目 xff0c