2024年最全Makefile,最新C C++大厂高频面试题

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!


这将把 `value` 追加到名为 `variable` 的变量的末尾。


1. `:=`(覆盖赋值):使用 `:=` 运算符可以将一个值赋给一个变量,并且这个赋值操作会立即生效。例如:



variable := value


这将把 `value` 赋给名为 `variable` 的变量,并且它会立即生效。



=和:=有啥区别

在Makefile中,= 和 := 是两种不同的赋值操作符,它们在变量赋值时有一些区别。

  1. =(简单赋值):使用 = 运算符进行赋值时,变量的值会在变量被使用时动态地展开。这意味着,如果变量的值在后续的使用中发生了变化,那么这个变化会反映在变量的展开值中。例如:
variable = initial_value
target:
    echo $(variable)
variable = updated_value

当目标 target 被执行时,$(variable) 的值将是 updated_value,而不是 initial_value。这是因为变量的展开是动态的。

2. `:=`(覆盖赋值):使用 `:=` 运算符进行赋值时,变量的值在赋值时就会被展开,并且展开后的值将被固定。这意味着,无论后续对变量的赋值如何变化,变量的展开值都不会受到影响。例如: 

variable := initial_value
target:
echo $(variable)
variable := updated_value

当目标 `target` 被执行时,`$(variable)` 的值将始终是 `initial_value`,而不会受到后续赋值的影响。这是因为变量的展开是静态的。

总结起来,= 运算符进行简单赋值,展开值是动态的;而 := 运算符进行覆盖赋值,展开值是静态的。根据具体的需求和使用场景,可以选择适合的赋值操作符来定义变量。

隐含规则

符号含义
%.o任意的.o文件
*.o所有的.o文件

通配符

符号含义
$^所有依赖文件
$@所有目标文件
$<所有依赖文件的第一个文件

makefile函数

这里只对我用到的做总结,持续更新

dir

在Makefile中,dir 函数是一个用于提取路径的内置函数。它可以从文件名或文件路径中提取目录部分。

dir 函数的语法如下:

dir names...

其中 names 是一个或多个文件名或文件路径。

下面是一些示例,说明如何使用 dir 函数:

  1. 提取文件路径的目录部分:
filepath := /path/to/file.txt
dirpath := $(dir $(filepath))

在这个例子中,$(dir $(filepath)) 将返回 /path/to/,即文件路径 /path/to/file.txt 的目录部分。

  1. 提取文件名的目录部分:
filename := myfile.txt
dirpath := $(dir $(filename))

在这个例子中,$(dir $(filename)) 将返回空字符串,因为文件名 myfile.txt 没有目录部分,它位于当前目录下。

  1. 处理多个文件名:
files := file1.txt file2.txt file3.txt
dirs := $(dir $(files))

在这个例子中,$(dir $(files)) 将返回 file1/ file2/ file3/,即每个文件名的目录部分。

patsubst

在Makefile中,patsubst 函数是一个用于模式替换的内置函数。它可以根据模式匹配和替换的规则对文本进行替换操作。

patsubst 函数的语法如下:

patsubst pattern,replacement,text

其中,pattern 是要匹配的模式,replacement 是替换的规则,text 是需要进行替换操作的文本。

下面是一些示例,说明如何使用 patsubst 函数:

  1. 替换文件后缀:
files := file1.txt file2.cpp file3.c
new_files := $(patsubst %.txt,%.csv,$(files))

在这个例子中,$(patsubst %.txt,%.csv,$(files)) 将把 files 列表中以 .txt 结尾的文件替换为相应的以 .csv 结尾的文件。结果将是 file1.csv file2.cpp file3.c

  1. 替换路径中的目录:
files := dir1/file1.txt dir2/file2.txt dir3/file3.txt
new_files := $(patsubst dir%,newdir%,$(files))

在这个例子中,$(patsubst dir%,newdir%,$(files)) 将把 files 列表中以 dir 开头的路径替换为相应的以 newdir 开头的路径。结果将是 newdir1/file1.txt newdir2/file2.txt newdir3/file3.txt

patsubst 函数可用于执行基于模式的替换操作,以生成新的文本。它在Makefile中非常有用,特别是在处理文件名、路径或其他具有规律的文本时。

lastword

在Makefile中,lastword 是一个用于提取字符串中的最后一个单词的函数。

lastword 函数的语法如下:

lastword text

其中,text 是一个字符串。

下面是一个示例,说明如何使用 lastword 函数:

variable := This is a sentence
last := $(lastword $(variable))

在这个例子中,$(lastword $(variable)) 将返回字符串 This is a sentence 中的最后一个单词,即 sentence。该值将被赋给变量 last

lastword 函数在Makefile中非常有用,特别是在需要提取字符串中最后一个单词的场景下。它可以帮助您对字符串进行处理和操作。请注意,lastword 函数将字符串以空格作为分隔符,因此如果字符串中包含多个单词,它将提取最后一个单词。

wildcard

1、wildcard —— 获取指定格式的文件列表
原型(不同格式之间使用空格隔开)

$(wildcard <pattern…>)
示例:获取当前目录下所有的 .cpp 文件和 test目录下所有的 .cpp 文件

$(wildcard *.cpp test/*.cpp)

findstring

findstring函数用于在给定的字符串中搜索指定的子字符串,返回子字符串在字符串中的位置。

strip函数 strip函数对BACKSLASH进行处理,去除可能存在的空白字符。

在makefile中,subst是一个字符串替换函数,用于替换字符串中的指定子串。

语法如下:

$(subst from,to,text)

其中,from表示要替换的子串,to表示替换后的子串,text表示要进行替换操作的原始字符串。

subst函数会在text字符串中查找所有与from子串匹配的内容,并将其替换为to子串。

举个案例分析

cur_dir := $(dir $(lastword $(MAKEFILE_LIST)))

obj-y := $(patsubst ( c u r _ d i r ) (cur\_dir)%,%, (cur_dir)(wildcard $(cur_dir)*.c $(cur_dir)*.cpp $(cur_dir)*.s))

这两句话什么意思呢?

cur\_dir := $(dir $(lastword $(MAKEFILE\_LIST))) 这行代码的作用是将当前Makefile所在的目录路径赋值给变量 cur\_dir

obj-y := $(patsubst ( c u r _ d i r ) (cur\_dir)%,%, (cur_dir)(wildcard $(cur_dir)*.c $(cur_dir)*.cpp $(cur_dir)*.s))

这个代码片段是一个Makefile规则,用于将指定目录(cur_dir)下的所有以.c、.cpp和.S为扩展名的文件添加到目标列表(obj-y)中。

具体解释如下:

  • wildcard $(cur\_dir)\*.c $(cur\_dir)\*.cpp $(cur\_dir)\*.S:使用wildcard函数获取指定目录下所有以.c、.cpp和.S为扩展名的文件路径列表。
  • $(patsubst $(cur\_dir)%,%,$(wildcard $(cur\_dir)\*.c $(cur\_dir)\*.cpp $(cur\_dir)\*.S)):使用patsubst函数对文件路径列表进行模式替换,将路径中的$(cur_dir)部分替换为空字符串,得到相对于当前目录的文件路径列表。
  • obj-y := $(patsubst $(cur\_dir)%,%,$(wildcard $(cur\_dir)\*.c $(cur\_dir)\*.cpp $(cur\_dir)\*.S)):将替换后的文件路径列表赋值给目标变量obj-y。

这个规则常用于构建系统中,用于自动化地将指定目录下的源文件添加到编译目标中。

假设当前目录(cur_dir)下有以下文件:

  • main.c
  • utils.cpp
  • startup.S

应用上述Makefile规则后,以下是变量obj-y的值:

obj-y := main.c utils.cpp startup.S

该规则将这三个文件添加到目标变量obj-y中,以供后续构建过程使用。这意味着在编译过程中,这些文件将被编译为目标文件,并最终链接到可执行文件中。

obj-y := $(obj-y:.c=.o)
obj-y := $(obj-y:.cpp=.o)
obj-y := $(obj-y:.S=.o)

$(obj-y:.c=.o)是一个Makefile中的字符串替换表达式,用于将目标变量obj-y中所有以.c为扩展名的文件替换为以.o为扩展名的文件。

特殊变量

MAKEFILE_LIST

MAKEFILE_LIST 是一个特殊变量,在Makefile中用于表示当前被解析的 Makefile 文件列表。

MAKEFILE_LIST 是一个包含了正在被处理的 Makefile 文件名的列表,按照它们被解析的顺序排列。列表中的第一个元素是顶层的 Makefile 文件,后续元素是被包含或递归调用的其他 Makefile 文件。

您可以使用 $(MAKEFILE_LIST) 来引用 MAKEFILE_LIST 变量的值。

以下是一个示例,展示如何使用 MAKEFILE_LIST

# Makefile1
include Makefile2

target:
    @echo "Current Makefile: $(lastword $(MAKEFILE_LIST))"

# Makefile2
include Makefile3

# Makefile3
target:
    @echo "Current Makefile: $(lastword $(MAKEFILE_LIST))"

在这个示例中,假设您运行 make target 命令。Makefile1 通过 include 包含了 Makefile2,然后 Makefile2 又通过 include 包含了 Makefile3。

当执行 make target 时,输出将会是:

Current Makefile: Makefile3
Current Makefile: Makefile1

这是因为 MAKEFILE_LIST 变量存储了当前正在被解析的 Makefile 文件列表,按照它们被解析的顺序排列。因此,在 Makefile3 中,$(lastword $(MAKEFILE_LIST)) 获取到的就是 Makefile3 的文件名;而在 Makefile1 中,$(lastword $(MAKEFILE_LIST)) 获取到的是 Makefile1 的文件名。

通过使用 MAKEFILE_LIST,您可以在 Makefile 中了解当前正在处理的文件是哪个,以便进行相应的操作和判断。

条件语句

条件语句的语法如下:

ifeq (condition, value)
    # 条件为真时执行的代码
else
    # 条件为假时执行的代码
endif

举个例子

ifeq ($(IBRT),1)

在这个示例中,ifeq ($(IBRT),1) 表示如果变量 IBRT 的值等于 1,则执行其后的代码块。如果条件为真,则会执行 ifeq 和 else 之间的代码块。如果条件为假,则会执行 else 和 endif 之间的代码块。

编译器选项

-I

在编译器中,-I是一个选项,用于指定包含文件的搜索路径。它告诉编译器在指定的路径中查找头文件。

-D

在编译器中,-D是一个选项,用于定义预处理器宏(Preprocessor Macro)。它可以在编译过程中设置宏定义,使得在源代码中可以使用这些宏。

例如,假设需要定义一个名为DEBUG_MODE的宏,并在源代码中根据该宏执行不同的操作。可以使用-D选项设置该宏的定义。以下是一个示例:

gcc -DDEBUG_MODE source.c

在这个命令中,-DDEBUG_MODE告诉编译器在预处理阶段定义了DEBUG_MODE宏。源代码文件source.c中可以使用#ifdef#ifndef等条件编译指令来检查并根据宏的定义执行不同的代码。

在Makefile中,通过在subdir-ccflags-y变量中使用-D选项,可以设置预处理器宏的定义。例如:

subdir-ccflags-y += -DDEBUG_MODE

这样,在执行编译操作时,编译器将使用-DDEBUG_MODE选项来设置DEBUG_MODE宏的定义。这样做可以在源代码中使用条件编译指令来根据宏的定义执行不同的代码逻辑。

makefile Platform and shell detection

# 检测操作系统平台
ifeq ($(OS),Windows_NT)
    # Windows平台
    PLATFORM = Windows
else
    UNAME_S := $(shell uname -s)
    ifeq ($(UNAME_S),Linux)
        # Linux平台
        PLATFORM = Linux
    endif
    ifeq ($(UNAME_S),Darwin)
        # macOS平台
        PLATFORM = macOS
    endif
endif

# 打印检测到的平台
$(info Detected platform: $(PLATFORM))

# 检测Shell类型
SHELL_TYPE := $(shell echo $$0)

ifeq ($(findstring bash,$(SHELL_TYPE)),bash)
    # Bash Shell
    SHELL_NAME = Bash
else ifeq ($(findstring zsh,$(SHELL_TYPE)),zsh)
    # Zsh Shell
    SHELL_NAME = Zsh
else ifeq ($(findstring fish,$(SHELL_TYPE)),fish)
    # Fish Shell
    SHELL_NAME = Fish
else
    # 其他Shell
    SHELL_NAME = Unknown
endif

# 打印检测到的Shell类型
$(info Detected shell: $(SHELL_NAME))

$(call)是Makefile中的一个内置函数,用于调用自定义函数或预定义的函数。

Unix-style Shell(类Unix Shell)是一种命令行解释器,为用户提供与操作系统交互的接口。它是类Unix操作系统中的一部分,如Linux、macOS等。

Unix-style Shell 提供了一个命令行界面,用户可以在其中输入命令并与操作系统进行交互。用户可以执行各种命令,操作文件和目录,管理进程,配置系统等。

常见的Unix-style Shell 包括:

  • Bourne Shell(sh):是早期Unix系统上最基本和常见的Shell。它是许多其他Shell的基础。
  • Bourne-Again Shell(bash):是Bourne Shell的增强版本,为许多现代Unix-like系统的默认Shell。它提供了更多功能和改进的用户体验。
  • C Shell(csh):以C语言的语法为基础,提供了一些额外的功能和语法糖。
  • Korn Shell(ksh):融合了Bourne Shell和C Shell的特性,并提供了更多的功能,包括命令行编辑和命令历史等。
  • Z Shell(zsh):是一种功能强大的Shell,具有高级的自动补全、主题和插件系统等特性。

在Makefile中,SHELL是一个特殊的变量,用于指定用于执行命令的默认Shell解释器。

当我们在Makefile中添加MAKEFLAGS += -rR,禁用了Make的内置规则和推导规则后,我们可以更加灵活地自定义构建过程。(有待补充)

在Makefile中,quiet变量的值是根据是否启用静默模式来确定的。如果启用了静默模式(通过make命令中的-s参数),quiet变量的值将为silent\_,否则将为空字符串。

quiet变量通常用于控制构建规则中命令的输出行为。通过在命令前面添加$(quiet),可以根据静默模式的状态来决定是否显示命令。

例如,假设我们有以下的Makefile:

 # Makefile


quiet = @


all:  
     $(quiet)echo "Building target..."  
     $(quiet)gcc -o program main.c  
     $(quiet)echo "Build completed."


clean:  
     $(quiet)echo "Cleaning target..."  
     $(quiet)rm program  

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

令。

例如,假设我们有以下的Makefile:

 # Makefile


quiet = @


all:  
     $(quiet)echo "Building target..."  
     $(quiet)gcc -o program main.c  
     $(quiet)echo "Build completed."


clean:  
     $(quiet)echo "Cleaning target..."  
     $(quiet)rm program  

[外链图片转存中…(img-EdBoNYnf-1715604631157)]
[外链图片转存中…(img-oQnXElGt-1715604631158)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 21
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值