2016-08-27 103 views
11

比方说,我有以下程序(hello.c):链接文件/头

#include <stdio.h> 
#include <math.h> 

#define NAME "ashoka" 


int main(int argc, char *argv[]) 
{ 
    printf("Hello, world! My name is %s\n", NAME); 
} 

所以,按照我的理解编译这一计划的过程是:

  1. 预处理:将复制粘贴stdio.hmath.h函数声明并替换NAME"ashoka"

    clang -E hello.c 
    
  2. 编译:将转向代码到组件代码

    clang -S hello.c 
    

    文件制备:hello.s

  3. 组装:变换组件代码对象代码生成

    clang -c hello.s 
    

    文件:hello.o

  4. 链接:结合对象文件合并成一个文件,我们将执行。

    clang hello.o -lm 
    

    OR(比方说,我也想链接hello2.o)

    clang hello.o hello2.o 
    

所以,来到这里的问题:

  1. 是过程描述了正确的一个?

  2. 在链接阶段,我们链接在一起.o(Object code)文件。我知道math.h驻留在/usr/include目录中。 math.o在哪里?链接器如何找到它?

  3. 什么是.a在Linux下(静态库)和.so(动态库)?他们如何与.o文件和链接阶段相关?

  4. 比方说,我想分享我与世界上的一个图书馆。我有一个mylib.c文件,其中我已经声明并实现了我的功能。我将如何去分享这个,以便人们通过执行#include <mylib.h>#include "mylib.h"将它包含在他们的项目中?

+0

由于没有关闭,你的代码会发出编译错误 – MikeCAT

+0

@MikeCat thx。编辑。 – padawanTony

+0

谢谢大家的回答。我也会在完成一些研究后创建一个关于问题4的新帖子。 – padawanTony

回答

5
  1. 是的,虽然通过汇编是一个额外的步骤(您可以将C源代码编译为对象)。在内部,编译器将有更多的阶段:将代码解析到AST中,生成中间代码(例如的LLVM位代码),优化等。 )。函数本身存在于libm.a(参见下文)中的对象文件中。
  2. 静态库只是对象文件的存档。链接器将检查使用哪些符号,并提取并链接导出这些符号的目标文件。这些库可以使用ar(例如ar -t列出库中的对象文件)进行操作。动态(或共享)库不包括在输出二进制文件中。相反,您的代码需要的符号在运行时加载。
  3. 只需将您的extern版原型创建一个头文件:

    #ifndef MYLIB_H 
    #define MYLIB_H 
    
    extern int mylib_something(char *foo, int baz); 
    
    #endif 
    

    ,并与库出货。当然,开发者也必须连接(dinamically)对您的图书馆。

静态库的优点是可靠性:不会有惊喜,因为你已经链接你的代码对你确定它的工作原理与确切版本。其他可能有用的情况是,当您使用不常见或出血端的库时,并且您不想将它们安装为共享库。这是以增加二进制大小为代价的。

共享库生成较小的二进制文件(因为该库不在二进制文件中),占用的RAM更小(因为操作系统可以加载库一次并在多个进程间共享),但需要更多的关注才能确保你正在加载你想要的东西(例如,在Windows上看到DLL Hell)。

@iharob指出,他们的优势不仅仅停留在二进制大小。例如,如果在共享库中修复了一个错误,则所有程序都将从中受益(只要它不破坏兼容性)。另外,共享库提供了外部接口和实现之间的抽象。例如,假设一个操作系统提供了一个应用程序库来连接它。通过更新,OS界面发生变化,并且库实现跟踪这些更改。如果它被编译为一个静态库,那么所有的程序将不得不用新版本重新编译。如果它是一个共享库,他们甚至不会注意到它(只要外部接口保持不变)。另一个例子是将系统/发行版特定的方面包装到通用接口的Linux库。

+0

我非常喜欢你的答案,但是*你没有加载不兼容的版本*有点误导。假设如果您的代码是针对正确版本的库进行编译并与之链接的,那么就不会有任何意外。此外,除了二进制文件的大小以外,共享库还有很多优于静态文件的优点。例如,没有共享库的完整操作系统将很具挑战性。 –

+0

@iharob重写了该部分并在共享库上添加了更多内容。 –

+0

@AndreaBiondo很好的答案。但是你让我有更多的问题。请看看新创建的问题4. – padawanTony

2
  1. 是的,这是一般的过程。
  2. 没有math.o文件时,-lm开关链接libm.so共享对象,因此:。所以)其中所有由数学函数所需要的码元在math.h中声明定义为
  3. 允许在两节回答这个

    静态库

         只保存归档格式的目标文件的集合。

    共享库(在Linux

         与定义,如他们在可执行文件中定义的符号ELF文件时,链接程序能够在运行时使用这些符号和有一个加载器将这些符号加载到要使用的程序中。

         这是一个很值得在其他平台上一样,像.DLL S于窗口,他们基本上是编译的缺乏的main()函数因此他们不能直接执行的程序。 Thye包含要在运行时加载的可执行代码。事实上,你可以在Linux上使用dlopen(3)


注意:在你的代码贴一些事情不会发生,因为你没有从文件math.h所以链接到libm.so使用任何东西是完全uneeded。编译器也会尝试优化生成的代码,在您的情况下,程序相当于中最简单的Hello World。但是问题的其余部分是有效的,它的确有意义。

+0

很棒的回答。但是你让我有更多的问题。请看看新创建的问题4. – padawanTony

+0

@padawanTony很高兴帮助,并让你有更多的问题其实很好。但是,请问另一个问题。答案已发布后不要编辑你的。 –

4

您在上面描述的过程是正确的。在绝大多数然而箱子,把这种C代码和预处理在单个步骤中装配如下:

clang -c hello.c 

执行单独的预处理通常仅用于调试完成。除非您打算进行一些手工装配级别优化(几乎不需要),否则几乎不会执行装配转换。

关于链接,-l选项告诉链接程序查找“lib {name} .so”形式的共享库。在你的例子中,-lm告诉链接器与libm.so链接。默认情况下,它将在/ usr/lib中查找,但是您可以使用-L选项为其提供一个搜索库的目录列表。

您使用-B标志与静态库或动态链接库之间进行切换:

clang hello.o -lm -Bstatic -lstaticlib -B dynamic -ldynamiclib 

这将libm.so,libstaticlib.a链接,并libdynamiclib.so

静态库直接链接到您的可执行文件,如.o文件。相比之下,动态库与您的可执行文件保持分离,并在运行时加载。

+0

很棒的回答。但是你让我有更多的问题。请看看新创建的问题4. – padawanTony