在 makefile 中我们什么时候使用 .o 文件?

use*_*508 0 compiling c make gcc

在下面的make文件中,添加.o文件而不是.c文件有什么意义?

CC=gcc
CFLAGS=-I.

hellomake: hellomake.o hellofunc.o
     $(CC) -o hellomake hellomake.o hellofunc.o -I.
Run Code Online (Sandbox Code Playgroud)

Eli*_*gem 6

更新:

对我来说,当使用类比时,很多事情都是不言而喻的,所以也请原谅我在这里添加类比。

如果一本书的页数无论如何都要编号,为什么还要为书费心呢?如果我想看书,我可以直接拿起书页,对吗?如果 .c + .h 文件是单独的页面,那么 .o 文件就像章节。它们是某种独立的机器代码实体,但不必(而且通常没有)本身就有意义。它们是需要传递给链接器的预编译实体,链接器会将它们放在一起,这样一切都有意义。

就像一堆松散的论文或章节很少有任何意义,除非有人将它们按正确的顺序放在一起。但是将松散的文件放在一起很无聊,而且很耗时,并且会留下很大的错误空间。将一些章节放在一起更快、更容易、更安全。在这方面,目标文件只是......必要的。

简而言之,如果您要编写并编译程序,例如 MySQL 客户端,则必须将 DB 查询代码与处理前端/GUI 内容的代码分开编写。

如果您将它们全部放在一个文件中,那将是一团糟,而且您也无法重用任何代码。至少,不容易。

例如,通过分离代码的 GUI 层,您可以使用您编写的为下一个程序绘制按钮的代码。

此外,通过根据代码的作用分离代码,您可以避免任何可能的错误,因为名称冲突、无效的 typedef 等。此外:谁会在他们的头脑中看到这段代码并认为这是正常的

gtk_window_set_default_size(GTK_WINDOW(window), 230, 150);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_icon(GTK_WINDOW(window), create_pixbuf("icon.png"));
MYSQL *connection = mysql_init(NULL);
if (connection == NULL) exit(EXIT_FAILURE);
Run Code Online (Sandbox Code Playgroud)

混合后端和 GUI 的东西在各方面都很糟糕。

  • 代码必须结构化以减少错误的变化,简化和加速开发并提高代码的可重用性。

所以这就是为什么我们根据它应该做什么来分离代码。相同的逻辑可以应用于编译代码。因为分离允许您编译程序的段,所以您可以一次又一次地使用这些段,而不必再次重新编译该批次。

  • 使用 o-files 更快,因为我们可以重用我们编写的内容,而无需重新编译它。

在编译时,尤其是在开发/调试/测试代码时,您希望编译阶段很快。通过保留那些未更改的源文件的 .o 文件,只编译您更改的内容很容易。

再加上添加新功能和(考虑这里的 SVC 系统)以这种方式使用子模块要容易得多,您很快就会意识到,如果不这样组织您的工作,您将不得不发疯。

因此,从开发的角度来看,一点点地构建您的程序,然后将它们链接在一起是最好的方法。

  • 调试更容易,如果链接器抛出错误,你知道去哪里找。

当你编译不包含main函数的代码时,你会得到一个错误。事实上,是链接器产生了这个错误。它查看需要链接在一起以创建二进制文件的所有对象,以搜索您的程序所依赖的所有符号
如果main在任何地方都找不到符号,则程序没有起点,因此无法运行。编译器不需要这个函数,因为将代码编译为非独立的 .o 文件是很高兴的。链接器吐出程序,如果main就找不到函数,就不能继续了。

这意味着链接器实际上会通知您缺少哪些函数(可能是错别字),并且因为您首先将代码编译成对象,以便稍后链接在一起,然后您可以看到,仅通过其中的错误模块您应该寻找修复那个丢失的符号,或者哪个依赖项没有正确链接。

那么,一旦您的代码准备好发布,为什么不更改 makefile 呢?

  • 何苦?
  • 如果您的 makefile 是公开的,那么您可能正在处理一个开源项目。一天之内,对代码感兴趣的每个人都会制作自己的 makefile(这对开源项目没有帮助 -> 标准很重要)。一次性编译批次不会使您的项目对开源友好:没有人可以修补给定的 1 段代码,而不必不断重新编译批次。你为什么要花时间和精力去惹恼可能的贡献者?
  • Makefile 应该被视为代码:为了您和我们的利益,让它们易于阅读。如果您有 5 个子模块,每个子模块都取决于必须传递给 gcc 的 5 到 10 个参数,那么您的 makefile 将看起来一团糟。
  • 当您编译的程序需要链接的文件超过 2 个时,谁能说所有文件都必须以相同的方式相互链接?不仅如此,谁能说您希望所有文件本身都以相同的方式编译?根据版本的不同,或者如果您正在处理一个支持扩展的项目,您可能会发现自己必须以不同的方式以给定的顺序编译源代码的各个部分。
  • 如果它是一个开源项目:如果您的代码构建在您的系统上,则意味着您已准备好所有依赖项。其他人可能不会。一个 make 文件(理想情况下,你也有一个配置文件)可以告诉人们渴望尝试你所做的一切,他们缺少什么依赖项

以这个为例:

MAIN   ===> API ==> MySQL
  |          /\      /
  |  --->  std*  ---/
  v        /
 GUI -----/
  |
  v
 GTK+
Run Code Online (Sandbox Code Playgroud)

您的 makefile 将完全独立地编译 API + MySQL 代码、GUI + GTK 代码,然后将它们全部链接到上面给出的示例的“MAIN”上。一口气编译所有内容是谎言。如果 API 编写得很好,那么是否有 GUI 应该无关紧要。

就像这里的情况一样,您经常发现自己链接到共享依赖项。尽管我找不到 ATM 示例,但链接器可能需要您的帮助才能正确解决依赖关系。仅此一项就迫使您分步编译,从而编写生成文件。

然后是静态依赖。我只会给你留下这个链接,它在解释这个方面做得很好,一旦你通读了它,你应该很好地理解为什么 makefile 如此有价值。