假设我有一个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.o
和bar.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 使用的某些内容可能使用的内容,等等。如果存在无法调用的函数或无法访问的对象,则链接器可以将其忽略。如果目标文件包含五个可以调用的函数和十个不能调用的函数,则链接器可能只包含前五个。