Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/66.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ C和C++;静态链接:只是一个副本?_C++_C_Linker_Static Linking - Fatal编程技术网

C++ C和C++;静态链接:只是一个副本?

C++ C和C++;静态链接:只是一个副本?,c++,c,linker,static-linking,C++,C,Linker,Static Linking,当有人静态链接一个.lib时,链接器会将lib的全部内容复制到最终的可执行文件中,还是只复制目标文件中使用的函数?某种程度上。不过,它还需要修复所有函数调用指针。尤其是当这些函数调用存在于静态库之外(即在另一个静态库或可执行文件中)时。链接器是在古代发明的,当时内存特别宝贵。它们的主要功能之一是删减不使用的模块。这种能力一直延续到今天 一些库函数依赖其他函数是很常见的,所有依赖项都将被链接。它将只使用使用过的函数和符号(除非另有说明,但这可能很棘手) 附带问题: 这实际上可能是一个问题,如果你的

当有人静态链接一个.lib时,链接器会将lib的全部内容复制到最终的可执行文件中,还是只复制目标文件中使用的函数?

某种程度上。不过,它还需要修复所有函数调用指针。尤其是当这些函数调用存在于静态库之外(即在另一个静态库或可执行文件中)时。

链接器是在古代发明的,当时内存特别宝贵。它们的主要功能之一是删减不使用的模块。这种能力一直延续到今天


一些库函数依赖其他函数是很常见的,所有依赖项都将被链接。

它将只使用使用过的函数和符号(除非另有说明,但这可能很棘手)

附带问题:


这实际上可能是一个问题,如果你的f.ex。有一些类只是将自己注册到工厂。没有人直接调用这些类,因此它们不会被包含,因此不会在工厂中注册。有很多方法可以解决这个问题(通常是在引用源文件的头文件中声明一些匿名变量)。

取决于链接器。有些链接器很懒惰,只是把整个库都放进去了。另一个极端是链接器,它只向可执行文件中插入必要的代码

示例测试是编写一个使用
put
的程序,并与使用
printf
的程序进行比较。如果可执行文件大小相同,则更像是一个懒惰的链接器

例子: puts_test.cpp

#include <cstdio>
using namespace std;

int main(void)
{
  puts("Hello World\n");
  return 0;
}
#包括
使用名称空间std;
内部主(空)
{
放置(“Hello World\n”);
返回0;
}
printf_test.cpp

#include <cstdio>
using namespace std;

int main(void)
{
  printf("%s\n", "Hello World");
  return 0;
}
#包括
使用名称空间std;
内部主(空)
{
printf(“%s\n”、“你好世界”);
返回0;
}
在上面的示例中,
put
函数不需要额外的代码来解析格式字符串或将数字转换为文本。这是基线,因为它需要最小的库函数

使用
printf
的示例需要更多功能。
printf
函数需要解析格式字符串并输出文本

预期结果是
printf
可执行文件应该大于
put
可执行文件。大多数编译器都会为
printf
函数引入所有代码,以解析符号(例如用于显示
float
s),即使未使用该部分代码。更智能(且成本更高)的编译器将分解
printf
功能,只包含使用或需要的部分。在上面的例子中,编译器应该只包括处理文本的部分,而不包括格式化整数和浮点值的代码

惰性编译器或在调试模式下,将复制
puts
示例的整个库,从而使可执行文件大小相同

符号比较 *nix平台和Cygwin提供了从可执行文件获取符号的工具。一个这样的实用程序是
nm
。对每个可执行文件运行
nm
,将输出定向到文本文件。比较这两个文本文件。惰性编译器应该具有相同的符号;但它们的位置可能不同(这对问题不重要)

  • 整个图书馆--编号
  • 只是你调用的函数--编号
  • 还有别的吗--是。
它当然不会把整个图书馆都包括进去

但它也不一定只包含“对象文件中使用的函数”

链接器将创建一个递归构建的列表,其中列出库中哪些对象模块满足您未定义的符号

然后,它将包括每个对象模块


通常,一个给定的对象模块将包含多个函数,如果其中一些函数没有被您调用的函数调用,您将得到一些您不需要的函数(和数据对象)。

链接器通常不会在构建最终可执行文件之前删除死代码。也就是说,它(通常)将链接所有符号,无论它们是否在最终可执行文件中使用。但是,链接器通常会显式地提供优化设置,您可以使用这些设置强制链接器进行额外的尝试

对于GCC,这分两个阶段完成:

  • 首先编译数据,但告诉编译器在翻译单元中将代码分成不同的部分。这将通过使用以下两个编译器标志对函数、类和外部变量执行:

    -fdata节-F功能节

  • 使用链接器优化标志将翻译单元链接在一起(这会导致链接器放弃未引用的节):

    -Wl,--gc部分

  • 因此,如果您有一个名为test.cpp的文件,其中声明了两个函数,但其中一个未使用,则可以使用以下命令将未使用的函数省略到gcc(g++):

    (注意,-Os是一个额外的编译器标志,它告诉GCC优化大小)

    对于MSVC,函数级链接实现了同样的功能。 我认为编译器的标志是(将事物分类为部分):

    然后是链接器标志(放弃未使用的节):


    你想要苹果派还是草莓蛋糕?有点像。@nightcracker:我说“有点像”,因为链接过程的一个主要部分是将内容从静态库复制到最终的可执行文件中。函数指针修正也会发生在函数剥离时。说“链接过程的一个主要部分实际上是将静态库中的内容复制到最终的可执行文件中。但是它将…”是错误的。链接器的基本定义是解析函数调用指针。未解析符号是指尚未解析的符号。不是吗
    gcc -Os -fdata-sections -ffunction-sections test.cpp -o test.o -Wl,--gc-sections
    
    /Gy
    
    /OPT:REF