C++ 单独创建对象文件,然后在Makefile中将它们链接在一起的目的是什么?
当使用gcc编译器时,它将在一个步骤中链接和编译。然而,将源文件转换为目标文件,然后在最后链接它们似乎是惯用的做法。对我来说,这似乎没有必要。这不仅会用一堆对象文件把目录弄得乱七八糟,而且会使Makefile变得复杂,因为您可以简单地将所有源文件添加到编译器中。例如,以下是我认为简单的:C++ 单独创建对象文件,然后在Makefile中将它们链接在一起的目的是什么?,c++,makefile,C++,Makefile,当使用gcc编译器时,它将在一个步骤中链接和编译。然而,将源文件转换为目标文件,然后在最后链接它们似乎是惯用的做法。对我来说,这似乎没有必要。这不仅会用一堆对象文件把目录弄得乱七八糟,而且会使Makefile变得复杂,因为您可以简单地将所有源文件添加到编译器中。例如,以下是我认为简单的: .PHONY: all SOURCES = $(wildcard *.cpp) all: default default: g++ $(SOURCES) -o test 它巧妙地变成: g++ m
.PHONY: all
SOURCES = $(wildcard *.cpp)
all: default
default:
g++ $(SOURCES) -o test
它巧妙地变成:
g++ main.cpp test.cpp -o test
然而,使用模式规则的更复杂的makefile会使每个文件的输出变得混乱。例如:
.PHONY: all
SOURCES = $(wildcard *.cpp)
OBJECTS = $(SOURCES:.cpp=.o)
%.o: %.cpp
g++ -c -o $@ $<
all: default
default: $(OBJECTS)
g++ -o test $^
clean:
rm -rf *.o
g++ -c -o main.o main.cpp
g++ -c -o test.o test.cpp
g++ -o test main.o test.o
。假冒:全部
源=$(通配符*.cpp)
对象=$(源:.cpp=.o)
%.o:%.cpp
g++-c-o$@$<
全部:默认值
默认值:$(对象)
g++-o测试$^
清洁:
rm-rf*.o
g++-c-o main.o main.cpp
g++-c-o test.o test.cpp
g++-o测试main.o测试.o
对我来说,这似乎是不必要的、复杂的和容易出错的。那么,这种做法的原因是什么呢?对我来说,在众多原因中,最重要的两个原因是:
- 您可以同时编译多个源文件,以减少生成时间
- 如果更改了一个文件,则只需重新编译该文件,而不是重新编译所有文件
make-j3
(即同时运行3个编译作业,我目前使用的是只有一个双核处理器的备用计算机),编译文件的时间只需少10秒,但如果没有多个编译作业,则无法执行-j3
仅重新编译一个(较大的)模块需要16秒
我知道我宁愿等16秒或35秒 为什么要编写Makefile而不是编写简单的shell脚本?在您认为简单的示例中,您不使用任何特性,甚至可以编写一个简单的shell脚本,它可以理解关键字<代码> Bug 和<代码> Cube < /C> >,就是这样!p> 您实际上是在质疑编写Makefiles而不是shell脚本的意义,我将在回答中对此进行说明 还要注意,在编译和链接三个中等大小文件的简单情况下,任何方法都可能令人满意。因此,我将考虑一般情况,但使用MaMax文件的许多好处仅对大型项目很重要。一旦我们学会了让我们掌握复杂案例的最佳工具,我们也想在简单案例中使用它 shell脚本的过程范式对于编译类作业是错误的 编写Makefile类似于编写shell脚本,只是稍微改变一下透视图。在shell脚本中,我们描述了一个问题的过程性解决方案:我们可以开始使用未定义的函数以非常抽象的术语描述整个过程,并对该描述进行细化,直到达到最基本的描述级别,其中过程只是一个普通的shell命令。在Makefile中,我们不引入任何抽象,而是关注我们想要生成的文件以及如何生成它们。这很好,因为在UNIX中,一切都是一个文件,因此每个处理都由一个程序完成,该程序从输入文件读取输入数据,进行一些计算,并将结果写入一些输出文件 如果我们想计算一些复杂的东西,我们必须使用大量的输入文件,这些文件被程序处理,这些程序的输出被用作其他程序的输入,依此类推,直到我们生成包含结果的最终文件为止。如果我们将准备最终文件的计划转换为shell脚本中的一组过程,那么处理的当前状态是隐式的:计划执行者知道“它在哪里”,因为它正在执行给定的过程,这隐式地保证这样或那样的计算已经完成,也就是说,这些中间文件已经准备好了。现在,哪些数据描述了“计划执行者在哪里” 无害观察描述“计划执行者在哪里”的数据正是已经准备好的中间文件集,这正是我们编写Makefiles时明确显示的数据 这一无伤大雅的观察结果实际上是shell脚本和makefile之间的概念差异,它解释了编译作业和类似作业中makefile优于shell脚本的所有优点。当然,要充分理解这些优势,我们必须编写正确的makefile,这对初学者来说可能很难 Make使中断的任务在其所在位置继续变得容易 当我们用Makefile描述编译作业时,我们可以很容易地中断它并在以后继续它。这是无害观察的结果。类似的效果只有在shell脚本中付出相当大的努力才能实现 Make使得使用项目的多个构建变得容易 您观察到Makefiles会将源树与对象文件混在一起。但是Makefiles实际上可以参数化,以将这些对象文件存储在专用目录中,高级Makefiles允许我们同时拥有多个目录,其中包含具有不同选项的项目的多个构建。(例如,启用了不同的功能或调试版本等)这也是一种无害的观察结果,即Makefiles实际上是围绕中间文件集进行表达的 使构建并行化变得容易 我们可以轻松地并行构建一个程序,因为这是