Linux使用静态库和动态库

Linux使用静态库和动态库

(一)库的概念

库是可以复用的代码,在一些大的项目中常常会用到库。
本质上说:库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。
一般说库是说两种:
静态库:linux下.a文件、windows下.lib文件
动态库:linux下.so文件、windows下.dll文件
最近花了一些时间把linux下编译、链接等研究了一下,作为一个菜鸟记录并分享一蛤。


(二)静态库与动态库

程序的编译运行要经过以下步骤:

1.源文件(.h .cpp等)
2.预编译
3.编译
4.汇编(.o文件)
5.链接
6.运行

在上述过程中,静态库是在链接阶段,将汇编生成的目标文件.o与引用到的库一起打包到可执行文件中,此种链接方式被称为静态链接。静态库其实一组目标文件(.o/.obj)的集合,即很多目标文件经过压缩打包后形成的一个文件,有一个说法称为:Archive即归档合并到一起。静态库在链接过程中被一起打包到可执行文件中,因此程序在运行时与函数库再无瓜葛,移植起来就很方便,但也因为这个原因会浪费空间资源,因为所有相关的目标文件与牵涉到的函数库都会被链接成一个可执行文件,如果10个100个可执行文件使用同一个静态库,就会造成大大的浪费;并且由于静态库是需要链接进入应用程序的,因此一旦对静态库进行修改,就需要对整个程序重新编译。

与静态库不同,动态库不会被链接打包到目标代码中,而是在程序运行时才被载入。这样就解决了静态库浪费空间的缺点,动态库把对一些库函数的链接载入推迟到程序运行的时期,在内存中只存在一份拷贝,这样可以实现进程之间的资源共享。

在linux和windows下静态库和动态库的概念以及使用方法有所不同,之后会慢慢学习分享。


(三)Linux下使用静态库

首先知道一个概念,也就是.h头文件,静态库.a文件的关系:
.h头文件是编译时必须的,声明函数接口,编译时通过引用.h文件找到函数声明。
.a文件是linux下静态库文件的后缀,通常以libxxxx.a的形式出现,在链接时必不可少。

下面编写一个简单的四则运算库:
首先是声明部分:

//StaticMath.h
#pragma once
class StaticMath
{
public:
    StaticMath(void);
    ~StaticMath(void);

    static double add(double a,double b);
    static double sub(double a,double b);
    static double mul(double a,double b);
    static double div(double a,double b);

    void print();
};

接着编写对应的.cpp文件实现上述接口声明:

//StaticMath.cpp
#include "StaticMath.h"
double StaticMath::add(double a,double b)
{
    return a+b;
}

由于静态库是目标文件的集合,那么我们现在需要对上述文件进行预编译->编译->汇编步骤,生成对应的.o文件,通过g++ --help命令知道参数-c的描述是:

这里写图片描述

因此执行命令:

g++ -c StaticMath.cpp

将代码文件编译汇编成目标文件StaticMath.o
然后,使用ar工具将目标文件打包成.a形式的静态库文件,这里的ar实际上可以理解为Archive的简写:

ar -crv libstaticmath.a StaticMath.o

如此生成静态库文件libstaticmath.a。

PS:在以前做过的嵌入式项目里面,静态库的生成和使用大多通过一些自动化工具例如autogen、cmake等,一直没有搞懂其真正的原理,这下也算学习了一蛤。

最后编写测试代码使用静态库

//test.cpp
#include <iostream>
#include "StaticMath.h"
int main()
{
    double a=1.2;
    double b=2.4;
    std::cout << "a+b="<<StaticMath::add(a,b)<<std::endl;
    return 0;
}

因为在链接的过程中要使用静态库,这里编译的时候需要加入静态库所在目录和静态库的名字来作为编译选项,否则会出现问题:

g++ -o test test.cpp -L ./ -lstaticmath

最后可执行文件test正常执行:

这里写图片描述


(四)Linux下使用动态库

动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入。不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例,规避了空间浪费问题。动态库在程序运行是才被载入,也解决了静态库对程序的更新、部署和发布页会带来麻烦。用户只需要更新动态库即可,增量更新。

同样的首先编写库文件。
声明:

//DynamicMath.h
#pragma once
class DynamicMath
{
    public:
    DynamicMath();
    ~DynamicMath();


    static  double add(double a,double b);
    static  double sub(double a,double b);
    static  double mul(double a,double b);
    static  double div(double a,double b);
};

实现:

//DynamicMath.cpp
#include "DynamicMath.h"
double DynamicMath::add(double a,double b)
{
    return a+b;
}

生成动态库libxxxx.so

g++ DynamicMath.cpp -fPIC -shared -o libdynamicmath.so

-fPIC 创建与地址无关的编译程序(pic,position independent code),是为了能够在多个应用程序间共享。
-shared指定生成动态链接库。

使用动态库,测试代码:

//test.cpp
#include <iostream>
#include "DynamicMath.h"
int main()
{
    double a=1.2;
    double b=2.4;
    std::cout << "a+b =" << DynamicMath::add(a,b)<<std::endl;
    return 0;
}

跟静态库使用类似,编译测试文件并加上合适的编译选项,生成可执行文件,并且通过ldd命令查看应用程序依赖的动态库及其相关信息:

这里写图片描述

最后动态库的使用有一个需要注意的地方:
动态库是在运行时加载,这时候需要一个动态载入器(dynamic linker/loader),来得知动态库的绝对路径。
对于elf格式的可执行程序,是由ld-linux.so*来完成的,它先后搜索elf文件的 DT_RPATH段—环境变量LD_LIBRARY_PATH—/etc/ld.so.cache文件列表—/lib/,/usr/lib 目录找到库文件后将其载入内存。
什么意思呢?就是首先有一堆配置文件可以配置动态载入库搜索动态库的路径,ld会在这些路径中去寻找依赖的动态库,这样就有两个解决办法:
1. 修改配置文件(/etc/ld.so.conf)添加我们创建的动态库的目录
2. 将我们创建的动态库(.so)拷贝到默认的动态库目录下/lib或者/usr/lib

关于上述第1点,我们可以这样做,在终端

添加动态库目录
 echo /home/zhangxiao/learnlib/dynamiclib/ >> /etc/ld.so.conf
配置生效
ldconfig

关于上述第2点,直接将生成动态库拷贝到/usr/lib目录下即可。

参考:

http://www.cnblogs.com/skynet/p/3372855.html
http://blog.csdn.net/yusiguyuan/article/details/12649737

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值