一文带你从零到实战,学会gcc和Makefile,多文件编译神器的使用与编写

news2024/11/15 6:40:16

目录: 

目录: 

一、什么是Makefile

1.1 makefile的作用:

1.2 makefile的基本组成:

二、Linux编译过程:

2.1 linux编译过程:

2.1.1 预处理(Preprocessing)

2.1.2 编译(Compilation)

2.1.3 汇编(Assembly)

2.1.4 链接(Linking)

2.1.5 过程总结

2.2 脚本语言: Makefile

2.2.1 第一层显式规则:

2.2.2 第二层变量:

2.2.3 第三层隐含规则:

2.2.4 第四层通配符:

2.2.5 第五层函数:

1. $(foreach var,list,text)

2.  $(wildcard pattern)

3. $(wildcard pattern)

 4. $(patsubst pattern,replacement,$(files2))

 三、Makefile实操:

3.1 支持头文件依赖

3.2 添加CFLAGS

四、通用Makefile的使用:

4.1 本程序的Makefile分为3类:

4.2 各级子目录的Makefile:

4.3 顶层目录的Makefile:

4.4 顶层目录的Makefile.build:

4.5 怎么使用这套Makefile:

4.6 顶层目录

4.7 Makefile.build

4.8 子目录 

五、通用 Makefile 的解析:

5.1 通用 Makefile 的设计思想

5.2 顶层 Makefile 中把顶层目录的 built-in.o 链接成 APP:


一、什么是Makefile

Makefile 是一个用来管理自动化构建过程的文件,主要用于编译和构建项目。在软件开发中,尤其是在编译大型项目时,手动编译每个源文件并将它们链接成一个可执行文件或库可能会非常繁琐。Makefile 提供了一种自动化和高效的方式来管理这些任务。

1.1 makefile的作用:

1. 自动化构建Makefile 允许开发者定义一组规则来说明如何编译和链接程序。这些规则可以通过简单的命令自动执行,从而避免了手动执行每个步骤。

2. 依赖管理Makefile 可以跟踪文件之间的依赖关系。例如,如果一个源文件被修改,Makefile 会只重新编译受影响的文件,而不是重新编译整个项目。这大大提高了编译效率。

3. 可重用性Makefile 通常用于跨多个项目的通用构建规则,开发者可以在不同的项目中重用这些规则。

1.2 makefile的基本组成:

一个简单的 Makefile 通常包含以下部分:

1. 目标(Target):要生成的文件(如可执行文件)。

2. 依赖(Dependencies):生成目标所依赖的文件或其他目标。

3. 命令(Commands):生成目标所需要执行的命令。这些命令通常是 Unix/Linux 的 shell 命令。

二、Linux编译过程:

2.1 linux编译过程:

微观的C/C++编译执行过程。

在 C 语言中,源代码文件(.c 文件)到最终的可执行文件(.exe 文件)的生成过程,通常包括以下几个步骤:预处理汇编编译、和链接

2.1.1 预处理(Preprocessing)

预处理是 C 语言编译的第一个阶段,它的任务是处理以 # 开头的预处理指令,比如 #include#define 等。预处理的结果仍然是 C 语言代码。

  • 宏替换:将代码中的宏(如 #define)替换为定义的内容。例如,#define STR "Jiuxia is a teacher\n" 会把代码中的 STR 替换成 "Jiuxia is a teacher\n"
  • 头文件包含:将头文件中的内容插入到 #include 指令的地方。例如,#include <stdio.h> 会被替换为 stdio.h 中的所有代码。
  • 条件编译:处理诸如 #ifdef#ifndef 这些条件编译指令,确定哪些代码需要保留,哪些需要移除。

预处理后的代码通常是一个扩展的 .i 文件。

2.1.2 编译(Compilation)

编译阶段将经过预处理的 C 代码转换为汇编代码。在这个过程中,编译器会检查代码的语法是否正确,并将高层次的 C 代码翻译成低级的汇编语言代码。

  • 编译器将把每个 C 语句转换成对应的汇编语言指令。
  • 输出的文件通常是 .s 文件(汇编代码文件)。

2.1.3 汇编(Assembly)

汇编阶段将汇编语言代码转换为机器代码。这个阶段的输出是目标文件(Object File),目标文件包含机器代码和一些二进制数据,但它仍然不是一个完整的可执行文件。

  • 汇编器将 .s 文件(汇编代码)转换成二进制的机器代码。
  • 生成的文件通常是 .o(在 Linux 下)或 .obj(在 Windows 下)文件。

2.1.4 链接(Linking)

链接是整个编译过程的最后一个阶段,它的作用是将所有目标文件(以及可能依赖的库)链接成一个可执行文件。

  • 符号解析:确定每个函数和变量的内存地址,确保所有的引用都能正确找到定义。
  • 库链接:如果你的程序使用了外部库(比如标准 C 库 libc),链接器会将这些库链接进来。
  • 生成可执行文件:链接器将所有目标文件和库组合起来,生成最终的可执行文件(.exe 或 .out)。

链接完成后,输出的就是一个可以运行的可执行文件。

2.1.5 过程总结

预处理:处理 #define#include 等预处理指令,输出扩展后的源代码文件(.i 文件)。

编译:将 C 语言代码转换为汇编代码(.s 文件)。

汇编:将汇编代码转换为目标文件(.o 或 .obj 文件)。

链接:将目标文件与外部库链接在一起,生成最终的可执行文件(.exe 或 .out 文件)。

2.2 脚本语言: Makefile

Linux C/C++ 必须要使用的一个编译脚本。

2.2.1 第一层显式规则:

目标文件:依赖文件

[TAB] 指令

第一个目标文件是我的最终目标!!!(递归)

最简单的C语言编程:

hello: hello.o
	gcc hello.o -o hello

hello.o: hello.S
	gcc -c hello.S -o hello.o

hello.S: hello.i
	gcc -S hello.i -o hello.S

hello.i: hello.c
	gcc -E hello.c -o hello.i

使用rm -rf hello.o hello.S hello.i hello对这些编译文件进行删除处理。这种没有目标文件的操作我们可以叫做伪目标。

伪目标:.PHONY:

hello: hello.o
	gcc hello.o -o hello

hello.o: hello.S
	gcc -c hello.S -o hello.o

hello.S: hello.i
	gcc -S hello.i -o hello.S

hello.i: hello.c
	gcc -E hello.c -o hello.i

.PHONY:
clear:
    rm -rf hello.o hello.S hello.i

circle.c circle.h cube.c cube.h main.c main.h

得到可执行文件test省去预编译汇编

test: circle.o cube.o main.o
	gcc circle.o cube.o main.o -o test

circle.o: circle.c
	gcc -c circle.c -o circle.o

cube.o: cube.c
	gcc -c cube.c -o cube.o

main.o: main.c
	gcc -c main.c -o main.o

.PHONY: clearall clear
cleanall:
	rm -rf circle.o cube.o main.o test

clean:
	rm -rf circle.o cube.o main.o

2.2.2 第二层变量:

=(替换)                +=(追加)                :=(恒等于,常量)

$(变量名)(使用变量进行替换)

TAR = test
OBJ = circle.o cube.o main.o
CC := gcc

$(TAR): $(OBJ)
	$(CC)  $(OBJ) -o $(TAR)

circle.o: circle.c
	$(CC) -c circle.c -o circle.o

cube.o: cube.c
	$(CC) -c cube.c -o cube.o

main.o: main.c
	$(CC) -c main.c -o main.o

.PHONY: clearall clear

cleanall:
	rm -rf circle.o cube.o main.o test

clean:
	rm -rf circle.o cube.o main.o

即时变量、延时变量,export:

A := xxx           #    A的值即刻确定,在定义时即确定

B = xxx           #    B的值使用到时才确定

:=        #即时变量

=         #延时变量

?=       #延时变量,如果是第1次定义才起效,如果在前面该变量已定义则忽略这句

+=        #附加,它是即时变量还是延时变量取决于前面的定义

A := $(C)
B = $(C)
C = abc
 
#D = 100ask
D ?= weidongshan
 
all:
        @echo A = $(A)
        @echo B = $(B)
        @echo D = $(D)
 
C += 123

2.2.3 第三层隐含规则:

%.c(任意的.c文件)        %.o(任意的.o文件)         

*.c (所有的.c文件)          *.o(所有的.o文件)

TAR = test
OBJ = circle.o cube.o main.o
CC := gcc

$(TAR): $(OBJ)
	$(CC)  $(OBJ) -o $(TAR)

%.o: %.c
	$(CC) -c %.c -o %.o    

.PHONY: clearall clear

cleanall:
	rm -rf $(OBJ) $(TAR)

clean:
	rm -rf $(OBJ)

2.2.4 第四层通配符:

$^  (所有的依赖文件)

$@(所有的目标文件)

$<  (所有依赖文件的第一个文件)

$*   (不包含扩展名的目标文件名称)

$? (所有时间戳比目标文件晚的依赖文件,并以空格分开)

TAR = test
OBJ = circle.o cube.o main.o
CC := gcc
RMRF := rm -rf

$(TAR): $(OBJ)
	$(CC) $^ -o $@

%.o: %.c
	$(CC) -c $^ -o $@  

.PHONY: clearall clear

cleanall:
	$(RMRF) $(OBJ) $(TAR)

clean:
	$(RMRF) $(OBJ)

2.2.5 第五层函数:

1. $(foreach var,list,text)

简单地说,就是 for each var in list, change it to text。

对 list 中的每一个元素,取出来赋给 var,然后把 var 改为 text 所描述 的形式。

A = a b c
B = $(foreach f, $(A), $(f).o)
 
all:
        @echo B = $(B)

2.  $(wildcard pattern)

pattern 所列出的文件是否存在,把存在的文件都列出来。

$(filter pattern. ..,text)                    #在text中取出符合patten格式的值

$(filter-out pattern.. . ,text)            #在text中取出不符合patten格式的值

C = a b c d/
 
D = $(filter %/, $(C))
E = $(filter-out %/, $(C))
 
all:
        @echo D = $(D)
        @echo E = $(E)

3. $(wildcard pattern)

pattern定义了文件名的格式,wildcard取出其中存在的文件。

files = $(wildcard *.c)    #列出符合后缀是.c的文件都有哪些
 
all:
        @echo files = $(files)

files2 = a.c b.c c.c d.c e.c
files3 = $(wildcard $(files2))
 
all:
        @echo files = $(files)
        @echo files3 = $(files3)

 4. $(patsubst pattern,replacement,$(files2))

从列表中取出每—个值,如果符合pattern,则替换为replacement

files2 = a.c b.c c.c d.c e.c
files3 = $(wildcard $(files2))
 
dep_files = $(patsubst %.c,%.d,$(files2))    #将files2中.c后缀文件替换为.d后缀文件
 
all:
        @echo dep_files = $(dep_files)

 三、Makefile实操:

3.1 支持头文件依赖

a.c

 #include <stdio.h>
 
void func_b();
void func_c();
 
 
int main()
{
        func_b();
        func_c();
        return 0;
}

b.c

#include <stdio.h>
 
void func_b()
{
        printf("This is B\n");
}

c.c 

#include <stdio.h>
#include "c.h"
 
void func_c()
{
        printf("This is C = %d\n", C);
}
 

c.h

 #define C 4

gcc -M c.c                                       //打印出依赖
gcc -M -MF c.d c.c                         //把依赖写入文件c.d
gcc -c -o c.o c.c -MD -MF c.d        //编译c.o,把依赖写入文件c.d

Makefile 

objs = a.o b.o c.o
 
dep_files := $(patsubst %,.%.d, $(objs))
dep_files := $(wildcard $(dep_files))
 
 
test: $(objs)
	gcc -o test $^
 
ifneq ($(dep_files),)                              #将依赖文件添加进去
include $(dep_files)
endif
 
%.o : %.c
	gcc -c -o $@ $< -MD -MF .$@.d        #自动生成依赖文件
 
clean:
	rm *.o test
 
distclean:
	rm $(dep_files)
	
.PHONY: clean	

3.2 添加CFLAGS

c.c

#include <stdio.h>
#include <c.h>
 
void func_c()
{
        printf("This is C = %d\n", C);
}

CFLAGS = -Werror -I.include         #-Werror将所有的警告变为错误
                                                       #-I.include执行当前目录下的include文件

%.o : %.c
        gcc $(CFLAGS) -c -o $@ $< -MD -MF .$@.d       #自动生成依赖文件

Makefile 

objs = a.o b.o c.o
 
dep_files := $(patsubst %,.%.d, $(objs))
dep_files := $(wildcard $(dep_files))
 
CFLAGS = -Werror -I.include                           #-Werror将所有的警告变为错误
                                                      #-I.include执行当前目录下的include文件
 
test: $(objs)
        gcc -o test $^
 
ifneq ($(dep_files),)                                 #将依赖文件添加进去
include $(dep_files)
endif
 
%.o : %.c
        gcc $(CFLAGS) -c -o $@ $< -MD -MF .$@.d       #自动生成依赖文件
 
clean:
        rm *.o test
 
distclean:
        rm $(dep_files)
 
.PHONY: clean

四、通用Makefile的使用:

参考 Linux 内核的 Makefile 编写了一个通用的 Makefile,它可以用来 编译应用程序:

1.支持多个目录、多层目录、多个文件;

2.支持给所有文件设置编译选项;

3.支持给某个目录设置编译选项;

4.支持给某个文件单独设置编译选项;

5.简单、好用。

4.1 本程序的Makefile分为3类:

1. 顶层目录的Makefile

2. 顶层目录的Makefile.build

3. 各级子目录的Makefile

4.2 各级子目录的Makefile:

 它最简单,形式如下:

EXTRA_CFLAGS  := 
CFLAGS_file.o := 

obj-y += file.o
obj-y += subdir/

"obj-y += file.o"  表示把当前目录下的file.c编进程序里。

"obj-y += subdir/" 表示要进入subdir这个子目录下去寻找文件来编进程序里,是哪些文件由subdir目录下的Makefile决定。

"EXTRA_CFLAGS",    它给当前目录下的所有文件(不含其下的子目录)设置额外的编译选项, 可以不设置

  "CFLAGS_xxx.o",    它给当前目录下的xxx.c设置它自己的编译选项, 可以不设置

注意: 

1. "subdir/"中的斜杠"/"不可省略

2. 顶层Makefile中的CFLAGS在编译任意一个.c文件时都会使用

3. CFLAGS  EXTRA_CFLAGS  CFLAGS_xxx.o 三者组成xxx.c的编译选项

4.3 顶层目录的Makefile:

它除了定义obj-y来指定根目录下要编进程序去的文件、子目录外,

主要是定义工具链前缀CROSS_COMPILE,

定义编译参数CFLAGS,

定义链接参数LDFLAGS,

这些参数就是文件中用export导出的各变量。

4.4 顶层目录的Makefile.build:

这是最复杂的部分,它的功能就是把某个目录及它的所有子目录中、需要编进程序去的文件都编译出来,打包为built-in.o

4.5 怎么使用这套Makefile:

1.把顶层Makefile, Makefile.build放入程序的顶层目录

      在各自子目录创建一个空白的Makefile

2.确定编译哪些源文件

修改顶层目录和各自子目录Makefile的obj-y : 

obj-y += xxx.o
obj-y += yyy/

上一句表示要编译当前目录下的xxx.c文件, 下一句表示要编译当前目录下的yyy子目录    

3. 确定编译选项、链接选项

CFLAGS := -Wall -O2 -g
CFLAGS += -I $(shell pwd)/include

 
LDFLAGS := 
 
export CFLAGS LDFLAGS

修改顶层目录Makefile的CFLAGS,这是编译所有.c文件时都要用的编译选项;

修改顶层目录Makefile的LDFLAGS,这是链接最后的应用程序时的链接选项;


4. 修改各自子目录下的Makefile:

EXTRA_CFLAGS := -D DEBUG
CFLAGS_sub3.o := -D DEBUG_SUB3

"EXTRA_CFLAGS",    它给当前目录下的所有文件(不含其下的子目录)设置额外的编译选项, 可以不设置

"CFLAGS_xxx.o",    它给当前目录下的xxx.c设置它自己的编译选项, 可以不设置
   
5. 使用哪个编译器?

CROSS_COMPILE =

修改顶层目录Makefile的CROSS_COMPILE, 用来指定工具链的前缀(比如arm-linux-)
   
6. 确定应用程序的名字:

TARGET := test

修改顶层目录Makefile的TARGET, 这是用来指定编译出来的程序的名字

7. 执行"make"来编译,执行"make clean"来清除,执行"make distclean"来彻底清除。

clean:
	rm -f $(shell find -name "*.o")
	rm -f $(TARGET)

4.6 顶层目录

 Makefile

 
CROSS_COMPILE = 
AS		= $(CROSS_COMPILE)as
LD		= $(CROSS_COMPILE)ld
CC		= $(CROSS_COMPILE)gcc
CPP		= $(CC) -E
AR		= $(CROSS_COMPILE)ar
NM		= $(CROSS_COMPILE)nm
 
STRIP		= $(CROSS_COMPILE)strip
OBJCOPY		= $(CROSS_COMPILE)objcopy
OBJDUMP		= $(CROSS_COMPILE)objdump
 
export AS LD CC CPP AR NM
export STRIP OBJCOPY OBJDUMP
 
CFLAGS := -Wall -O2 -g
CFLAGS += -I $(shell pwd)/include
 
LDFLAGS := 
 
export CFLAGS LDFLAGS
 
TOPDIR := $(shell pwd)
export TOPDIR
 
TARGET := test
 
 
obj-y += main.o
obj-y += sub.o
obj-y += a/
 
 
all : start_recursive_build $(TARGET)
	@echo $(TARGET) has been built!
 
start_recursive_build:
	make -C ./ -f $(TOPDIR)/Makefile.build
 
$(TARGET) : start_recursive_build
	$(CC) -o $(TARGET) built-in.o $(LDFLAGS)
 
clean:
	rm -f $(shell find -name "*.o")
	rm -f $(TARGET)
 
distclean:
	rm -f $(shell find -name "*.o")
	rm -f $(shell find -name "*.d")
	rm -f $(TARGET)

4.7 Makefile.build

PHONY := __build
__build:
 
 
obj-y :=
subdir-y :=
EXTRA_CFLAGS :=
 
include Makefile
 
# obj-y := a.o b.o c/ d/
# $(filter %/, $(obj-y))   : c/ d/
# __subdir-y  : c d
# subdir-y    : c d
__subdir-y	:= $(patsubst %/,%,$(filter %/, $(obj-y)))
subdir-y	+= $(__subdir-y)
 
# c/built-in.o d/built-in.o
subdir_objs := $(foreach f,$(subdir-y),$(f)/built-in.o)
 
# a.o b.o
cur_objs := $(filter-out %/, $(obj-y))
dep_files := $(foreach f,$(cur_objs),.$(f).d)
dep_files := $(wildcard $(dep_files))
 
ifneq ($(dep_files),)
  include $(dep_files)
endif
 
 
PHONY += $(subdir-y)
 
 
__build : $(subdir-y) built-in.o
 
$(subdir-y):
	make -C $@ -f $(TOPDIR)/Makefile.build
 
built-in.o : $(subdir-y) $(cur_objs)
	$(LD) -r -o $@ $(cur_objs) $(subdir_objs)
 
dep_file = .$@.d
 
%.o : %.c
	$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) -Wp,-MD,$(dep_file) -c -o $@ $<
	
.PHONY : $(PHONY)

4.8 子目录 

 
EXTRA_CFLAGS := -D DEBUG
CFLAGS_sub3.o := -D DEBUG_SUB3
 
obj-y += sub2.o 
obj-y += sub3.o 

五、通用 Makefile 的解析:

5.1 通用 Makefile 的设计思想

在 Makefile 文件中确定要编译的文件、目录,比如:

obj-y += main.o
obj-y += a/

Makefile”文件总是被“Makefile.build”包含的。

在 Makefile.build 中设置编译规则,有 3 条编译规则:

怎么编译子目录? 进入子目录编译:

$(subdir-y):
make -C $@ -f $(TOPDIR)/Makefile.build
  1. $(subdir-y) 被展开为所有子目录的列表。
  2. 对于列表中的每个子目录,make 命令都会被调用。
  3. -C $@ 将Make的工作目录切换到当前子目录。
  4. -f $(TOPDIR)/Makefile.build 指定使用位于顶层目录下的 Makefile.build 文件来构建当前子目录。

顶层Makefile会递归地构建所有子目录,每个子目录都会使用自己的Makefile来构建其目标文件。 

怎么编译当前目录中的文件?

        规则部分:%.o : %.c

  • %.o:这是一个模式规则的模式部分,表示所有以.o结尾的目标文件。%是一个占位符,代表目标文件名中除扩展名以外的部分。
  • : %.c:这是模式规则的结果部分,表示所有以.c结尾的源文件都会生成对应的目标文件。同样,%.c中的%是一个占位符,代表源文件名中除扩展名以外的部分。
  • 这条规则的意思是,对于任何以.c结尾的源文件,都会生成一个与之对应且以.o结尾的目标文件。

    命令部分:

  • $(CC):这是一个变量,代表编译器的名称。通常,这个变量被设置为gccclang等。
  • $(CFLAGS):这是一个变量,包含了编译器的基本编译选项。
  • $(EXTRA_CFLAGS):这是一个变量,包含了额外的编译器选项,可能由项目配置或特定文件指定。
  • $(CFLAGS_$@):这是一个变量,其中$@是当前目标文件的名称。它允许为不同的目标文件指定不同的编译器选项。
  • -Wp,-MD,$(dep_file):这是一个用于生成依赖文件的选项。-MD告诉编译器生成依赖文件,而$(dep_file)是依赖文件的名称。
  • -c:这是一个编译选项,指示编译器只进行编译,不进行链接。
  • -o $@:这个选项告诉编译器将输出文件(目标文件)命名为当前目标文件的名称,即$@
  • $<:这是一个自动变量,代表当前规则中第一个依赖文件的名称,即源文件。

使用指定的编译器($(CC))和一系列编译选项($(CFLAGS)$(EXTRA_CFLAGS)$(CFLAGS_$@)),编译名为$<的源文件(.c),生成一个名为$@的目标文件(.o),并且同时生成一个依赖文件($(dep_file)),其中包含了源文件和目标文件之间的依赖关系。 

当前目录下的.o 和子目录下的 built-in.o 要打包起来:

built-in.o : $(cur_objs) $(subdir_objs)
$(LD) -r -o $@ $^

        规则部分:built-in.o : $(cur_objs) $(subdir_objs)

  • built-in.o:这是目标文件,表示最终生成的文件名。
  • : $(cur_objs) $(subdir_objs):冒号后面的部分是依赖列表,表示生成built-in.o所需的依赖项。这里,$(cur_objs)$(subdir_objs)是变量,它们分别包含当前目录和子目录中所有对象文件的列表。

这条规则的意思是,built-in.o这个目标文件依赖于$(cur_objs)$(subdir_objs)中列出的所有对象文件。

        命令部分:$(LD) -r -o $@ $^

  • $(LD):这是一个变量,代表链接器的名称。通常,这个变量被设置为gccld等。
  • -r:这是一个链接器选项,指示链接器生成可重定位的输出文件。这意味着生成的文件可以在不同的程序中使用,而不是一个独立的可执行文件。
  • -o $@:这个选项告诉链接器将输出文件命名为当前目标文件的名称,即$@
  • $^:这是一个自动变量,代表当前规则中所有依赖项的列表,即$(cur_objs)$(subdir_objs)中列出的所有对象文件。

使用指定的链接器($(LD))和选项(-r),将所有依赖的对象文件($^,即$(cur_objs)$(subdir_objs)中的所有.o文件)链接成一个可重定位的输出文件($@,即built-in.o)。 

5.2 顶层 Makefile 中把顶层目录的 built-in.o 链接成 APP:

$(TARGET) : built-in.o
    $(CC) $(LDFLAGS) -o $(TARGET) built-in.o

5.3 情景演绎

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

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

相关文章

# 移动硬盘误操作制作为启动盘数据恢复问题

移动硬盘误操作制作为启动盘数据恢复问题 文章目录 移动硬盘误操作制作为启动盘数据恢复问题步骤一恢复原有数据 步骤二格式化并重新分区 注意注意先找数据恢复软件恢复数据&#xff0c;把之前移动硬盘或者U盘上的数据恢复到其它地址 步骤一 恢复原有数据 使用一些数据恢复软…

SpringBoot实现Word转PDF/TXT

背景 研发工作中难免会遇到一些奇奇怪怪的需求&#xff0c;就比如最近&#xff0c;客户提了个新需求&#xff1a;上传一个WORD文档&#xff0c;要求通过系统把该文档转换成PDF和TXT。客户的需求是没得商量的&#xff0c;必须实现&#xff01;承载着客户的期望&#xff0c;我开始…

培训第三十七天(Dockerfile与registry)

一、使用Dockerfile创建镜像 Dockerfile文件命令介绍&#xff1a; FORM 指定基础镜像为该镜像的最后修改版本 FROM < img:tag >指定基础镜像为该镜像的⼀个tag版本 MAINTAINER 指定镜像创建者&#xff0c;企业内部不⽤指定&#xff0c;对外发布也可以不指定 RUN 运⾏…

探索Python的Excel力量:openpyxl库的奥秘

文章目录 探索Python的Excel力量&#xff1a;openpyxl库的奥秘背景&#xff1a;为什么选择openpyxl&#xff1f;库简介&#xff1a;openpyxl是什么&#xff1f;安装指南&#xff1a;如何安装openpyxl&#xff1f;快速上手&#xff1a;五个基本函数实战演练&#xff1a;三个应用…

Python实现Word文档转换为图片(JPG、PNG、SVG等常见格式)例子解析

在Python中将Word文档转换为图片&#xff08;如JPG、PNG、SVG等格式&#xff09;可以通过多种库实现&#xff0c;例如Spire.Doc for Python和Aspose.Words for Python。以下是一些详细的代码示例&#xff0c;展示了如何使用这些库完成转换。 使用Spire.Doc for Python转换Word…

网络服务器及IO模型

网络服务器 单循环服务器&#xff1a;服务器在同一时刻只能响应一个客户端的请求 并发服务器模型&#xff1a;服务器在同一时刻可以响应多个客户端的请求 实现TCP并发服务器 1.多进程 2.多线程 3.IO多路复用&#xff1a; 为了解决进程或线程阻塞到某个 I/O 系统调用而出现的…

几种前端处理文本换行展示

文章目录 一、使用 CSS 的 white-space 属性二、使用 CSS 的 word-break 和 word-wrap 属性三、 使用 CSS 的 flex 布局和自动换行四、 使用overflow实现换行 一、使用 CSS 的 white-space 属性 可以将 white-space 属性设置为 pre-wrap 或 pre-line。 pre-wrap&#xff1a;保…

【STM32】FMC

FMC功能与FSMC类似&#xff0c;但比FSMC更强大&#xff0c;但仅在F4 / F7 / H7等高级一点的MCU上支持&#xff0c;F1不支持。虽然我的是F103&#xff0c;但顺便都看了。 大部分图片来源&#xff1a;正点原子HAL库课程 专栏目录&#xff1a;记录自己的嵌入式学习之路-CSDN博客 目…

数据结构学习:栈

栈的简介 栈&#xff08;Stack&#xff09;是限定仅在表尾进行插入租删除操作的线性表。 允许插入和删除的一端称为栈顶(top),另-端称为栈底(bottom) 不含任何数据元素的栈称为空栈 栈又称为后进先出的线性表,简称LIFO结构 栈的插入操作&#xff0c;也叫做进栈&#xff0c…

java JVM

JVM的组成 Java虚拟机&#xff08;JVM&#xff09;是执行Java字节码的运行时环境。它由以下几个主要部分组成&#xff1a; 1. **类加载器&#xff08;ClassLoader&#xff09;**&#xff1a; - 负责加载Java类的字节码到JVM中&#xff0c;并进行链接和初始化。 关于Java的…

C++基础练习

1》提示并输入一个字符串&#xff0c;统计该字符串中字母个数、数字个数、空格个数、其他字符的个数 1 #include<iostream>2 using namespace std;3 4 int main()5 {6 string str1; //定义字符串数据7 cout << "请输入一个字符串>>>" ;8…

三种常用的Word打印部分内容操作技巧

作为打工人&#xff0c;我们经常需要处理Word文档&#xff0c;有时还会遇到只需要打印文档中的部分内容而非整个文档的情况。为了高效地完成这一任务&#xff0c;Word提供了多种灵活的设置方法。本文将详细介绍三种常用的方法来帮助你实现只打印Word文档中的部分内容。 方法一&…

第一周学习--联邦学习

OUC读研--第一周 目录 1、课程学习 2、fedavg的算法实现 关于代码详解 1、client __init__ 方法 local_train 方法 2、server 3、get_dataset 函数定义 数据集加载 MNIST 数据集 CIFAR-10 数据集 返回值 使用示例 4、 main 代码解释 可能的改进点 5、models …

机器学习之 K-means算法的代码实现

K-means 算法简介 K-means 是一种常用的无监督学习算法&#xff0c;主要用于数据聚类。它的主要思想是将数据集中的数据分成 K 个簇&#xff08;Cluster&#xff09;&#xff0c;使得簇内的数据点尽可能相似&#xff0c;而簇间的差异尽可能大。K-means 算法的核心步骤包括初始…

关于类与构造函数继承的小挑战

题目 /* 使用构造函数将电动汽车&#xff08;称为 EV&#xff09;作为 Car 的子 “类 ”来实现。除了品牌和当前速度外&#xff0c;EV 还具有当前电池电量&#xff08;百分比&#xff09;&#xff08;“charge ”属性&#xff09;&#xff1b;实现一个 “chargeBattery ”方法…

Vitis AI 基本认知(Tiny-VGG 项目代码详解)

目录 1. 简介 1.1 Tiny-VGG 1.2 data 目录结构 2. 代码分析 2.1 Import packages 2.2 Dataset 2.3 Train step 2.4 Vali & Test step 2.5 Ceate model 2.6 Compile model 2.6.1 计算 loss 2.6.2 计算平均值 3.6.3 计算准确度 2.7 训练循环 2.7.1 自定义训练…

BLE蓝牙协议详解

BLE蓝牙协议详解 1、BLE协议栈 1、协议栈结构 蓝牙LE协议栈按功能分为三个层&#xff1a;Controller、Host和Application Profiles and Services。 HCI event是按BLE Spec标准设计的&#xff0c;是BLE Controller和Host用来交互的事件&#xff1b;GAP event是BLE host定义的…

环境配置 --- miniconda安装torch报错OSError: [WinError 126] 找不到指定的模块

环境配置 — miniconda安装torch报错OSError: [WinError 126] 找不到指定的模块。 CSDN 原因&#xff1a;fbegmm.dll文件出现问题 解决方案&#xff1a; 使用依赖分析工具https://github.com/lucasg/Dependencies/releases/tag/v1.11.1 检测报错提示的那个dll文件发现哪个文…

Nuclei:开源漏洞扫描器

Nuclei 拥有灵活的模板系统&#xff0c;可以适应各种安全检查。 它可以使用可自定义的模板向多个目标发送请求&#xff0c;确保零误报并实现跨多台主机的快速扫描。 它支持多种协议&#xff0c;包括 TCP、DNS、HTTP、SSL、文件、Whois、Websocket 等。 特征 模板库&#xf…

Java中的定时器(Timer)

目录 一、什么是定时器? 二、标准库中的定时器 三、实现定时器 一、什么是定时器? 定时器就像一个"闹钟"&#xff0c;当它到达设定的时间后&#xff0c;就会执行预定的代码。 例如&#xff0c;我们在TCP的超时重传机制中讲过&#xff0c;如果服务器在规定的时间…