函数库的概念

函数库一般分为静态库和动态库两种。

静态库:
是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了。其后缀名一般为”.a”。

动态库:
与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库,这样可以节省系统的开销。动态库一般后缀名为”.so”,gcc/g++在编译时默认使用动态库。无论静态库,还是动态库,都是由.o文件创建的。

动态库的编译

下面通过一个例子来介绍如何生成一个动态库。
建一个头文件:dynamic.h
三个.cpp文件:dynamic_a.cpp、dynamic_b.cpp, dynamic_c.cpp
我们将这几个文件编译成一个动态库:libdynamic.so。

//dynamic.h
#ifndef __DYNAMIC_H_
#define __DYNAMIC_H_
#include <iostream>
void dynamic_a();
void dynamic_b();
void dynamic_c();
#endif
//dynamic_a.cpp:

#include"dynamic.h"
void dynamic_a()
{
  cout<<"this is in dynamic_a "<<endl;
}

//dynamic_b.cpp:
#include"dynamic.h"
void dynamic_b()
{
  cout<<"this is in dynamic_b "<<endl;
}
//dynamic_c.cpp:

#include"dynamic.h"
void dynamic_c()
{
  cout<<"this is in dynamic_c "<<endl;
}

将这几个文件编译成动态库libdynamic.so。编译命令如下:

g++ dynamic_a.cpp dynamic_b.cpp dynamic_c.cpp -fPIC -shared -o libdynamic.so

参数说明:
-shared:该选项指定生成动态连接库
-fPIC:表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的所以动态载入时是通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的。

在上面的部分,我们已经生成了一个libdynamic.so的动态链接库,现在我们用一个程序来调用这个动态链接库。
文件名为:main.cpp

//main.cpp:
#include"dynamic.h"
int main()
{
  dynamic_c();
  dynamic_c();
  dynamic_c();
  return 0;
}

将main.cpp与libdynamic.so链接成一个可执行文件main。命令如下:

g++ main.cpp -L. -ldynamic -o main

参数说明:
-L.:表示要连接的库在当前目录中
-ldynamic:编译器查找动态连接库时有隐含的命名规则,即在给出的名字前面加上lib,后面加上.so来确定库的名称

测试可执行程序main是否已经链接的动态库libdynamic.so,如果列出了libdynamic.so,那么就说明正常链接了。可以执行以下命令:

ldd main

如果运行:

./main

出现错误: error while loading shared libraries: libdynamic.so: cannot open shared object file: No such file or directory

错误原因:
ld提示找不到库文件,而库文件就在当前目录中。

链接器ld默认的目录是/lib和/usr/lib,如果放在其他路径也可以,需要让ld知道库文件在哪里。

解决方法:
方法1:
编辑/etc/ld.so.conf文件,在新的一行中加入库文件所在目录;比如笔者应添加:
/home/neu/code/Dynamic_library

运行:

sudo ldconfig

目的是用ldconfig加载,以更新/etc/ld.so.cache文件;

方法2:
在/etc/ld.so.conf.d/目录下新建任何以.conf为后缀的文件,在该文件中加入库文件所在的目录;
运行:

sudo ldconfig

以更新/etc/ld.so.cache文件;

ld.so.cache的更新是递增式的,就像PATH系统环境变量一样,不是从头重新建立,而是向上累加。除非重新开机,才是从零开始建立ld.so.cache文件。

方法3:
在bashrc或profile文件里用LD_LIBRARY_PATH定义,然后用source加载。

方法4:
你可以直接采用在编译链接的时候告诉系统你的库在什么地方

执行main可以看看main是否调用了动态链接库中的函数。

./main

静态库的编译

就以以上代码演示,最好把生成的动态库的东西全部删掉。

1.编译静态库:

g++ -c dynamic_a.cpp dynamic_b.cpp dynamic_c.cpp  

2.使用ar命令创建静态库文件(把目标文档归档)

ar cr libstatic.a dynamic_a.o dynamic_b.o dynamic_c.o  //cr标志告诉ar将object文件封装(archive)

参数说明:

      d 从指定的静态库文件中删除文件 
      m 把文件移动到指定的静态库文件中 
      p 把静态库文件中指定的文件输出到标准输出 
      q 快速地把文件追加到静态库文件中 
      r 把文件插入到静态库文件中 
      t 显示静态库文件中文件的列表 
      x 从静态库文件中提取文件 
      a 把新的目标文件(*.o)添加到静态库文件中现有文件之后 

使用nm -s 命令来查看.a文件的内容

nm -s libstatic.a 

3.链接静态库

g++ main.cpp -lstatic -L. -static -o main//这里的-static选项是告诉编译器,hello是静态库也可以用

//g++ main.cpp -lstatic -L.  -o main 

执行以下命令,因为笔者还是用的动态库的代码,所以结果一样

./main

动态库相对于静态库的优点和缺点

优点

  1. 动态链接库有利于进程间资源共享,节省了内存。当某个程序在运行中要调用某个动态链接库函数的时候,操作系统首先会查看所有正在运行的程序,看在内存中是否已经有此库函数的拷贝了。如果有,则共享这个拷贝,如果没有时才链接载入。对于静态库来说,如果系统中多个程序都要调用某个静态链接库的函数时,则每个库都要把这个库函数拷贝到自己的代码段中,这显然将占有更大的内存资源。
  2. DLL文件与EXE文件独立,只要输出接口不变(即名称、参数、返回值类型和调用约定不变),更换DLL文件不会对EXE文件造成任何影响,因而极大地提高了可维护性和可扩展性。

缺点

  1. 使用动态链接库的应用程序不是自完备的,它依赖的DLL模块也要存在,如果没有DLL模块,程序会加载失败。
  2. 运行速度比静态链接慢。
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐