1

当我编译一个 C 程序时,为方便起见,我一直在最后包含某个标头的源文件。因此,如果 main.c 包含 util.h,则 util.h 将包含 util.c 将使用的所有头文件、概述类型或结构等,然后在最后包含 util.c。然后,当我编译时,我只需要使用 gcc main.c -o main,其余的都处理好了。

我一直在查找 C 编码标准,试图找出做事的最佳方式是什么,但有这么多,这么多相互矛盾的意见,我不知道该怎么想。为什么这么多地方建议单独编译目标文件而不是将它们全部包含在网络中?util 除了 util.c 之外从不触及任何东西,所以两者是完全独立的,理论上(我的理论)会很好,但我可能错了,因为这是计算机科学,即使他们是对的,人们也会错,所以如果我已经错了,我可能错了。

有人说头文件应该只是原型,源文件是包含它的文件,它是必要的系统头文件。从纯粹美学的角度来看,我更喜欢在标题(在本例中为 util.h)中包含所有信息(类型、使用的系统标题、原型),并且在 util.c 中只有功能代码(不包括一个“#include” util.h"" 在最顶部)。

我想我的意思是,在所有这些东西都有效的情况下,选择一种方法对于不了解背景的人(我)来说听起来很随意。请告诉我为什么和什么。

4

5 回答 5

6

虽然您的程序很小,但这将起作用。但是,在某些时候,您的程序会变得足够大,以至于每次更改一行时都重新编译整个程序是一件很痛苦的事情。

这 - 甚至比避免编辑大文件 - 是拆分程序的原因。如果 main.c 和 util.c 分别编译成目标文件,那么在 main.c 中更改一个函数中的一行将不再需要您重新编译 util.c 中的所有代码。

当你的程序由几十个文件组成时,这将是一个巨大的胜利。

于 2013-01-31T23:25:39.223 回答
1

我认为关键是您只想包含该文件独立所需的内容。这通过允许编译器仅读取必要的标头而不是在可能不需要时重复读取每个标头来减少总体编译时间。例如,如果您的util.c方法使用函数和/或类型 in<stdio.h>但您util.h不使用,那么您只想包含<stdio.h>inutil.c以便在编译器编译util.c只包含in ,但如果您改为包含在其中 ,则每个源文件这包括也包括它是否需要它。<stdio.h><stdio.h>util.hutil.h<stdio.h>

对于只有少量文件的小型项目,这可以忽略不计,但正确包含头文件会影响大型项目的编译时间。

关于“目标文件”的问题:当您将源文件编译为目标文件时,您创建了一个快捷方式,允许构建系统仅重新编译具有过时目标文件的源文件。这是显着减少编译时间的有效方法,尤其是对于大型项目。

于 2013-01-31T23:23:09.597 回答
1

首先,从 .h 文件中包含 .c 文件是完全符合低音要求的。

这样做的“标准”方式遵循大致如下的思路:

您有一个库,其中包含许多函数。将所有内容保存在一个大源文件中意味着任何使用您的库的人都必须链接整个库,即使他只使用其中的一个函数。(想象一下将整个 C 标准库链接到一个puts( "Hello" ).)

因此,您将内容拆分到多个源文件中,这些源文件是单独编译的。每当您对其中一个函数进行更改时,您只需要重新翻译一个小源文件并更新库存档(或可执行文件) - 而不是每次都重新翻译整个内容。(这仍然是一个问题,因为代码大小在某种程度上跟上了 CPU 的改进。在不太花哨的硬件上编译类似 Boost 库的东西仍然需要几分钟......)

然而,现在你正处于紧要关头。该函数在 .c 文件中定义,并且可以方便地链接相应的 .o 文件(如果需要,可以通过 .a 存档)。但是,要真正从另一个源文件(也称为“翻译单元”)正确地处理函数(由 .o 文件提供),您的编译器需要知道函数名称、它的参数列表和它的返回类型。这就是为什么将函数的声明(即没有函数体的函数头)放在单独的头文件 (.h) 中的原因。

其他源文件现在可以#include作为头文件,正确地寻址函数(编译器不知道函数实际做了什么,当你的库/程序的所有部分都编译成 .o 文件时,所有东西都链接在一起。

源文件包含自己的头文件,主要是为了确保两个文件在函数声明上一致。;-)

就是这样,就我现在能写出来的而言。将所有内容放在一个单一的源文件中几乎是不可接受的(实际上,不,不是,对于超过 200 行的任何内容都不能),但是在 .h 文件末尾包含 .c 文件意味着您学会了 C 编码通过查看糟糕的代码而不是一本好书,或者任何辅导过你的人都不应该在他的生活中辅导另一个人的 C 编码。无意冒犯。;-)

PS:头文件还提供了对一段代码的很好的总结/监督。不提供标头的语言(例如 Java)需要 IDE 或文档工具来提取此类信息。就个人而言,我发现头文件是一种好处,而不是一种责任。

于 2013-01-31T23:29:30.797 回答
0

请按习惯使用*.hand文件: files是d in files;包含宏定义、数据类型声明、函数声明和数据声明。所有定义都在文件中。这就是其他人组织 C 程序的方式,请帮助您的人类同胞(有一天可能需要理解您的程序)。如果在外部使用了 in 的内容,您将编写包含该文件中要在外部使用的任何内容的声明,并将其包含在(以检查声明和定义是否一致)和所有使用文件中。如果一堆总是*.c*.h#include*.c*.hextern*.cfile.cfile.hfile.c*.c*.h包含在一起,这可能意味着拆分*.c为不正确(或者至少是*.h; 也许您应该制作一个.h包含所有这些声明的拆分,并在相关文件*.h组中根据需要创建供内部使用)。*.c

[如果按照您的大纲编写的程序遇到了我的问题,我可以向您保证,我会像瘟疫一样避免它。额外的混淆可能会在IOCCC中受到欢迎,但不是我。这肯定表明有人不知道如何干净地组织程序,因此该程序可能不值得尝试。]

回复:单独编译:你分解了一个 C 程序,以便更容易理解,你可以在 C 文件中隐藏事情如何工作的细节(想想static),这为 Parnas 的模块化提供了支持。这也意味着如果您更改文件,则不必重新编译所有内容。

回复:不同的 C 编程标准:是的,周围有很多。选择一个你觉得舒服的,并坚持下去。如果您从事一个项目,请遵守他们的标准。

于 2013-01-31T23:47:48.920 回答
0

“包含在单个翻译单元中”的方法对于任何大型项目都变得非常低效,对于分布在多个开发人员之间的项目是不切实际的。

Morover 在创建静态库时,如果库中的所有内容都来自单个翻译单元,那么任何链接到它的代码都会获得所有库代码,无论它是否被引用。

使用诸如make之类的构建管理器或大多数 IDE 中可用的功能的项目使用头文件依赖项来允许增量构建;只编译那些被修改或依赖于修改文件的源。依赖关系由文件包含确定,因此最小化冗余依赖关系可以加快构建时间。

一个典型的商业项目可能包含数十万行代码和数百个源文件;完全重建时间可能从几分钟到几小时不等。如果在您的开发周期中,您必须在代码更改和测试之间等待那么长的时间,那么生产力将会非常低!

于 2013-02-01T00:24:18.490 回答