makefile 学习(5)完整的makefile模板

参考自:

  • (1)深度学习部署笔记(二): g++, makefile语法,makefile自己的CUDA编程模板
  • (2)https://zhuanlan.zhihu.com/p/396448133
  • (3) 一个挺好的工程模板,(https://github.com/shouxieai/cpp-proj-template)

1. c++ 编译流程图

在这里插入图片描述

2. 使用makefile编译工程

  • Makefile是一个经典的构建工具,使用它可以根据一系列规则构建程序。在编写Makefile的过程中,需要了解一些底层的细节,包括链接器编译器等等。Makefile可以被配置成在所有的平台上都能够使用,非常适合轻量级项目和较小的项目
  • CMake则是一种构建工具的高级语言,可以自动生成适合各种平台和编译器的Makefile文件,同时也能生成Visual Studio等IDE所需要的工程文件。CMake使用的是一种高级的、面向目标的语言,可以实现更加复杂的构建任务。CMake的优点在于它可以跨平台地生成Makefile文件,同时具有很好的可读性和可维护性,非常适合大型的、复杂的项目。CMake的缺点在于它的学习曲线相对较陡,需要学习一些新的概念和语法。
  • 总之,Makefile和CMake各自有其优缺点,在实际使用中需要根据项目的规模和需求选择适合的构建工具。

2.1 demo

(1) 一个makefile的demo

  • 新建一个main.cpp文件和一个01kernel.cu文件
#include <iostream>
using namespace std;

int main()
{
  cout << "Hello CUDA" << endl;
  return 0;
}

  • 新建一个Makefile文件
cpp_srcs := $(shell find src -name "*.cpp")
cpp_objs := $(patsubst %.cpp,%.o,$(cpp_srcs))
cpp_objs := $(subst src/,objs/,$(cpp_objs))

debug :
	@echo $(cpp_objs)

  • 终端执行 make debug 如果有问题就是有bug

(2) 逐行解释这个小demo

  • 定义变量 cpp_srcs,它的值是通过调用find命令在 src 目录下查找所有以 .cpp 结尾的文件,并将它们的路径存入 cpp_srcs 变量中。
cpp_srcs := $(shell find src -name "*.cpp")
  • 定义变量 cpp_objs,它的值是将 cpp_srcs 变量中所有 .cpp 后缀的文件名都替换成以.o结尾的目标文件名,并将这些目标文件名保存到 cpp_objs 变量中。
cpp_objs := $(patsubst %.cpp,%.o,$(cpp_srcs))
  • cpp_objs 变量中的所有 src/ 替换为 objs/,并将替换后的值存储回 cpp_objs 变量
cpp_objs := $(subst src/,objs/,$(cpp_objs))
  • 创建一个名为 debug 的伪目标,它的命令是打印 cpp_objs 变量的值,@ 告诉 Make 不要显示命令行上的命令,只显示命令的输出。
debug :
    @echo $(cpp_objs)

2.2 makefile的一个简单模板

# 定义cpp源码路径,并转换为objs目录先的o文件
cpp_srcs := $(shell find src -name "*.cpp")    
cpp_objs := $(patsubst %.cpp,%.o,$(cpp_srcs))
cpp_objs := $(subst src/,objs/,$(cpp_objs))

# 定义cu源码路径,并转换为objs目录先的cuo文件
# 如果cpp文件和cu名字一样,把.o换成.cuo
cu_srcs := $(shell find src -name "*.cu")   # 全部src下的*.cu存入变量cu_srcs
cu_objs := $(patsubst %.cu,%.cuo,$(cu_srcs)) # cu_srcs中全部.cu换成.o
cu_objs := $(subst src/,objs/,$(cu_objs))    # cu_objs src/换成objs/


# 定义名称参数
workspace := workspace 
binary := pro

# makefile中定义cpp的编译方式
# $@:代表规则中的目标文件(生成项)
# $<:代表规则中的第一个依赖文件
# $^:代表规则中的所有依赖文件,以空格分隔
# $?:代表规则中所有比目标文件更新的依赖文件,以空格分隔


# 定义cpp文件的编译方式
# @echo Compile $<     输出正在编译的源文件的名称
objs/%.o : src/%.cpp
	@mkdir -p $(dir $@)
	@echo Compile $<        
	@g++ -c $^ -o $@ 

# 定义.cu文件的编译方式
objs/%.cuo : src/%.cu
	@mkdir -p $(dir $@)
	@echo Compile $< 
	@nvcc -c $^ -o $@

# 定义workspace/pro文件的编译
$(workspace)/$(binary) : $(cpp_objs) $(cu_objs)
	@mkdir -p $(dir $@)
	@echo Link $^
	@g++ $^ -o $@

# 定义pro快捷编译指令,这里只发生编译,不执行
# 快捷指令就是make pro
pro : $(workspace)/$(binary)

# 定义指令并且执行的指令,并且执行目录切换到workspace下
run : pro
	@cd $(workspace) && ./$(binary)
	
debug :
	@echo $(cpp_objs)
	@echo $(cu_objs)

这里报错了,因为缺少了cuda的库文件,下面是查看自己cuda版本和找到在哪里的指令,以我的CUDA11.7为例, 去到目录下看看有什么库文件,我们当前需要一个cudaruntime的头文件

nvcc --version
whereis cuda-11.7
cuda-11: /usr/local/cuda-11.7
cd /usr/local/cuda-11.7/lib64 

2.3 完整学习整个makefile文件

来看看最后要完成的工程目录

project/
  - src/
      - main.cpp
      - 01.kernel.cu
  - objs/
  - workspace/
  - Makefile

(1) 定义源码路径

  • 这里定义源码路径的作用是为了在后续的代码中可以方便地引用源码文件,并将其编译成目标文件。具体地,使用find命令查找src目录下的所有扩展名为.cpp.cu的文件,然后将它们分别存储到cpp_srcscu_srcs变量中。接着,使用patsubstsubst函数将源码文件路径中的src/替换成objs/,并将它们分别存储到cpp_objs和cu_objs变量中,这样就得到了在objs目录下存储的目标文件列表。在后面的编译和链接过程中,这些目标文件(*.o, *.cuo)会被用来生成最终的可执行文件。
    - objs 目录中放置的是编译生成的目标文件,对于这个 makefile 来说,所有的 .o.cuo文件都会被放到 objs 目录下。在链接生成可执行文件时,makefile 会从这些目标文件中找到需要的文件进行链接
# 定义cpp源码路径,并转换为objs目录先的o文件
cpp_srcs := $(shell find src -name "*.cpp")    
cpp_objs := $(patsubst %.cpp,%.o,$(cpp_srcs))
cpp_objs := $(subst src/,objs/,$(cpp_objs))

# 定义cu源码路径,并转换为objs目录先的cuo文件
# 如果cpp文件和cu名字一样,把.o换成.cuo
cu_srcs := $(shell find src -name "*.cu")    # 全部src下的*.cu存入变量cu_srcs
cu_objs := $(patsubst %.cu,%.cuo,$(cu_srcs)) # cu_srcs中全部.cu换成.o
cu_objs := $(subst src/,objs/,$(cu_objs))    # cu_objs src/换成objs/

(2) 定义可执行文件及存放位置

这里定义的名称参数 workspace 和 binary 是用来指定工作空间和生成的可执行文件名称的。在这个 Makefile 中,workspace 表示的是工作空间的目录名称,binary 表示生成的可执行文件的名称。这些参数可以在 Makefile 中的其它规则中使用,例如在链接时指定目标文件路径和生成的可执行文件名称等。

workspace := workspace
binary := pro

(3) 定义头文件,库文件和链接目标

  • 因为编译cuda环境需要用到libcudart.so这个库,cudart 是 CUDA runtime库,CUDA编程必须用到的
  • 先整个定义了,后面用foreach一次性加进来
  • 每个人的cuda的安装位置不一样,可以用whereis cuda-版本查找位置
  • 编译的过程中需要查找头文件库文件include_path, library_path告诉编译器去哪里找头文件,库文件
  • link_librarys是告诉链接器需要链接那些库文件
# 定义头文件库文件和链接目标,后面用foreach一次性增加
include_paths := /usr/local/cuda-11.7/include
library_paths := /usr/local/cuda-11.7/lib64
link_librarys := cudart

(4) 定义编译选项

# 定义编译选项
cpp_compile_flags := -m64 -fPIC -g -O0 -std=c++11
cu_compile_flags := -m64 -g -O0 -std=c++11
  • -m64:表示编译器生成的代码是 64 位的。
  • -fPIC:表示编译器要生成位置独立的代码。
  • -g:表示编译器会在生成的目标文件中加入调试信息,方便进行调试。
  • -O0:表示关闭优化。
  • -std=c++11:表示采用 C++11 标准进行编译。

(5)合并选项

  • -L 指定链接时查找的目录
  • -l 指定链接的目标名称,符合libname.so -lname 规则
  • -I 指定编译时头文件查找目录
  • run path 链接的时查找动态链接库文件的路径,让程序运行的时候,自动查找并加载动态链接库
rpath         := $(foreach item,$(link_librarys),-Wl,-rpath=$(item))
include_paths := $(foreach item,$(include_paths),-I$(item))
library_paths := $(foreach item,$(library_paths),-L$(item))
link_librarys := $(foreach item,$(link_librarys),-l$(item))

(6) 把合并后的选项给到编译器选项

  • cpp_compile_flags += $(include_paths): 将include_paths添加到cpp_compile_flags中,用于在编译C++源代码时指定头文件搜索路径。
  • cu_compile_flags += $(include_paths): 将include_paths添加到cu_compile_flags中,用于在编译CUDA源代码时指定头文件搜索路径。
  • link_flags := $(rpath) $(library_paths) $(link_librarys): 将rpath、library_pathslink_librarys合并成一个链接选项link_flags。rpath指定运行时库搜索路径,library_paths指定链接库搜索路径,link_librarys指定要链接的库文件名。
cpp_compile_flags += $(include_paths)
cu_compile_flags  += $(include_paths)
link_flags        := $(rpath) $(library_paths) $(link_librarys)

(7) 定义cpp cuda编译方式

  • .cpp .cu源文件 编译成目标文件 .o .cuo,放在objs里面
  • .cpp, .cu源文件是依赖项,生成目标文件.o .cuo
# 定义cpp文件的编译方式
# @echo Compile $<     输出正在编译的源文件的名称
objs/%.o : src/%.cpp
	@mkdir -p $(dir $@)
	@echo Compile $<        
	@g++ -c $^ -o $@ $(cpp_compile_flags)

# 定义.cu文件的编译方式
objs/%.cuo : src/%.cu
	@mkdir -p $(dir $@)
	@echo Compile $< 
	@nvcc -c $^ -o $@ $(cu_compile_flags)

(8) 在workspace下编译出可执行文件

  • 上面.cpp, .cu文件编译出来的结果在这里就是依赖项
  • -L./objs 表示告诉链接器在当前目录下寻找库文件,./objs 是指定的路径。实际上,./objs 是目标文件存储的路径,不是库文件存储的路径,这里写的是 -L./objs 只是为了指定链接时查找目录的路径。
# 定义workspace/pro文件的编译
$(workspace)/$(binary) : $(cpp_objs) $(cu_objs)
	@mkdir -p $(dir $@)
	@echo Link $^
	@g++ $^ -o $@ $(link_flags) -L./objs

(9)定义伪标签, 作为指令

  • 上面编译的可执行文件,是指令依赖项
  • 执行可执行文件
# 定义pro快捷编译指令,这里只发生编译,不执行
# 快捷指令就是make pro
pro : $(workspace)/$(binary)

# 定义指令并且执行的指令,并且执行目录切换到workspace下
run : pro
	@cd $(workspace) && ./$(binary)
	
debug :
	@echo $(cpp_objs)
	@echo $(cu_objs)

clean : 
	@rm -rf objs $(workspace)/$(binary)

# 指定伪标签,作为指令
.PHONY : clean debug run pro
  • (10) 完整的makefile文件
# 定义cpp源码路径,并转换为objs目录先的o文件
cpp_srcs := $(shell find src -name "*.cpp")    
cpp_objs := $(patsubst %.cpp,%.o,$(cpp_srcs))
cpp_objs := $(subst src/,objs/,$(cpp_objs))

# 定义cu源码路径,并转换为objs目录先的cuo文件
# 如果cpp文件和cu名字一样,把.o换成.cuo
cu_srcs := $(shell find src -name "*.cu")    # 全部src下的*.cu存入变量cu_srcs
cu_objs := $(patsubst %.cu,%.cuo,$(cu_srcs)) # cu_srcs中全部.cu换成.o
cu_objs := $(subst src/,objs/,$(cu_objs))    # cu_objs src/换成objs/


# 定义可执行文件存放目录以及可执行文件名
workspace := workspace
binary := pro

# 定义头文件库文件和链接目标,后面用foreach一次性增加
include_paths := /usr/local/cuda-11.7/include
library_paths := /usr/local/cuda-11.7/lib64
link_librarys := cudart

# 定义编译选项
cpp_compile_flags := -m64 -fPIC -g -O0 -std=c++11
cu_compile_flags := -m64 -g -O0 -std=c++11

# 对头文件, 库文件,目标统一增加 -I,-L-l
rpath         := $(foreach item,$(link_librarys),-Wl,-rpath=$(item))
include_paths := $(foreach item,$(include_paths),-I$(item))
library_paths := $(foreach item,$(library_paths),-L$(item))
link_librarys := $(foreach item,$(link_librarys),-l$(item))

# 合并选项
# 合并完选项后就可以给到编译方式里面去了
cpp_compile_flags += $(include_paths)
cu_compile_flags  += $(include_paths)
link_flags        := $(rpath) $(library_paths) $(link_librarys)


# makefile中定义cpp的编译方式
# $@:代表规则中的目标文件(生成项)
# $<:代表规则中的第一个依赖文件
# $^:代表规则中的所有依赖文件,以空格分隔
# $?:代表规则中所有比目标文件更新的依赖文件,以空格分隔


# 定义cpp文件的编译方式
# @echo Compile $<     输出正在编译的源文件的名称
objs/%.o : src/%.cpp
	@mkdir -p $(dir $@)
	@echo Compile $<        
	@g++ -c $^ -o $@ $(cpp_compile_flags)

# 定义.cu文件的编译方式
objs/%.cuo : src/%.cu
	@mkdir -p $(dir $@)
	@echo Compile $< 
	@nvcc -c $^ -o $@ $(cu_compile_flags)

# 定义workspace/pro文件的编译
$(workspace)/$(binary) : $(cpp_objs) $(cu_objs)
	@mkdir -p $(dir $@)
	@echo Link $^
	@g++ $^ -o $@ $(link_flags) -L./objs


# 定义pro快捷编译指令,这里只发生编译,不执行
# 快捷指令就是make pro
pro : $(workspace)/$(binary)

# 定义指令并且执行的指令,并且执行目录切换到workspace下
run : pro
	@cd $(workspace) && ./$(binary)
	
debug :
	@echo $(cpp_objs)
	@echo $(cu_objs)

clean : 
	@rm -rf objs $(workspace)/$(binary)

# 指定伪标签,作为指令
.PHONY : clean debug run pro

3. 推荐一个makefile工程

超全面和详细的一个makefil工程,值得学习借鉴

cc        := g++
name      := pro
workdir   := workspace
srcdir    := src
objdir    := objs
stdcpp    := c++11
cuda_home := /home/yuanwushui/anaconda3/lib/python3.9/site-packages/trtpy/trt8cuda112cudnn8
syslib    := /home/yuanwushui/anaconda3/lib/python3.9/site-packages/trtpy/lib
cpp_pkg   := /home/yuanwushui/anaconda3/lib/python3.9/site-packages/trtpy/cpp-packages  #opencv4.2
cuda_arch := 
nvcc      := $(cuda_home)/bin/nvcc -ccbin=$(cc)

# 定义cpp的路径查找和依赖项mk文件
cpp_srcs := $(shell find $(srcdir) -name "*.cpp")
cpp_objs := $(cpp_srcs:.cpp=.cpp.o)
cpp_objs := $(cpp_objs:$(srcdir)/%=$(objdir)/%)
cpp_mk   := $(cpp_objs:.cpp.o=.cpp.mk)

# 定义cu文件的路径查找和依赖项mk文件
cu_srcs := $(shell find $(srcdir) -name "*.cu")
cu_objs := $(cu_srcs:.cu=.cu.o)
cu_objs := $(cu_objs:$(srcdir)/%=$(objdir)/%)
cu_mk   := $(cu_objs:.cu.o=.cu.mk)

# 定义opencv和cuda需要用到的库文件
link_cuda      := cudart cudnn
link_trtpro    := 
link_tensorRT  := nvinfer nvinfer_plugin
link_opencv    := opencv_core opencv_imgproc opencv_imgcodecs opencv_videoio opencv_video opencv_highgui
link_sys       := stdc++ dl protobuf
link_librarys  := $(link_cuda) $(link_tensorRT) $(link_sys) $(link_opencv)

# 定义头文件路径,请注意斜杠后边不能有空格
# 只需要写路径,不需要写-I
include_paths := src              \
    $(cuda_home)/include/cuda     \
	$(cuda_home)/include/tensorRT \
	$(cpp_pkg)/opencv4.2/include  \
	$(cuda_home)/include/protobuf \
	include

# 定义库文件路径,只需要写路径,不需要写-L
library_paths := $(cuda_home)/lib64 $(syslib) $(cpp_pkg)/opencv4.2/lib

# 把library path给拼接为一个字符串,例如a b c => a:b:c
# 然后使得LD_LIBRARY_PATH=a:b:c
empty := 
library_path_export := $(subst $(empty) $(empty),:,$(library_paths))

# 把库路径和头文件路径拼接起来成一个,批量自动加-I、-L、-l
run_paths     := $(foreach item,$(library_paths),-Wl,-rpath=$(item))
include_paths := $(foreach item,$(include_paths),-I$(item))
library_paths := $(foreach item,$(library_paths),-L$(item))
link_librarys := $(foreach item,$(link_librarys),-l$(item))

# 如果是其他显卡,请修改-gencode=arch=compute_75,code=sm_75为对应显卡的能力
# 显卡对应的号码参考这里:https://developer.nvidia.com/zh-cn/cuda-gpus#compute
# 如果是 jetson nano,提示找不到-m64指令,请删掉 -m64选项。不影响结果
cpp_compile_flags := -std=$(stdcpp) -w -g -O0 -m64 -fPIC -fopenmp -pthread
cu_compile_flags  := -std=$(stdcpp) -w -g -O0 -m64 $(cuda_arch) -Xcompiler "$(cpp_compile_flags)"
link_flags        := -pthread -fopenmp -Wl,-rpath='$$ORIGIN'

cpp_compile_flags += $(include_paths)
cu_compile_flags  += $(include_paths)
link_flags        += $(library_paths) $(link_librarys) $(run_paths)

# 如果头文件修改了,这里的指令可以让他自动编译依赖的cpp或者cu文件
ifneq ($(MAKECMDGOALS), clean)
-include $(cpp_mk) $(cu_mk)
endif

$(name)   : $(workdir)/$(name)

all       : $(name)
run       : $(name)
	@cd $(workdir) && ./$(name) $(run_args)

$(workdir)/$(name) : $(cpp_objs) $(cu_objs)
	@echo Link $@
	@mkdir -p $(dir $@)
	@$(cc) $^ -o $@ $(link_flags)

$(objdir)/%.cpp.o : $(srcdir)/%.cpp
	@echo Compile CXX $<
	@mkdir -p $(dir $@)
	@$(cc) -c $< -o $@ $(cpp_compile_flags)

$(objdir)/%.cu.o : $(srcdir)/%.cu
	@echo Compile CUDA $<
	@mkdir -p $(dir $@)
	@$(nvcc) -c $< -o $@ $(cu_compile_flags)

# 编译cpp依赖项,生成mk文件
$(objdir)/%.cpp.mk : $(srcdir)/%.cpp
	@echo Compile depends C++ $<
	@mkdir -p $(dir $@)
	@$(cc) -M $< -MF $@ -MT $(@:.cpp.mk=.cpp.o) $(cpp_compile_flags)
    
# 编译cu文件的依赖项,生成cumk文件
$(objdir)/%.cu.mk : $(srcdir)/%.cu
	@echo Compile depends CUDA $<
	@mkdir -p $(dir $@)
	@$(nvcc) -M $< -MF $@ -MT $(@:.cu.mk=.cu.o) $(cu_compile_flags)

# 定义清理指令
clean :
	@rm -rf $(objdir) $(workdir)/$(name) $(workdir)/*.trtmodel $(workdir)/*.onnx 
	@rm -rf $(workdir)/image-draw.jpg $(workdir)/input-image.jpg $(workdir)/pytorch.jpg

# 防止符号被当做文件
.PHONY : clean run $(name)

# 导出依赖库路径,使得能够运行起来
export LD_LIBRARY_PATH:=$(library_path_export)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/188960.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux加强篇004-Vim编辑器与Shell命令脚本

目录 前言 1. Vim文本编辑器 1.1 编写简单文档 1.2 配置主机名称 1.3 配置网卡信息 1.4 配置软件仓库 2. 编写Shell脚本 2.1 编写简单的脚本 2.2 接收用户的参数 2.3 判断用户的参数 3. 流程控制语句 3.1 if条件测试语句 3.2 for条件循环语句 3.3 while条件循环语…

深入浅出 Linux 中的 ARM IOMMU SMMU II

SMMU 驱动中的系统 I/O 设备探测 要使系统 I/O 设备的 DMA 内存访问能通过 IOMMU&#xff0c;需要将系统 I/O 设备和 IOMMU 设备绑定起来&#xff0c;也就是执行 SMMU 驱动中的系统 I/O 设备探测。总线发现系统 I/O 设备并和对应的驱动程序绑定&#xff0c;与 IOMMU 设备驱动程…

死锁是什么?死锁是如何产生的?如何破除死锁?

1. 死锁是什么 多个线程同时被阻塞&#xff0c;它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞&#xff0c;因此程序不可能正常终止。 2. 死锁的三种典型情况 一个线程, 一把锁, 是不可重入锁, 该线程针对这个锁连续加锁两次, 就会出现死锁. 两个线程…

Java多态:多态多态,多么变态

&#x1f451;专栏内容&#xff1a;Java⛪个人主页&#xff1a;子夜的星的主页&#x1f495;座右铭&#xff1a;前路未远&#xff0c;步履不停 目录 一、重写1、重写的规则2、重写与重载的区别 二、多态1、多态的概念2、多态的实现3、向上转移和向下转型Ⅰ、向上转型Ⅱ、向下转…

蓝桥杯官网算法赛(蓝桥小课堂)

问题描述 蓝桥小课堂开课啦&#xff01; 海伦公式&#xff08;Herons formula&#xff09;&#xff0c;也称为海伦-秦九韶公式&#xff0c;是用于计算三角形面积的一种公式&#xff0c;它可以通过三条边的长度来确定三角形的面积&#xff0c;而无需知道三角形的高度。 海伦公…

BART 并行成像压缩感知重建:联合重建

本文使用 variavle-density possion-disc 采样的多通道膝盖数据进行并行重建和压缩感知重建。 0 数据欠采样sampling pattern 1 计算ESPIRiT maps % A visualization of k-space dataknee = readcfl(data/knee); ksp_rss = bart(rss 8, knee);ksp_rss = squeeze(ksp_rss); figu…

11 月 25 日 ROS 学习笔记——3D 建模与仿真

文章目录 前言一、在 ROS 中自定义机器人的3D模型1. 在 rviz 里查看3D模型2. xacro 二、Gazebo1. urdf 集成 gazebo2. 综合应用1). 运动控制及里程计2). 雷达仿真3). 摄像头信息仿真4). kinect 深度相机仿真5). 点云 前言 本文为11 月 25 日 ROS 学习笔记——3D 建模与仿真&am…

求集合的笛卡尔乘积

求集合的笛卡尔乘积 一&#xff1a;【实验目的】二&#xff1a;【实验内容】三&#xff1a;【实验原理】四&#xff1a;代码实现&#xff1a; 一&#xff1a;【实验目的】 通过编实现给定集合A和B的笛卡尔积CAA,DAB,EBA,FAAB,GA(A*B&#xff09;. 二&#xff1a;【实验内容】…

迈巴赫S480升级电动后门 手势控制开关 更加方便

安装了电动后门的迈巴赫S480&#xff0c;从原来的触摸门把手和门内拉手开关门&#xff0c;增加了钥匙控制、前排显示屏控制、后门按键开关控制、后排娱乐屏控制等多种开关门方式&#xff0c;将一个待客之礼体现出多种不一样的尊贵感受。 中控显示屏由驾驶者控制&#xff0c;可以…

1.4 8位加法器

1.半加器 2.全加器 半加器: 完整模拟1位加法 1.A,B 接受端,接受1或0 , 2个电信号 2.异或门 做为结果: 1^10, 0^00, 1^01, 0^11 与编程中的: 异或一致 3.与门 做为进位: 1&11,1&00,0&10, 0&01 与编程中的: 与一致 4.半加器实现1位的加法运算,比如:A端: …

图书馆管理系统源码(Java)

Book包->内含Book类与BookList类 Book类 用于初始化图书并对其进行操作 BookList类 用于存放多本图书 Book类 package Book;public class Book {private String name;private String author;private int price;private String type;private boolean isBorrow;//写一个…

6.11左叶子之和(LC404-E)

用java定义树&#xff1a; public class TreeNode {int val;TreeNode left;TreeNode right; //一个空构造方法TreeNode()&#xff0c;用于初始化节点的默认值。TreeNode() {} //一个构造方法TreeNode(int val)&#xff0c;用于初始化节点的值&#xff0c;并设置默认的左右子节…

【Unity3D】MAX聚合广告SDK——Pangle广告接入

Pangle, App Monetization Simplified 注册 登录 创建应用 创建广告单元 将其应用ID和广告ID关联到MAX广告。 下载Pangle Unity Plugin包&#xff0c;新建一个空工程&#xff08;很重要&#xff09; Unity版本2019.4.0f1 gradle plugin 4.2.0 gradle版本6.7.1 build_tools 34.…

el-uploader同一文件无法上传问题

在上传成功和失败的回调方法中&#xff0c;吊用一下clearFiles方法。 this.$refs.upload.clearFiles();

Node.js入门指南(四)

目录 express框架 express介绍 express使用 express路由 express 响应设置 中间件 路由模块化 EJS 模板引擎 express-generator hello&#xff0c;大家好&#xff01;上一篇文章我们介绍了Node.js的模块化以及包管理工具等知识&#xff0c;这篇文章主要给大家分享Nod…

c语言中指针详解

内存与地址 1.地址的概念 众所周知数据需要在内存中读取&#xff0c;那么我们当我们需要找这个内存时该怎么办&#xff1f;其实在计算机中每个内存单元都有一个编号&#xff0c;有了这个编号CPU可以快速找到内存空间&#xff0c;在这里我们将这个编号叫做地址&#xff0c;在c…

企业计算机服务器中了360勒索病毒怎么办,360勒索病毒解密文件恢复

计算机技术的不断发展&#xff0c;为企业的生产运营提供了极大便利&#xff0c;不仅提升了办公效率&#xff0c;还促进了企业的发展。企业计算机在日常工作中一定加以防护&#xff0c;减少网络威胁事件的产生&#xff0c;确保企业的生产生产运营。最近&#xff0c;网络上的360后…

nodejs+vue+python+PHP+微信小程序-健身俱乐部在线管理平台的设计与实现-安卓-计算机毕业设计

随着经济的发展、财富的累积&#xff0c;人们生活水平、生活质量大幅度提高&#xff0c;生活环境得到明显改善&#xff0c;但是竞争激烈、人们生活压力大、生活节奏快加上饮食习惯和生活方式不合理导致国内 亚健康人群逐年增多。统计数据表明当前我国亚健康人群比例已经超过了7…

vscode导入STM32CubeIDE工程文件夹未定义警告清除方法

0 前言 在我们使用vscode去编辑STM32CubeIDE的工程文件时&#xff0c;经常会出现一些类型未定义、头文件路径无效的问题&#xff0c;无法正常使用且非常影响观感。本文介绍如何设置vscode导入的STM32CubeIDE配置文件&#xff0c;解决这一问题。 1 vscode导入STM32CubeIDE工程…

JVM——几种常见的对象引用

目录 1. 软引用软引用的使用场景-缓存 2.弱引用3.虚引用和终结器引用 可达性算法中描述的对象引用&#xff0c;一般指的是强引用&#xff0c;即是GCRoot对象对普通对象有引用关系&#xff0c;只要这层关系存在&#xff0c; 普通对象就不会被回收。除了强引用之外&#xff0c;Ja…
最新文章