20-22 - 打造专业的编译环境

news2024/9/21 14:50:21

---- 整理自狄泰软件唐佐林老师课程

文章目录

  • 1. 大型项目的编译(无第三方库)
    • 1.1 大型项目的目录结构(无第三方库)
    • 1.2 项目结构设计分析
    • 1.3 需要打造的编译环境
    • 1.4 解决方案设计
  • 2. 第 1 阶段任务
    • 2.1 关键的实现要点
    • 2.2 模块 makefile 中的构成
    • 2.3 实验
  • 3. 第 2 阶段任务
    • 3.1 关键的实现要点
    • 3.2 开发中的经验假设
    • 3.3 解决方案设计
    • 3.4 makefile 中嵌入 shell 的 for 循环
    • 3.5 工程 makefile 中的关键构成
    • 3.6 链接时的注意事项
    • 3.7 实验
  • 4. 优化
    • 4.1 问题 1
    • 4.2 问题 2
    • 4.3 关键问题
    • 4.4 工程 makefile 的重构

1. 大型项目的编译(无第三方库)

1.1 大型项目的目录结构(无第三方库)

在这里插入图片描述

1.2 项目结构设计分析

  • 项目被划分为不同模块
    • 每个模块的代码用一个文件夹进行管理
      文件夹由 inc、src、makefile 构成
    • 每个模块的对外函数声明统一放置于 common/inc 中
      如:common.h、xxxfunc.h

1.3 需要打造的编译环境

  • 源码文件夹在编译时不能被改动(只读文件夹)
  • 在编译时自动创建文件夹(build)用于存放编译结果
  • 编译过程中能够自动生成依赖关系,自动搜索需要的文件
  • 每个模块可以拥有自己独立的编译方式
  • 支持调试版本的编译选项

1.4 解决方案设计

  • 第 1 阶段:将每个模块中的代码编译成静态库文件

在这里插入图片描述

  • 第 2 阶段:将每个模块的静态库文件链接成最终可执行程序

在这里插入图片描述

2. 第 1 阶段任务

  • 完成可用于各个模块编译的 makefile 文件
  • 每个模块的编译结果为静态库文件(.a 文件)

2.1 关键的实现要点

  • 自动生成依赖关系(gcc -MM
  • 自动搜索需要的文件(vpath
  • 将目标文件打包为静态库文件(ar crs

2.2 模块 makefile 中的构成

在这里插入图片描述

2.3 实验

在这里插入图片描述

.PHONY : all
# 声明all为伪目标(PHONY),避免与同名文件冲突。

DIR_BUILD := /home/wx/uuxiang/makefile/20_22/00/build
DIR_COMMON_INC := /home/wx/uuxiang/makefile/20_22/00/common/inc
# 定义编译输出目录和公共头文件目录的路径。

DIR_SRC := src
DIR_INC := inc
# 定义源文件目录和头文件目录的路径。

TYPE_INC := .h
TYPE_SRC := .c
TYPE_OBJ := .o
TYPE_DEP := .dep
# 定义不同文件类型的扩展名,包括头文件(.h)、源文件(.c)、目标文件(.o)、依赖文件(.dep)。

AR := ar
ARFLAGS := crs
# 定义生成静态库的工具和其使用的参数。

CC := gcc
CFLAGS := -I$(DIR_INC) -I$(DIR_COMMON_INC)
# 定义编译器为gcc,CFLAGS包含了头文件搜索路径。

ifeq ($(DEBUG),true)
CFLAGS += -g
endif
# 如果变量DEBUG为true,则添加-g选项用于生成调试信息。

MODULE := $(realpath .)
MODULE := $(notdir $(MODULE))
DIR_OUTPUT := $(addprefix $(DIR_BUILD)/, $(MODULE))
# 获取当前目录的绝对路径,将模块名设置为当前目录的名称,并定义模块的输出目录。

OUTPUT := $(MODULE).a
OUTPUT := $(addprefix $(DIR_BUILD)/, $(OUTPUT))
# 定义最终生成的静态库文件名,并设置输出路径。

SRCS := $(wildcard $(DIR_SRC)/*$(TYPE_SRC))
OBJS := $(SRCS:$(TYPE_SRC)=$(TYPE_OBJ))
OBJS := $(patsubst $(DIR_SRC)/%, $(DIR_OUTPUT)/%, $(OBJS))
DEPS := $(SRCS:$(TYPE_SRC)=$(TYPE_DEP))
DEPS := $(patsubst $(DIR_SRC)/%, $(DIR_OUTPUT)/%, $(DEPS))
# 查找源文件,生成目标文件和依赖文件列表,并将它们从源目录映射到输出目录。

vpath %$(TYPE_INC) $(DIR_INC)
vpath %$(TYPE_INC) $(DIR_COMMON_INC)
vpath %$(TYPE_SRC) $(DIR_SRC)
# 设置文件搜索路径,vpath用于在指定目录中查找特定类型的文件。

-include $(DEPS)
# 包含所有生成的依赖文件,如果依赖文件不存在则跳过(-表示忽略错误)。

all : $(OUTPUT)
	@echo "Success! Target ==> $(OUTPUT)"
# 定义默认目标all,生成最终的静态库,并打印成功信息。

$(OUTPUT) : $(OBJS)
	$(AR) $(ARFLAGS) $@ $^
# 规则:生成静态库,将目标文件归档为静态库。

$(DIR_OUTPUT)/%$(TYPE_OBJ) : %$(TYPE_SRC)
	$(CC) $(CFLAGS) -o $@ -c $(filter %$(TYPE_SRC), $^)
# 规则:编译源文件生成目标文件。

$(DIR_OUTPUT)/%$(TYPE_DEP) : %$(TYPE_SRC)
	@echo "Creating $@ ..."
	@set -e; \
	$(CC) $(CFLAGS) -MM -E $(filter %$(TYPE_SRC), $^) | sed 's,\(.*\)\.o[ :]*,$(DIR_OUTPUT)/\1$(TYPE_OBJ) $@ : ,g' > $@
# 规则:生成依赖文件。通过gcc的-MM选项生成依赖信息,并使用sed命令调整格式,将生成的依赖信息重定向到依赖文件中。

在这里插入图片描述

3. 第 2 阶段任务

  • 完成编译整个工程的 makefile 文件
  • 调用模块 makefile 编译生成静态库文件
  • 链接所有模块的静态库文件,最终得到可执行程序

在这里插入图片描述

3.1 关键的实现要点

  • 如何自动创建 build 文件夹以及子文件夹?
  • 如何进入每一个模块文件夹进行编译?
  • 编译成功后如何链接所有模块静态库?

3.2 开发中的经验假设

项目中的各个模块在设计阶段就已经基本确定,因此,在之后的开发过程中不会频繁随意的增加或减少

3.3 解决方案设计

  • 定义变量保存模块名列表(模块名变量)
  • 利用 shell 中的 for 循环遍历模块名变量
  • 在 for 循环中进入模块文件夹进行编译
  • 循环结束后链接所有的模块静态库文件

3.4 makefile 中嵌入 shell 的 for 循环

在这里插入图片描述

  • 注意事项:
    makefile 中嵌入 shell 代码时,如果需要使用 shell 变量的值,必须在变量名前加上 $$(例如:$$dir

在这里插入图片描述
在这里插入图片描述

3.5 工程 makefile 中的关键构成

在这里插入图片描述

3.6 链接时的注意事项

  • gcc 在进行静态库链接时必须遵循严格的依赖关系
    • gcc -o app.out x.a y.a z.a
      其中的依赖关系必须为:x.a–>y.a,y.a–>z.a
      默认情况下遵循自左向右的依赖关系
  • 如果不清楚库间的依赖,可以使用 -Xlinker 自动确定依赖关系
    • gcc -o app.out -Xlinker "-("z.a y.a x.a -Xlinker "-)"

3.7 实验

在这里插入图片描述

  • common/makefile:
.PHONY : all
# 声明all为伪目标(PHONY),避免与同名文件冲突。

DIR_BUILD := /home/wx/uuxiang/makefile/20_22/02/build
DIR_COMMON_INC := /home/wx/uuxiang/makefile/20_22/02/common/inc
# 定义编译输出目录和公共头文件目录的路径。

DIR_SRC := src
DIR_INC := inc
# 定义源文件目录和头文件目录的路径。

TYPE_INC := .h
TYPE_SRC := .c
TYPE_OBJ := .o
TYPE_DEP := .dep
# 定义不同文件类型的扩展名,包括头文件(.h)、源文件(.c)、目标文件(.o)、依赖文件(.dep)。

AR := ar
ARFLAGS := crs
# 定义生成静态库的工具和其使用的参数。

CC := gcc
CFLAGS := -I$(DIR_INC) -I$(DIR_COMMON_INC)
# 定义编译器为gcc,CFLAGS包含了头文件搜索路径。

ifeq ($(DEBUG),true)
CFLAGS += -g
endif
# 如果变量DEBUG为true,则添加-g选项用于生成调试信息。

MODULE := $(realpath .)
MODULE := $(notdir $(MODULE))
DIR_OUTPUT := $(addprefix $(DIR_BUILD)/, $(MODULE))
# 获取当前目录的绝对路径,将模块名设置为当前目录的名称,并定义模块的输出目录。

OUTPUT := $(MODULE).a
OUTPUT := $(addprefix $(DIR_BUILD)/, $(OUTPUT))
# 定义最终生成的静态库文件名,并设置输出路径。

SRCS := $(wildcard $(DIR_SRC)/*$(TYPE_SRC))
OBJS := $(SRCS:$(TYPE_SRC)=$(TYPE_OBJ))
OBJS := $(patsubst $(DIR_SRC)/%, $(DIR_OUTPUT)/%, $(OBJS))
DEPS := $(SRCS:$(TYPE_SRC)=$(TYPE_DEP))
DEPS := $(patsubst $(DIR_SRC)/%, $(DIR_OUTPUT)/%, $(DEPS))
# 查找源文件,生成目标文件和依赖文件列表,并将它们从源目录映射到输出目录。

vpath %$(TYPE_INC) $(DIR_INC)
vpath %$(TYPE_INC) $(DIR_COMMON_INC)
vpath %$(TYPE_SRC) $(DIR_SRC)
# 设置文件搜索路径,vpath用于在指定目录中查找特定类型的文件。

-include $(DEPS)
# 包含所有生成的依赖文件,如果依赖文件不存在则跳过(-表示忽略错误)。

all : $(OUTPUT)
	@echo "Success! Target ==> $(OUTPUT)"
# 定义默认目标all,生成最终的静态库,并打印成功信息。

$(OUTPUT) : $(OBJS)
	$(AR) $(ARFLAGS) $@ $^
# 规则:生成静态库,将目标文件归档为静态库。

$(DIR_OUTPUT)/%$(TYPE_OBJ) : %$(TYPE_SRC)
	$(CC) $(CFLAGS) -o $@ -c $(filter %$(TYPE_SRC), $^)
# 规则:编译源文件生成目标文件。

$(DIR_OUTPUT)/%$(TYPE_DEP) : %$(TYPE_SRC)
	@echo "Creating $@ ..."
	@set -e; \
	$(CC) $(CFLAGS) -MM -E $(filter %$(TYPE_SRC), $^) | sed 's,\(.*\)\.o[ :]*,$(DIR_OUTPUT)/\1$(TYPE_OBJ) $@ : ,g' > $@
# 规则:生成依赖文件。通过gcc的-MM选项生成依赖信息,并使用sed命令调整格式,将生成的依赖信息重定向到依赖文件中。

  • main/makefile:
.PHONY : all
# 声明all为伪目标(PHONY),避免与同名文件冲突。

DIR_BUILD := /home/wx/uuxiang/makefile/20_22/02/build
DIR_MAIN_INC := /home/wx/uuxiang/makefile/20_22/02/main/inc
# 定义编译输出目录和公共头文件目录的路径。

DIR_SRC := src
DIR_INC := inc
# 定义源文件目录和头文件目录的路径。

TYPE_INC := .h
TYPE_SRC := .c
TYPE_OBJ := .o
TYPE_DEP := .dep
# 定义不同文件类型的扩展名,包括头文件(.h)、源文件(.c)、目标文件(.o)、依赖文件(.dep)。

AR := ar
ARFLAGS := crs
# 定义生成静态库的工具和其使用的参数。

CC := gcc
CFLAGS := -I$(DIR_INC) -I$(DIR_MAIN_INC)
# 定义编译器为gcc,CFLAGS包含了头文件搜索路径。

ifeq ($(DEBUG),true)
CFLAGS += -g
endif
# 如果变量DEBUG为true,则添加-g选项用于生成调试信息。

MODULE := $(realpath .)
MODULE := $(notdir $(MODULE))
DIR_OUTPUT := $(addprefix $(DIR_BUILD)/, $(MODULE))
# 获取当前目录的绝对路径,将模块名设置为当前目录的名称,并定义模块的输出目录。

OUTPUT := $(MODULE).a
OUTPUT := $(addprefix $(DIR_BUILD)/, $(OUTPUT))
# 定义最终生成的静态库文件名,并设置输出路径。

SRCS := $(wildcard $(DIR_SRC)/*$(TYPE_SRC))
OBJS := $(SRCS:$(TYPE_SRC)=$(TYPE_OBJ))
OBJS := $(patsubst $(DIR_SRC)/%, $(DIR_OUTPUT)/%, $(OBJS))
DEPS := $(SRCS:$(TYPE_SRC)=$(TYPE_DEP))
DEPS := $(patsubst $(DIR_SRC)/%, $(DIR_OUTPUT)/%, $(DEPS))
# 查找源文件,生成目标文件和依赖文件列表,并将它们从源目录映射到输出目录。

vpath %$(TYPE_INC) $(DIR_INC)
vpath %$(TYPE_INC) $(DIR_MAIN_INC)
vpath %$(TYPE_SRC) $(DIR_SRC)
# 设置文件搜索路径,vpath用于在指定目录中查找特定类型的文件。

-include $(DEPS)
# 包含所有生成的依赖文件,如果依赖文件不存在则跳过(-表示忽略错误)。

all : $(OUTPUT)
	@echo "Success! Target ==> $(OUTPUT)"
# 定义默认目标all,生成最终的静态库,并打印成功信息。

$(OUTPUT) : $(OBJS)
	$(AR) $(ARFLAGS) $@ $^
# 规则:生成静态库,将目标文件归档为静态库。

$(DIR_OUTPUT)/%$(TYPE_OBJ) : %$(TYPE_SRC)
	$(CC) $(CFLAGS) -o $@ -c $(filter %$(TYPE_SRC), $^)
# 规则:编译源文件生成目标文件。

$(DIR_OUTPUT)/%$(TYPE_DEP) : %$(TYPE_SRC)
	@echo "Creating $@ ..."
	@set -e; \
	$(CC) $(CFLAGS) -MM -E $(filter %$(TYPE_SRC), $^) | sed 's,\(.*\)\.o[ :]*,$(DIR_OUTPUT)/\1$(TYPE_OBJ) $@ : ,g' > $@
# 规则:生成依赖文件。通过gcc的-MM选项生成依赖信息,并使用sed命令调整格式,将生成的依赖信息重定向到依赖文件中。

  • module/makefile:
.PHONY : all
# 声明all为伪目标(PHONY),避免与同名文件冲突。

DIR_BUILD := /home/wx/uuxiang/makefile/20_22/02/build
DIR_MODULE_INC := /home/wx/uuxiang/makefile/20_22/02/module/inc
DIR_COMMON_INC := /home/wx/uuxiang/makefile/20_22/02/common/inc
# 定义编译输出目录和公共头文件目录的路径。

DIR_SRC := src
DIR_INC := inc
# 定义源文件目录和头文件目录的路径。

TYPE_INC := .h
TYPE_SRC := .c
TYPE_OBJ := .o
TYPE_DEP := .dep
# 定义不同文件类型的扩展名,包括头文件(.h)、源文件(.c)、目标文件(.o)、依赖文件(.dep)。

AR := ar
ARFLAGS := crs
# 定义生成静态库的工具和其使用的参数。

CC := gcc
CFLAGS := -I$(DIR_INC) -I$(DIR_MODULE_INC) -I$(DIR_COMMON_INC)
# 定义编译器为gcc,CFLAGS包含了头文件搜索路径。

ifeq ($(DEBUG),true)
CFLAGS += -g
endif
# 如果变量DEBUG为true,则添加-g选项用于生成调试信息。

MODULE := $(realpath .)
MODULE := $(notdir $(MODULE))
DIR_OUTPUT := $(addprefix $(DIR_BUILD)/, $(MODULE))
# 获取当前目录的绝对路径,将模块名设置为当前目录的名称,并定义模块的输出目录。

OUTPUT := $(MODULE).a
OUTPUT := $(addprefix $(DIR_BUILD)/, $(OUTPUT))
# 定义最终生成的静态库文件名,并设置输出路径。

SRCS := $(wildcard $(DIR_SRC)/*$(TYPE_SRC))
OBJS := $(SRCS:$(TYPE_SRC)=$(TYPE_OBJ))
OBJS := $(patsubst $(DIR_SRC)/%, $(DIR_OUTPUT)/%, $(OBJS))
DEPS := $(SRCS:$(TYPE_SRC)=$(TYPE_DEP))
DEPS := $(patsubst $(DIR_SRC)/%, $(DIR_OUTPUT)/%, $(DEPS))
# 查找源文件,生成目标文件和依赖文件列表,并将它们从源目录映射到输出目录。

vpath %$(TYPE_INC) $(DIR_INC)
vpath %$(TYPE_INC) $(DIR_MODULE_INC)
vpath %$(TYPE_INC) $(DIR_COMMON_INC)
vpath %$(TYPE_SRC) $(DIR_SRC)
# 设置文件搜索路径,vpath用于在指定目录中查找特定类型的文件。

-include $(DEPS)
# 包含所有生成的依赖文件,如果依赖文件不存在则跳过(-表示忽略错误)。

all : $(OUTPUT)
	@echo "Success! Target ==> $(OUTPUT)"
# 定义默认目标all,生成最终的静态库,并打印成功信息。

$(OUTPUT) : $(OBJS)
	$(AR) $(ARFLAGS) $@ $^
# 规则:生成静态库,将目标文件归档为静态库。

$(DIR_OUTPUT)/%$(TYPE_OBJ) : %$(TYPE_SRC)
	$(CC) $(CFLAGS) -o $@ -c $(filter %$(TYPE_SRC), $^)
# 规则:编译源文件生成目标文件。

$(DIR_OUTPUT)/%$(TYPE_DEP) : %$(TYPE_SRC)
	@echo "Creating $@ ..."
	@set -e; \
	$(CC) $(CFLAGS) -MM -E $(filter %$(TYPE_SRC), $^) | sed 's,\(.*\)\.o[ :]*,$(DIR_OUTPUT)/\1$(TYPE_OBJ) $@ : ,g' > $@
# 规则:生成依赖文件。通过gcc的-MM选项生成依赖信息,并使用sed命令调整格式,将生成的依赖信息重定向到依赖文件中。

  • makefile:
.PHONY : all compile link clean rebuild
# 声明伪目标,避免与同名文件冲突。这些伪目标包括all、compile、link、clean和rebuild。

MODULES := common \
           module \
           main
# 定义模块列表,这些模块会被单独编译。

MKDIR := mkdir
RM := rm -fr
# 定义用于创建目录和删除文件/目录的命令。

CC := gcc
LFLAGS := 
# 定义C编译器为gcc,LFLAGS用于链接时的额外参数(当前为空)。

DIR_PROJECT := $(realpath .)
DIR_BUILD := build
DIR_BUILD_SUB := $(addprefix $(DIR_BUILD)/, $(MODULES))
MODULE_LIB := $(addsuffix .a, $(MODULES))
MODULE_LIB := $(addprefix $(DIR_BUILD)/, $(MODULE_LIB))
# 定义项目的根目录,构建目录,以及各模块的构建子目录。
# MODULE_LIB用于存储各模块生成的静态库文件名,并加上构建目录前缀。

APP := app.out
APP := $(addprefix $(DIR_BUILD)/, $(APP))
# 定义最终生成的应用程序文件名,并加上构建目录前缀。

all : compile $(APP)
	@echo "Success! Target ==> $(APP)"
# 默认目标all,先编译模块,再生成最终的应用程序,并输出成功信息。

compile : $(DIR_BUILD) $(DIR_BUILD_SUB)
	@echo "Begin to compile ..."
	@set -e; \
	for dir in $(MODULES); \
	do \
		cd $$dir && $(MAKE) all DEBUG:=$(DEBUG) && cd .. ; \
	done
	@echo "Compile Success!"
# 编译目标compile,首先创建必要的构建目录,然后遍历每个模块目录,执行`make all`命令来编译每个模块,并传递DEBUG变量。最后输出编译成功信息。

link $(APP) : $(MODULE_LIB)
	@echo "Begin to link ..."
	$(CC) -o $(APP) -Xlinker "-(" $^ -Xlinker "-)" $(LFLAGS)
	@echo "Link Success!"
# 链接目标link,依赖于所有模块的静态库。使用gcc进行链接,生成最终的应用程序,并输出链接成功信息。
# `-Xlinker "-(" $^ -Xlinker "-)"` 是为了确保静态库按顺序链接,防止符号丢失。

$(DIR_BUILD) $(DIR_BUILD_SUB) : 
	$(MKDIR) $@
# 目标:创建构建目录和各模块的子目录。

clean : 
	@echo "Begin to clean ..."
	$(RM) $(DIR_BUILD)
	@echo "Clean Success!"
# 清理目标clean,删除整个构建目录,输出清理成功信息。

rebuild : clean all
# 重建目标rebuild,先执行clean再执行all,即先清理再重新编译和链接。

在这里插入图片描述
在这里插入图片描述

4. 优化

4.1 问题 1

  • 所有模块 makefile 中使用的编译路径均为写死的绝对路径,一旦项目文件夹移动,编译必将失败

在这里插入图片描述

  • 解决方案:
    • 在工程 makefile 中获取项目的源码路径
    • 根据项目源码路径:
      拼接得到编译文件夹的路径(DIR_BUILD)
      拼接得到全局包含路径(DIR_COMMON_INC)
    • 通过定义命令行变量将路径传递给模块 makefile
  • 这样使得工程文件夹随意移动

4.2 问题 2

  • 所有模块 makefile 的内容完全相同(复制粘贴)
  • 当模块 makefile 需要移动时,将涉及多处相同的改动
  • 解决方案:
    • 将模块 makefile 拆分为两个模板文件
      mkd-cfg.mk:定义可能改变的变量
      mod-rule.mk:定义相对稳定的变量和规则
    • 默认情况下,模块 makefile 复用模板文件实现功能(include)
      模块makefile怎么知道模板文件的具体位置?

4.3 关键问题

  • 模块 makefile 如何知道模板文件的具体位置?
  • 解决方案:通过命令行变量进行模板文件位置的传递

4.4 工程 makefile 的重构

  • 拆分命令变量,项目变量,以及其它变量和规则到不同文件
    • cmd-cfg.mk:定义命令相关的变量
    • pro-cfg.mk:定义项目变量以及编译路径变量等
    • pro-rule.mk:定义其它变量和规则
    • 最后的工程 makefile 通过包含拆分的文件构成(include)

20-22 - 打造专业的编译环境/20_22/03

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MES管理系统如何提升产品质量与可追溯性

在智能制造的新纪元里,MES管理系统正逐步崭露头角,成为驱动制造业转型升级的幕后英雄。MES管理系统不仅重新定义了生产过程的管理与控制方式,还以数据为驱动,引领着制造业迈向更高效、更智能、更可持续的未来。 智能制造的赋能者&…

U-Mail垃圾邮件过滤网关‍是如何过滤垃圾邮件的?

随着互联网的普及,垃圾邮件已经成为计算机网络安全的又一个公害。因此,反垃圾邮件已经成为互联网应用研究中一个重要课题。为了防止垃圾邮件首先要学会保护自己的邮件地址,避免在网上随意登记和使用邮件地址,预防垃圾邮件骚扰。其…

ALV Tree Control树控件

ALV Tree Control CL_GUI_ALV_TREE 效果: 自定义函数调用,在函数里画屏幕定义树控件显示 FUNCTION ZFI_YSYWLX_HELP_NEW . *"---------------------------------------------------------------------- *"*"本地接口: *&qu…

zset使用lua实现取最高分数中的随机成员

zset使用lua实现取最高分数中的随机成员 这种场景适用队列中不想要先入先出、先入后出因为zset的命令都是带有排序功能的,所以取值时要不从大到小要不从小到大所以我使用lua实现随机取成员使用lua是因为可以保持原子性在执行过程中Lua脚本是不会被其他命令或请求打…

C语言典型例题59

《C程序设计教程(第四版)——谭浩强》 题目: 例题4.11 译密码。为使电文保密,往往按一定规律将其转换为密码,收报人再按约定的规律将其译回原文。 例如,可以按以下规律将电文变为密码: 将字母A…

我不是非酋之-天空概率

最近入坑了dnf手游,染上了合天空,大黑蛋子一个,突发奇想,模拟下合天空概率,看看是否真的有20%。 梳理代码逻辑如下: 不考虑礼包和其他东西 条件: 合成概率20%,每次需要2个装扮和一个…

格式化的磁盘数据能恢复吗?五个看完即可掌握的恢复方法

磁盘是大多数电子设备都必须具有的一个存储介质,在平时使用的过程中,很多用户为了途一时的方便,经常会需要一键将磁盘里面的数据执行格式化,以实现清空或者删除的情况。然而,在执行完具体的操作后,过了一段…

Vue(十三) 路由、路由嵌套、query、param传参、propos、replace属性。编程式路由导航,特有的生命周期函数,路由守卫

文章目录 路由1. 基本使用2. 多级(嵌套)路由3. 路由query传参4. 命名路由5. 路由param传参6. propos属性7. replace属性8. 编程式路由导航9. 缓存路由组件10. actived,deactived生命周期函数11. 路由守卫1、全局路由2、独享路由3、组件内路由守卫 12. 路由器工作的两…

干货含源码!如何用Java后端操作Docker(命令行篇)

目录 干货含源码!如何用Java后端操作Docker(命令行篇) 一、为什么要用后端程序操作Docker 二、安装Docker 1、安装Docker 2、启动Docker 三、使用Java后端操作docker 1、构建docker镜像并生成容器 2、执行完毕后删除容器和镜像 3、在…

怎么删除谷歌浏览器的下载记录

定期删除谷歌浏览器的下载记录,对于保护个人隐私和提升浏览器性能都非常的重要。为了帮助大家安全的进行谷歌浏览器下载记录的清除,本文为大家分享了实用的操作方法,一起来看看吧。 删除谷歌浏览器下载记录的原因说明 1、保护隐私&#xff1…

【算法 动态规划 简单多状态 dp 问题】打家劫舍题型

打家劫舍题型 按摩师 (easy)解题思路代码 打家劫舍II (medium)解题思路代码 删除并获得点数(medium)解题思路代码 按摩师 (easy) 题目链接 该题是打家劫舍的变形 解题思路 状态表示 分析: 注意题目, 对于当天的预约, 可以接受…

车辆远控功能自动化测试方案:打造高效可靠的测试流程

随着汽车逐步走向智能化、网联化,整车的功能已经不再局限于驾驶员在车内进行本地操作。在远离车辆时,驾驶员也可以通过手机APP下发控制指令来实现对车辆的远程控制。 近几年,伴随远控功能项不断增多,其功能逻辑也越来越复杂&…

python开发--信息的增删改

部门信息的增删改 1. 增加 点击新建部门 跳转到新建部门页面:http://127.0.0.1:8000/depart/add/ 在views.py里面增加如下代码,可以将用户输入的信息添加到数据库中 def depart_add(request):if request.method GET:return render(request, depart…

STL之my_list容器

前言:各位老铁好久不见了,今天分享的知识是自己实现一个简单的list容器,为什么我先跳过vector容器的自我实现呢?我个人觉得vector相对于list的自我实现简单一点,所以今天先分享实现my_list的知识 我们要实现my_list&a…

machine learning - 2

泛化误差 也可以认为是预测时的误差。 训练误差 并不是越小越好,太小会过拟合。 获得测试集合的方法: 1): 2):例如:k-折交叉验证法, 就的每k个数据取一个座位测试集 3&#xff0…

nginx容器映射配置文件后,启动一直报错提示:failed (13: Permission denied)的排查

问题现象: 使用harbor 的install.sh 创建docker-compose之后,出现nginx容器一直重启。 查看日志发现是:配置文件无权限。报错信息如下: Sep 2 16:43:13 172.28.0.1 nginx[1344]: 2024/09/02 08:43:13 [emerg] 1#0: open() “/e…

网站网站建设公司用什么

随着互联网的飞速发展,网站已经成为企业的重要门面和宣传工具。为了在网上展示自己的品牌形象和吸引更多的客户,越来越多的企业选择找专业的网站建设公司进行网站建设。那么,网站建设公司主要使用什么技术和方法呢? 首先&#xff…

四、基本电路设计笔记——4.1 DC-DC稳压电路

目录 4.1 DC-DC稳压电路 4.1.1 基于MT2492的DC-DC稳压电路 (1)芯片参数 (2)芯片引脚 (3)输出电压设置 4.1.2 基于MT2499A的DC-DC稳压电路 (1)芯片参数 (2&#xf…

【Redis】Redis 主从复制

文章目录 1 前言2 主从模式介绍3 配置 Redis 主从结构3.1 建立复制3.2 断开复制3.3 其他特性3.4 拓扑结构 4 Redis 主从复制原理4.1 复制过程4.2 PSYNC 数据同步4.3 PSYNC 运行流程 5 主从复制流程5.1 全量复制流程5.2 部分复制流程5.3 实时复制流程 1 前言 分布式系统中存在一…

鸿誉移民:定制化移民服务,吹响全球高效率移民的嘹亮号角!

鸿誉移民:定制化移民服务,吹响全球高效率移民的嘹亮号角! 作为国内知名海外移民服务机构,鸿誉移民历经多年行业沉淀,拥有着极其丰富的移民咨询以及移民办理经验,并以咨询及时精准,签证快捷、通…