Doea GCC编译所有功能,包括未使用的功能?
当我们包含头或库时,编译的excutable是否包含库中的所有函数?
答案 0 :(得分:2)
是的,所有功能都已编译完毕。但是:
- 它们可能会在链接之前被剥离(如果您已将其作为编译设置启用)或
- 根本没有链接到可执行文件中(如果你的构建设置在链接器阶段不包含这样的命令。
答案 1 :(得分:1)
(1)您声明的功能。这些是编译器知道的函数,但它不一定知道(或必须知道)它们在内部的样子(它确实知道它们期望的参数和返回的内容)。
当您包含标头时,标头通常主要包含声明,有时还包含一些内联函数。这意味着在大多数情况下,编译器知道但不需要在那时编译函数(内联函数除外,因为为这些函数提供了实现)。它假装功能存在,因为你“承诺”他们这样做。
您声明和实施的功能。通常,但不是必需的,这些函数在包含声明它们的标头的源文件中实现。编译器必须编译所有已实现的函数。此外,它们中的一些可能在几个地方内联或实例化或具有不同的参数,最终可能产生略微或完全不同的可执行代码。
另一方面,您有声明但不实施的功能。编译器并不关心,它可以假装它们存在,即使它们不存在(只要它们被声明)。
但是,如果使用它们这些二进制代码必须在链接时“来自某个地方”,最常见的是静态或动态库。如果链接器无法在任何地方找到实现,则无法构建可执行文件并生成链接错误。
(2)编译的内容以及最终放在可执行文件中的内容是截然不同的。构建可执行文件是一个很多步骤。
首先,每个编译单元(读作:源文件)都通过预处理器运行。预处理器是纯文本替换自动机,它不了解实际的编程语言。预处理程序指令(例如#include
)会导致另一个文件中的文本插入该位置,而不了解此文本的含义。
接下来,编译器检查语法,并构建表示程序含义的数据结构。这自然会假设编译传递给它的每一段代码。但是,并非所有功能都必须实施。可选地,编译器执行许多优化,然后生成某些形式的可执行代码(伪代码,汇编源,等等)。这可能包括调用编译器只是正式知道的函数,因为它已经看到了它们的声明
最后,链接器获取所有目标文件和可选的一些库,并使用使用的所有函数将可执行文件放在一起。它可以选择执行另一个优化过程。
通常,只有实际使用的函数才会被放入二进制文件中(尽管没有严格的保证)。这些函数可以从编译源代码时编译器生成的目标文件中复制,也可以从静态库中复制 对于来自动态库的函数,只有找到函数所需的必要信息以及一些小的胶水代码(通常是一个跳转指令后跟一个返回,这个被使用以便加载器可以轻松地重定位)被添加到可执行文件中,当程序运行时,实际函数从动态库加载。
请注意,函数可以内联,因此它实际上会出现几次(相同或略有不同),并且函数可能 内联并链接为正常函数。或者,它可能被放置在可执行文件中,即使您从未调用它(例如,因为您获取其地址)。或者,在另一个极端,函数可以完全优化,因此即使使用它也不会出现在可执行文件中。
答案 2 :(得分:0)
不,通常,可执行文件仅包含执行所需的功能。但是,如果怀疑编译器在安全方面出错,这意味着您的可执行文件很可能会携带大量不必要的代码,这主要是在编译器/链接器不确定是否需要某些代码时,或者何时它无法拆分不需要的代码,就像它与某些必需的函数在同一个目标文件中一样。