1 库的分类

依据链接时期的不同,库又有静态库和动态库之分。

静态库是在链接阶段被链接的。所以生成的可执行文件就不受库的影响了。即使库被删除了,程序依旧能够成功执行。

有别于静态库,动态库的链接是在程序执行的时候被链接的。所以,即使程序编译完,库仍须保留在系统上,以供程序执行时调用。

2 静态库和动态库的比較

链接静态库事实上从某种意义上来说也是一种粘贴复制。仅仅只是它操作的对象是目标代码而不是源代码而已。由于静态库被链接后库就直接嵌入可运行文件里了,这样就带来了两个问题。

首先就是系统空间被浪费了。这是显而易见的,想象一下,假设多个程序链接了同一个库,则每个生成的可运行文件就都会有一个库的副本,必定会浪费系统空间。

再者,人非圣贤,即使是精心调试的库,也难免会有错。一旦发现了库中有bug,拯救起来就比較麻烦了。必须一一把链接该库的程序找出来。然后又一次编译。

而动态库的出现正弥补了静态库的以上弊端。

由于动态库是在程序执行时被链接的,所以磁盘上仅仅须保留一份副本,因此节约了磁盘空间。假设发现了bug或要升级也非常easy,仅仅要用新的库把原来的替换掉即可了。

那么。是不是静态库就一无是处了呢?

答曰:非也非也。不是有句话么:存在即是合理。

静态库既然没有湮没在滔滔的历史长河中,就必定有它的用武之地。想象一下这种情况:假设你用libpcap库编了一个程序,要给被人执行,而他的系统上没有装pcap库。该怎么解决呢?最简单的办法就是编译该程序时把全部要链接的库都链接它们的静态库。这样。就能够在别人的系统上直接执行该程序了。

所谓有得必有失。正由于动态库在程序执行时被链接。故程序的执行速度和链接静态库的版本号相比必定会打折扣。

然而瑕不掩瑜,动态库的不足相对于它带来的优点在现今硬件下简直是微不足道的,所以链接程序在链接时通常是优先链接动态库的。除非用-static參数指定链接静态库。

3 动态链接库

隐式调用

1. 创建动态链接库

#include<stdio.h>
void hello()
{
printf("hello world/n");
}

用命令gcc -fPIC -shared hello.c -o libhello.so编译为动态库。

能够看到。当前文件夹下多了一个文件libhello.so。

gcc參数

-shared:

该选项指定生成动态连接库(让连接器生成T类型的导出符号表,有时候也生成弱连接W类型的导出符号)。不用该标志外部程序无法连接。

相当于一个可运行文件

-fpic:

表示编译为位置独立的代码。不用此选项的话编译后的代码是位置相关的所以动态加载时是通过代码拷贝的方式来满足不同进程的须要,而不能达到真正代码段共享的目的。

2. 再编辑一个測试文件test.c,内容例如以下

#include<stdio.h>
int main()
{
printf("call hello()");
hello();
}

编译 gcc test.c -lhello

-l 选项告诉编译器要使用hello这个库。奇怪的地方是动态库的名字是libhello.so,这里却使用hello.

但这样还不行。编译会出错。

In function `main':

test.c:(.text+0x1d): undefined reference to `hello'

collect2: ld returned 1 exit status

这是由于hello这个库在我们自己的路径中,编译器找不到。

须要使用-L选项,告诉hello库的位置

gcc test.c -lhello -L. -o test

-L .告诉编译器在当前文件夹中查找库文件

3. 编译成功后运行./test, 仍然出错

说找不到库

有两种方法:



一、能够把当前路径增加 /etc/ld.so.conf中然后执行ldconfig。或者以当前路径为參数执行ldconfig(要有root权限才行)。



二、把当前路径增加环境变量LD_LIBRARY_PATH中



当然。假设你认为不会引起混乱的话,能够直接把该库拷入/lib,/usr/lib/等位置(无可避免,这样做也要有权限),这样链接器和载入器就都能够准确的找到该库了。

我们採用另外一种方法:

export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH

这样,再运行就成功了。

注:

LD_LIBRARY_PATH:该环境变量主要用于指定查找共享库(动态链接库)时除了默认路径之外的其它路径。当运行函数动态链接.so时,假设此文件不在缺省文件夹下‘/lib’
and ‘/usr/lib’.那么就须要指定环境变量LD_LIBRARY_PATH。

假如如今须要在已有的环境变量上加入新的路径名,则採用例如以下方式:

LD_LIBRARY_PATH=NEWDIRS:$LD_LIBRARY_PATH.(NEWDIRS是新的路径串)

显式调用

显式调用须要包括头文件#include <dlfcn.h>。

涉及到以下几个函数:dlopen()、dlsym()、dlerror()、dlclose()。

dlopen()函数以指定模式打开指定的动态链接库文件,并返回一个句柄给dlsym()的调用进程。

使用dlclose()来卸载打开的库。当动态链接库操作函数运行失败时,dlerror能够返回出错信息,返回值为NULL时表示操作函数运行成功。

编译时候要增加 -ldl (指定dl库)

详细的函数原型例如以下:

void *dlopen(const char *filename, int flag);

char *dlerror(void);

void *dlsym(void *handle, const char *symbol);

int dlclose(void *handle);

dlopen以指定模式打开指定的动态连接库文件。并返回一个句柄给调用进程,dlerror返回出现的错误,dlsym通过句柄和连接符名称获取函数名或者变量名,dlclose来卸载打开的库。

如果已经生成libcaculate.so库,里面定义了add(),sub(),mul(),div()等函数。这里给出调用演示样例:

#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h> //动态链接库路径
#define LIB_CACULATE_PATH "./libcaculate.so" //函数指针
typedef int (*CAC_FUNC)(int, int); int main()
{
void *handle;
char *error;
CAC_FUNC cac_func = NULL; //打开动态链接库
handle = dlopen(LIB_CACULATE_PATH, RTLD_LAZY);
if (!handle) {
fprintf(stderr, "%s\n", dlerror());
exit(EXIT_FAILURE);
} //清除之前存在的错误
dlerror(); //获取一个函数
*(void **) (&cac_func) = dlsym(handle, "add");
if ((error = dlerror()) != NULL) {
fprintf(stderr, "%s\n", error);
exit(EXIT_FAILURE);
}
printf("add: %d\n", (*cac_func)(2,7)); cac_func = (CAC_FUNC)dlsym(handle, "sub");
printf("sub: %d\n", cac_func(9,2)); cac_func = (CAC_FUNC)dlsym(handle, "mul");
printf("mul: %d\n", cac_func(3,2)); cac_func = (CAC_FUNC)dlsym(handle, "div");
printf("div: %d\n", cac_func(8,2)); //关闭动态链接库
dlclose(handle);
exit(EXIT_SUCCESS);
}

4 静态链接库

仍使用刚才的hello.c和test.c。

1. gcc -c hello.c 注意这里没有使用-shared选项

2. 把目标文件归档    ar -r libhello.a hello.o

    程序 ar 配合參数 -r 创建一个新库 libhello.a 并将命令行中列出的对象文件插入。採用这样的方法,假设库不存在的话,參数 -r 将创建一个新的库。而假设库存在的话,将用新的模块替换原来的模块。

3. 在程序中链接静态库

           gcc test.c -lhello -L. -static -o hello.static

或者   gcc test.c libhello.a -L. -o hello.static

生成的hello.static就不再依赖libhello.a了LD_LIBRARY_PATH

GCC 编译使用动态链接库和静态链接库的方法的更多相关文章

  1. [转载]GCC 编译使用动态链接库和静态链接库--及先后顺序----及环境变量设置总结

    来自http://blog.csdn.net/benpaobagzb/article/details/51364005 GCC 编译使用动态链接库和静态链接库 1 库的分类 根据链接时期的不同,库又有 ...

  2. GCC 编译使用动态链接库和静态链接库

    1 库的分类 根据链接时期的不同,库又有静态库和动态库之分. 静态库是在链接阶段被链接的(好像是废话,但事实就是这样),所以生成的可执行文件就不受库的影响了,即使库被删除了,程序依然可以成功运行. 有 ...

  3. 【转】gcc 编译使用动态链接库和静态链接库

    1 库的分类 根据链接时期的不同,库又有静态库和动态库之分. 静态库是在链接阶段被链接的(好像是废话,但事实就是这样),所以生成的可执行文件就不受库的影响了,即使库被删除了,程序依然可以成功运行. 有 ...

  4. 利用GCC编译器生成动态链接库和静态链接库

    转载请标明:http://www.cnblogs.com/winifred-tang94/ 1.编译过程 gcc –fPIC –c xxx.c 其中-fPIC是通知gcc编译器产生位置独立的目标代码. ...

  5. Linux GCC编译使用动态、静态链接库 (转)

    原文出处:http://blog.csdn.net/a600423444/article/details/7206015 在windows下动态链接库是以.dll后缀的文件,二在Linux中,是以.s ...

  6. Linux 动态链接库包含静态链接库的方法

    今天老司机们在讨论一个编译问题  A是一个静态库  C是一个动态库  B是运行程序,能不能将A打包到C 然后B只需要链接C 就可以了. 这个问题我以前在出来zlib库版本冲突的时候有点印象,所以写了个 ...

  7. Linux下动态链接库和静态链接库

    第一部分:编译过程 先了解一下linux下C代码的编译过程,C代码的编译,一般分成四个阶段,包括:预编译,编译,汇编和链接,这四个阶段的分工是 预处理过程,负责头文件展开,宏替换,条件编译的选择,删除 ...

  8. Qt 共享库(动态链接库)和静态链接库的创建及调用

    前言: 编译器 Qt Creator, 系统环境 win7 64 位 1.创建共享库: 新建文件或项目->选择 Library 和 c++ 库->选择共享库->下一步(工程名为 sh ...

  9. GCC编译过程与动态链接库和静态链接库

    1. 库的介绍 库是写好的现有的,成熟的,可以复用的代码.现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常. 本质上来说库是一种可执行代码的二进制形式,可 ...

随机推荐

  1. JUnit4的使用2

    package com.imooc.test.aware; import org.junit.Test; import org.junit.runner.RunWith; import org.jun ...

  2. float与position

    使用float会使块级元素的宽高表现为包裹内容(在不设定宽高的情况下)  这是当然的  我们使用float就是使几个div排在一行 当然不可能在宽度上撑满父元素啦  至于高度 不论有没有float 高 ...

  3. 17.2?Replication Implementation 复制实施:

    17.2?Replication Implementation 复制实施: 17.2.1 Replication Implementation Details 17.2.2 Replication R ...

  4. linux 命令之sar——监视系统状态

    摘要:在进行系统或者内核测试的时候,我们经常需要观察cpu利用率,缓冲区使用情况,文件读写情况等等.在linux系统下,我们可以用sar命令来达到这个要求. sar 命令行的常用格式: sar [op ...

  5. windows phone:使用sqlite-net

    继上篇文章后,这里简单介绍下sqlite-net的使用(示例不为作者所写,摘自于:https://github.com/peterhuene/sqlite-net) Please consult th ...

  6. swift:打造你自己的折线图

    看到苹果Health里的折线图了吗.我们就是要打造一个这样的折线图.没看过的请看下图. 我们的主题在于折线图本身.其他的包括步数.日平均值等描述类的内容这里就不涉及了. 首先观察,这个图种包含些什么组 ...

  7. html的头部标签详解

    <!DOCTYPE html> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" &q ...

  8. ETHERNET帧结构

    以太网帧http://blog.csdn.net/guoshaobei/article/details/4768514 Ethernet的帧格式 (转)  http://jiangqiaosun.bl ...

  9. Ubuntu15.04上为火狐浏览器安装Adobe Flash Player插件

    前言:最新版的ubuntu好像没有flashplayer,而且更新源也无法更新成功,找些资料终于发现 这个需要自己手动配置.由于flashplayer无法安装,导致视频,百度上传等功能都无法使用: 安 ...

  10. 通过SecureCRT和PuTTY连接臻云CentOS版云主机

    原文地址:http://jingyan.baidu.com/article/fa4125acb6648128ac7092dc.html 如何通过SecureCRT和PuTTY工具远程连接臻云CentO ...