当静态链接到 .a 文件时,C 编译器是否会丢弃未使用的函数?

当静态链接到 .a 文件时,C 编译器是否会丢弃未使用的函数?

假设我有一个main.c静态链接到libmine.a.静态链接到库会导致库函数在编译时嵌入到主可执行文件中。

如果libmine.a要提供 未使用的函数main.c,编译器(例如 GCC)会丢弃这些函数吗?

这个问题的灵感来自于“常见消息传递”,即使用静态库会使可执行文件更大,所以我很好奇编译器是否至少从存档文件中删除未使用的代码。

答案1

默认情况下,链接器将目标文件作为一个整体来处理。在您的示例中,可执行文件最终将包含main.c( )中的代码main.o,以及提供(传递)使用的所有函数所需的任何目标文件libmine.a(目标文件的存档)main.c

所以链接器不一定包括全部libmine.a,但它可以使用的粒度不是函数(默认情况下),而是对象文件(严格来说,部分)。这样做的原因是,当给定.c文件编译为目标文件时,源代码中的信息会丢失;特别是,不存储函数的结尾,只存储函数的开头,并且由于可以组合多个函数,因此很难从目标文件中确定在函数未使用时实际可以删除的内容。

然而,如果编译器和链接器能够访问所需的额外信息,则可以做得更好。例如,80 年代 Mac 上的 LightspeedC 编程环境可以使用项目作为库,并且由于在这种情况下它具有完整的源代码,因此它只会包含实际需要的功能。

在更现代的系统上,可以告诉编译器生成允许链接器单独处理函数的目标文件。使用 GCC,.o-ffunction-sections -fdata-sections启用选项的情况下构建文件,并将最终程序与该--gc-sections选项链接。这确实会产生影响,特别是阻止某些类别的优化;看丢弃 GCC 中未使用的函数了解详情。

现代编译器和链接器可以使用的另一个选项是链接时优化;启用此功能-flto。当启用优化时(例如 -O2当编译目标文件时),链接器不会在生成的二进制文件中包含未使用的函数。即使没有-ffunction-sections -fdata-sections.

答案2

是的。但它是在模块级别上,而不是在功能上。

例如,您有两个源文件:foo_goo.c 和 bar.c

// foo_goo.c
int foo() { .. };
int goo() { .. };
// bar.c
int bar() { .. };

将它们编译成foo_goo.obar.o,并将它们放入.o存档中libmine.a

现在,您main()只调用该foo()函数。

// main.c
int main(int argc, char **argv) {
   return foo();
}

将此源文件编译成main.o并链接到.a.

链接器将foo()在模块中看到该函数foo_goo.o并使用它,main()将引用该foo()函数,并且该goo()函数将被添加到最终的二进制文件中,但不会被引用。将bar.o被忽略,因为其中没有引用任何名称。

答案3

这与编译器无关。如果使用静态链接,链接器会将所需的所有对象放入一个文件中。

这将是 C 程序的 main() 函数,main 可能使用的所有内容,main 使用的某些内容可能使用的内容,等等。如果存在无法调用的函数或无法访问的对象,则链接器可以将其忽略。如果目标文件包含五个可以调用的函数和十个不能调用的函数,则链接器可能只包含前五个。

相关内容