在C语言中包含过多的头文件会出现什么问题?

3

包含过多的头文件是否会增加源文件的大小,也会增加可执行文件的大小吗?这些头文件会增加编译时间吗?

例如,如果我在程序中添加这些头文件,它们会增加源文件或可执行文件的大小还是两者都会增加?

#include <stdio.h>
#include "header1.h"
#include "header2.h"

太多头文件的问题还有哪些?

1: 不清楚您的问题,但很可能是肯定的,因为每次添加 #include <someheader> 时,源文件的大小显然会增加。 2: 可能吧,这取决于头文件的内容。 3: 很可能是肯定的,这也取决于头文件的大小和复杂程度。 4: “太多” 是什么意思? - Jabberwocky
1
无论正确答案是什么,不要将您的小标题合并为一个巨大的标题,以避免#include行数的计算。 - SKi
5个回答

3
包含太多的头文件会增加源代码文件的大小。如果您的系统中1个字母等于1个字节,那么添加#include 至少会使源代码文件大小增加18个字节。除非您使用的是20世纪80年代中期的计算机,否则这对您并不重要。
它不会增加可执行文件的大小,只有使用的函数才会增加可执行文件的大小。
通常情况下,包含这些头文件会增加编译时间,虽然编译器会使用各种技巧(例如“预编译头”)来处理自己的库。除非您使用的是20世纪80年代或更糟糕的东西(如Eclipse),否则这不是问题。
包含太多头文件的另一个问题是不要包括您不使用的内容。每个包含都创建一个依赖项,并意味着全局命名空间中添加了更多的标识符和符号。

Eclipse或任何其他IDE与编译时间无关。 - the busybee
您可能想要添加的是,标准定义了至少15个嵌套级别的实现定义限制。嵌套总数没有限制。 - the busybee
因此,如果头文件仅包含函数声明(以便它可以知道函数声明),但编译器如何知道内置函数(如printf()scanf())的函数定义? - Anonymous
@Lundin 我不知道 windows.h 的包含树有多大,以及它与 Linux 的包含树相比如何。但是秒数很长,想象一下一个有1000个源文件的项目,当每个源文件仅包含需要3秒时,你已经失去了50分钟的CPU时间。 - 12431234123412341234123
为什么大小只会增加 strlen("#include <stdio.h>"); 字节?我读到编译器将用相应头文件中的所有代码替换头文件。 - Anonymous
显示剩余8条评论

3
包含过多的头文件会增加源文件的大小。例如,对于每个添加到源文件中的额外字符,例如“#include ”,将精确地增加该语句中字符数的物理大小,例如:strlen(“#include ”)字节。(并且,根据操作系统分配文件块大小的方式,它可能被操作系统视为额外的kByte。)然而更重要的是,在编译时,每个#included头文件的内容都将有效地扩展为源代码,并提供给编译器。同时也会增加可执行文件的大小吗?
根据实际使用的内容,是/否/可能。优化编译器可以排除可执行文件中不需要的任何内容。如果没有使用任何内容,则可执行文件不会增加额外的大小。但是,在编译时仍会进行额外的工作,因为即使头文件中没有有用的内容,编译器也要在处理之前知道这一点。

这些头文件是否会增加编译时间?

与什么相比? 例如,如果在头文件中包含了构建所需的必要组件,例如函数原型、#defines等,则编译时间就是正常的编译时间。但是,如果您已经使用了3个必要的头文件进行编译,并决定添加新库(及其相应的头文件),那么下一次编译肯定比之前的时间长。

包含太多头文件的其他问题是什么?

< p > < em > 太多了吗? 如果每个头文件都是必需的,那么就不会太多(附带关于 最大头文件深度 的警告)。但是,如果发现一个头文件是 < em > 不必要的 ,那么就应该摆脱它的 代码膨胀 。这会增加编译时间,因为构建过程必须查看每个头文件,无论其中是否有任何有用的内容。更糟糕的是,< em > 不必要的 头文件会增加复杂性,并给未来维护者的任务带来困难。

这里有一篇好的文章详细讨论了这个问题。

此外,这是一个有趣的页面,也讨论了头文件。


为什么大小只会增加 strlen("#include <stdio.h>"); 字节?我读到编译器将用相应头文件中的所有代码替换头文件。 - Anonymous
@匿名 - 那个问题的措辞是“源文件的大小”,因此,源文件的大小仅受其内容的字节数(即文件中包含的字符数)和操作系统对file块内存分配的影响。 (一些操作系统按块分配文件大小,每个分配1 kByte,因此即使只有一个字符,也会导致1 kByte的文件块分配大小,接下来的999个字符是免费的(不再分配块),但再加上一个字符(现在是1001字节)将导致额外的1 kByte块文件大小。(请注意,物理文件大小仍然... - ryyker
@匿名 - ...通过文件中的字节数来衡量。因此,所有这些都是为了说明,给定int len = strlen("#include <stdio.h>"),通过添加此语句,文件的物理大小仅增加了len个字节。请注意,包含头文件对后续对象(例如生成的二进制代码或编译器必须遍历的内容量)的影响非常大。 - ryyker

2

头文件不应该产生任何额外的代码,除非它们设计得很差。如果头文件产生了额外的代码并且您多次包含它们,则可能会遇到重定义问题。在这里,代码是指“机器代码”,即可执行代码。
关于源代码,编译器将最终将源代码视为一个包含 #include 指令替换为文件内容的大型源代码,因此添加更多的头文件将增加编译时间(因为表面上的源代码将变得更长)。因此应避免包含不必要的文件。


1
添加头文件会增加中间源文件的大小,考虑到包含关系。现代编译器可能甚至不会显式地生成此中间文件 - 它可能被吸收到整个编译过程中。这是编译器设计的问题。作为开发人员,除非您要求(例如,gcc -E),否则您可能永远不会看到完全展开的文件。
如果所有头文件都只包含声明和常量定义,则添加头文件不一定会增加编译后代码的大小,它们不会增加太多或根本不会增加大小。如果它们包含实际代码 - 这并不是一个特别常见的做法 - 它们可能会对可执行文件大小产生一些小影响。
添加头文件可能会对编译时间产生一些影响,但实际上,这不是任何人应该问的问题。如果需要头文件,就需要头文件。如果它减慢了编译速度,那么有什么替代方案呢?不编译吗?
如果问题确实是关于如何在头文件和源文件之间分配代码以改善构建过程中某些方面的话,那么回答这个问题就非常复杂。如果问题是关于包含一堆你不使用的头文件会造成什么危害的话,那么现代编译器的答案是:从功能角度来看,影响很小。然而,包含一些头文件会让读者误以为源代码实际上使用了它所声明的特性,这对可读性非常不利。你应该尽量避免包含未使用的头文件,这可以为你自己或同事做出贡献。但如果确实需要它们,那么担心后果也没有太大意义。

如果它减慢了编译速度,那么有什么替代方案呢?使用前向声明(仅适用于“struct”,“enum”等,而不是函数),以避免编译时间的指数增长,并且在头文件中不包含同一项目中的其他头文件。 - 12431234123412341234123
当然,您可以从头文件中挑选声明并将其放入源代码中。这很烦人,而且可能会给长期维护带来麻烦,但它肯定是可行的。但在什么情况下实际上是可行的呢? - Kevin Boone
不是从头文件中提取,而是从一个头文件到另一个头文件。前向声明意味着不定义 structenum,只声明这样的 structenum 存在。 - 12431234123412341234123
我承认如果你实际上在编写头文件,你可以做一些事情。不过我不确定这与问题有关。 - Kevin Boone

1
包含太多头文件会增加源文件的大小吗?
源文件中的字符越多,源文件的大小就越大。但这仅涉及到 #include 指令本身,而不是头文件的内容——源文件不会因为头文件的内容而扩展。
当你 #include 一个头文件时,编译器在那个时间点上就知道了要读取它,但源文件并没有改变。
所以,是的。
它是否也会增加可执行文件的大小?
这取决于头文件的内容。如果它们包含定义,那么是的。
这些头文件会增加编译时间吗?
编译器读取和评估的内容越多,编译所需的时间就越长。所以是的。
包含太多头文件还有哪些问题?
如前所述,评估所需的时间可能会更长,因此头文件越多,编译速度就越慢。但是,添加尽可能多的有用头文件没有问题。只是不要添加不必要的头文件,这会减慢编译速度。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接