我已经编写了一段时间的代码,但我不是经典的计算机科学培训,所以如果这个问题很荒谬,请放轻松我。
我一直试图找到一个确定答案的东西是,如果我在C中包含一个文件,我是否可以获得链接文件的整个内容,或者只是我使用的部分?如果它有10个函数,我只使用其中的1个函数,其他9个函数的代码是否包含在我的可执行文件中?这对我来说尤其重要,因为我正在研究微控制器并且内存非常珍贵。
感谢您对此问题的任何帮助。
答案 0 :(得分:18)
首先,头文件没有"链接在"中。 #include
基本上是文本复制粘贴功能。包含文件中的所有内容都会被预处理程序粘贴到最终的翻译单元中,以后将由编译器正确地进行无缝处理。编译器本身对任何头文件或#include
指令一无所知。
其次,这意味着如果在您的代码中声明或定义了一些您不使用的函数或变量,那么它是通过#include
来自头文件还是直接写在源文件中完全无关紧要。没有什么区别。
第三,问题是:您在头文件中包含的具体内容是什么?通常,头文件不定义对象和函数,它们只是声明它们。无论您是否使用该函数,声明都不会产生任何代码。 声明只是告诉编译器代码(从函数定义生成)已存在于其他地方。因此,只要我们讨论典型的头文件,#include
指令和头文件本身对最终代码大小没有影响。
第四,如果您的头文件属于某种包含函数(或对象) definitions 的异常类型,那么请参阅"首先"和"其次"以上。编译器本身一次只能看到一个转换单元,因此编译器的典型策略是使用内部链接(即static
对象和函数完全丢弃未使用的实体)并使用外部链接保留所有实体。具有外部链接的实体不能被编译器正确丢弃,因为在某些其他翻译单元中可能需要它们。
第五,在链接阶段链接器可以完整地看到程序,因此,如果它足够先进(并且如果允许链接器,则可以丢弃未使用的对象和函数)做它)。同时,典型的普通链接器的包含 - 排除精度仅限于单个目标文件。每个目标文件都是这种链接器的原子。这意味着如果您希望能够在每个函数的基础上排除未使用的函数,则可能必须采用每个目标文件的一个函数"策略,即每.c
个文件只写一个函数。当然,这只有在您编写自己的代码时才有可能。如果您要使用的某些第三方库不符合此约定,则您可能无法排除单个函数。
答案 1 :(得分:9)
如果#include
是C中的文件,则该文件的全部内容将添加到源文件中并由编译器编译。但是,头文件通常只有函数声明而没有定义(所以没有编译实际代码)。
因此,回答您的问题:只有您使用(并间接依赖)的函数才会包含在最终的程序文件中,这与您#include
的文件无关。快乐的黑客!
答案 2 :(得分:1)
您必须区分不同的场景:
.c
文件中实现的,还是分布在多个.c
文件中?关于第1点:仅通过#include
外部声明,其他任何代码都不会成为目标文件的一部分。并且,作为头文件一部分但未被代码引用的静态函数的定义可能不会成为目标文件的一部分 - 这是一种相当普遍的优化。但是,这取决于你的编译器。
关于第2点:有些链接器只能链接整个目标文件,全部或全部。这意味着,如果在头文件中声明的所有外部函数都在一个.c
文件中实现,并且,如果您的代码至少引用其中一个函数,则很可能会获得整个目标文件,包括所有你不能使用的其他功能。但是,一些链接器可以避免这种情况,并在链接目标文件时删除未使用的部分。
处理非优化链接器的一种强力方法是将每个外部函数放入自己的.c文件中。但是,您必须找到一种方法来处理这些函数中的某些函数引用作为原始.c
文件一部分的公共静态函数的情况......
答案 3 :(得分:0)
Include只是简单地向编译器呈现看起来像单个文件的内容(如果你在gcc上执行save-temps,你会看到提供给实际编译器的确切单个文件)。它并不复杂。因此,如果您在.c文件中有一些函数原型或定义,那么让它们来自一个包含没有任何区别,最终结果是相同的。
如果您包含的内容包括代码,函数,而不仅仅是原型,那么就像您在.c文件中拥有它们一样。这些是否显示在最终二进制文件中是否与使用static声明它们是否为全局有关,然后是否进行了优化等等。变量和结构以及其他内容也是如此。
并非所有链接器都是相同的,但是执行它的常用方法是编译器在对象中留下的最终二进制文件。但是如果你拿走那些物品并从中制造一个库那么一些/很多?链接器不会将所有内容都解压缩到解析依赖项所需的部分上。
所有这些你可以通过一些非常小的例子和大约3-5分钟的时间来解决这些问题。我建议你这样做。