GCC是否编译所有函数?

GCC是否编译所有函数?,gcc,Gcc,GCC是否编译所有函数,包括未使用的函数 当我们包含头或库时,编译的可执行文件是否包含库中的所有函数?是的,所有函数都已编译。但是: -它们可能会在链接之前被剥离(如果您将其作为编译设置打开),或者 -根本没有链接到可执行文件中(如果您的生成设置在链接器阶段没有包含这样的命令)。是的,所有函数都已编译。但是: -它们可能会在链接之前被剥离(如果您将其作为编译设置打开),或者 -完全没有链接到可执行文件中(如果您的生成设置在链接器阶段不包含此类命令)。(1)您声明了一些函数。这些函数是编译器知道的

GCC是否编译所有函数,包括未使用的函数


当我们包含头或库时,编译的可执行文件是否包含库中的所有函数?

是的,所有函数都已编译。但是:

-它们可能会在链接之前被剥离(如果您将其作为编译设置打开),或者


-根本没有链接到可执行文件中(如果您的生成设置在链接器阶段没有包含这样的命令)。

是的,所有函数都已编译。但是:

-它们可能会在链接之前被剥离(如果您将其作为编译设置打开),或者

-完全没有链接到可执行文件中(如果您的生成设置在链接器阶段不包含此类命令)。

(1)您声明了一些函数。这些函数是编译器知道的,但它不一定知道(或必须知道)它们的内部外观(它确实知道他们期望的参数和返回的参数)。
当包含头时,头通常包含大部分声明,有时包含一些内联函数。这意味着在大多数情况下,编译器知道但不需要在那时编译函数(内联函数除外,因为为这些函数提供了实现)。它将假装函数存在,因为您“承诺”它们存在

有些函数是您声明的实现的。这些函数通常(但不是必需的)在包含声明它们的头文件的源文件中实现。编译器必须编译所有实现的函数。此外,其中一些函数可能在多个位置内联或实例化,或者使用不同的参数最后可能会生成略有不同或完全不同的可执行代码

另一方面,有些函数是您声明的,但没有实现。编译器不在乎,它可以假装它们存在,即使它们不存在(只要它们被声明)。
但是,如果使用它们,这些二进制代码在链接时必须“来自某处”,最常见的是静态或动态库。如果链接器在任何地方都找不到实现,它将无法构建可执行文件并生成链接错误

(2)编译的内容和最终放入可执行文件的内容截然不同。构建可执行文件是一个多步骤的过程

首先,每个编译单元(读作:源文件)是通过预处理器运行的。预处理器是一个纯文本替换自动机,它不了解实际的编程语言。预处理器指令,如
#include
会导致将另一个文件中的文本插入到该位置,而不了解此文本的含义。
接下来,编译器将检查语法,并构建一个表示程序含义的数据结构。这自然假定编译交给它的每一段代码。但是,并非所有函数都必须实现。编译器也可以执行一些优化,然后生成某种形式的可执行code(伪代码、汇编程序源代码等)。这可能包括对函数的调用,因为编译器已经看到了这些函数的声明,所以只能正式了解这些函数。
最后,链接器获取所有目标文件和一些库(可选),并使用所有使用的函数将一个可执行文件放在一起。它可以选择执行另一个优化过程

通常,只有实际使用的函数才会放入二进制文件中(尽管没有严格的保证)。这些函数要么是从编译器编译源代码时生成的目标文件中复制的,要么是从静态库中复制的。
对于来自动态库的函数,只有找到该函数的必要信息以及一些小的粘合代码(通常是一条跳转指令,后跟一个返回,使用该指令可以方便地重新定位加载程序)被添加到可执行文件中,当程序运行时,实际函数从动态库加载

请注意,一个函数可能是内联的,因此它实际上会出现几次(相同或略有不同),并且一个函数可能是内联的,并作为普通函数链接。或者,即使您从未调用它,它也可能被放在可执行文件中(例如,因为您获取了它的地址)或者,在另一个极端,一个函数可能被完全优化,因此即使使用它,它也不会出现在可执行文件中。

(1)您声明了一些函数。这些函数是编译器知道的,但它不一定知道(或必须知道)它们的内部外观(它确实知道他们期望的参数和返回的参数)。
当包含头时,头通常包含大部分声明,有时包含一些内联函数。这意味着在大多数情况下,编译器知道但不需要在那时编译函数(内联函数除外,因为为这些函数提供了实现)。它将假装函数存在,因为您“承诺”它们存在

有些函数是您声明的实现的。这些函数通常(但不是必需的)在包含声明它们的头文件的源文件中实现。编译器必须编译所有实现的函数。此外,其中一些函数可能在多个位置内联或实例化,或者使用不同的参数最后可能会生成略有不同或完全不同的可执行代码

另一方面,有些函数是您声明的,但没有实现。编译器不在乎,它可以假装它们存在,即使它们不存在(只要它们被声明)。
霍维夫