C/C++ 链接与库

1, 链接

  链接是为了支持分离编译,编译器将每个源文件(.c, .cpp ,.h)编译成可重定位的目标文件(.o),然后使用链接器将应该重定向的符号(即定义在其他文件的符号)重定向到正确的地址,并将.o文件合并成一个可执行的目标文件。

  链接过程中对于局部变量,以及加了static的全局变量的处理很简单,但是对于为加static的全局变量处理很复杂。

  对于初始化了的全局变量称为强符号,未初始化的符号称为弱符号,对于标名字相同的强符号和弱符号,链接器在进行重定位时遵守下面的原则:

   1,发现多个重名的强符号,报重定义错误。

   2, 一个强符号,多个弱符号,则选择强符号。

   3, 多个弱符号,则从中任意选择一个

  这意味着,在C语言中,在各种地方同时写n个int i,连接器会从中任意选择一个而不报错,即把其中一个当成定义,其他当成声明。而在C++中会报重定义错误因为编译器把每个int i;语句都进行了默认初始化使之变成了强符号。 更重要的是,当不同文件中的全局变量恰好名字一样,并且恰好都没初始化被当成弱符号时,链接器会从中随便选择一个,或者只有恰好只有一个被初始化时,那个未被初始化的会被当成声明,比如在文件a.c 中有int x; b.c中有语句double y = 3.0; 那么这个int x会被重定位y的起始地址上去,然后变成不知道什么的乱七八糟的东西。

  到这种错误一般非常难被发现,这也是为什么全局变量要么要写成static的,要么要放进static函数中的原因之一。

  在gcc 编译链接时加入-fno-common选项可以让链接器遇见多重定义的全局符号时发出一条警告信息。

 

2, 共享库

  库分为静态链接库和动态链接库。

  (1)静态链接库是为了避免重复造轮子,把一堆函数做成静态库,然后在需要使用这些函数时只需要加入这个静态库的地址,同时链接器会非常智能的只把库中被引用的部分加入到可执行的目标文件中。

  为什么不直接用源代码呢,因为库一般非常大,在分离编译的情况下需要写n个.o文件。或者使用者得知道自己引用的函数到底定义在哪几个文件中,这是非常反人类的,所以制作成静态库的方式是非常方便的。同时静态库是需要编译使用的,为什么呢,因为尼玛不同版本的编译器,或者同一个编译器针对不同平台(如32位机器以及64位机)编译出来的静态库是互相不兼容的。

  (2)动态库与静态库不同,静态库中被引用的东西会被直接加入到可执行目标文件中,但是动态库是在程序运行被加载到内存中,动态库的好处在于软件更新时一般只要更新动态库文件,并且多个程序使用同一份动态库时一起使用同一份动态库也是可以的,多个程序使用同一段动态库时会共享动态库中的代码段,并且拥有各自独立的数据段。这也是为什么程序第一次使用是会比较慢,第一次使用动态库中的函数时需要重定位代码段内容。也许还要加载分配各自的数据段(未考证)。

  库的制作与使用。以linux下gcc为例。

  (1)静态库制作

  1, 编译: gcc -c a.c b.c

  2, 制作库: ar rcs libab.a a.o b.o

  (2)静态库使用

  1, 在源码中加入a.c 与 b.c中的申明,一般表现为包含其头文件即#include “a.c” 。。。

  2, gcc -c main.c    gcc -static  -o  a.out -main.o ./libab.a (先编译main.c, 然后-static告诉编译器要生成一个完全链接的可执行目标文件,最后./libab.a表明静态库地址,并且静态库一定要放在所有的引用它的目标文件的后面,否则出现在静态库后面的目标文件,gcc会按照文件顺序扫描.o跟.a的,并且只会把在.a之前对.a的引用部分加入到目标文件中,如果在.a之后再来个.o引用什么乱七八糟的东西,直接报为定义引用错误)

  (3)动态库制作

  1, gcc -shared -fPIC -o libab.so a.c b.c  (-shared表示编译器要生成共享的目标文件,-fPIC表示要生成位置无关的代码,这样在多个程序同时使用该动态库即多个程序同时,让链接器可以不加修改的在内存的任意位置加载这些代码)

  (4)动态库使用, 动态库有两种使用方法。

  1,在编译时就加入动态库位置信息,如gcc -o p.out main.c ./libab.so

  2,  在运行时使用系统API打开动态库,如#include<dlfcn.h> void *dlopen(const char *filename, int flag)

当然在使用之前需要在源码中加入动态库对应的头文件。

 

同理的,在windows下使用msvc编译器或者使用其他什么鬼使用库的步骤是一样的,即

 1, 使用同一个编译器编译库的源码

 2, 告诉编译器使用的是静态库还是动态库,库文件的位置,在源码中包含头文件(对引用的函数,变量进行申明)

对于msvc库文件一般以.lib为后缀,gcc则以.a为后缀

windows下使用动态库在编译动态库时会生成一个.lib,一个.dll这里.lib是用来代替gcc中链接器的作用,使程序能够正确的定位.dll文件,所以需要把.lib静态编译进可执行文件中。linux下使用gcc则只会编译出来一个.so文件,在生成可执行文件时指定.so文件地址就可以了。windows下需要把.dll放进system32文件夹或者与.exe文件同一个文件夹下。

  

 

posted on 2015-07-02 12:26  远近闻名的学渣  阅读(184)  评论(0)    收藏  举报

导航