前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >详解动态库和静态库

详解动态库和静态库

作者头像
南桥
发布2024-06-03 08:46:32
1010
发布2024-06-03 08:46:32
举报
文章被收录于专栏:南桥谈编程南桥谈编程

前言

在C、C++中我们使用过标准库,比如在使用strerrorvectorstring等时,都只是调用了这些函数接口,这些都是需要具体的实现。

让我们来看看C语言库:

将来运行程序,需要二进制文件和库文件

看下C++标准库:

在Linux系统中,.so结尾是动态库,以.a结尾是静态库; 在Windows中,.dll结尾是动态库,lib结尾是静态库。

静态库

静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库。

.c文件可形成一个.o文件,将这些.o文件链接形成可执行文件,头文件是一个手册,提供函数声明,告诉用户如何使用,.o文件提供实现,我们只需要补充一个main文件,调用头文件提供的方法,然后和.o文件进行连接,就能形成可执行文件。

静态库制作

简单制作一个静态库

代码语言:javascript
复制
// add.h
#pragma once

int add(int x, int y);
代码语言:javascript
复制
// add.c
#include "add.h"

int add(int x, int y)
{
    return x + y;
}
代码语言:javascript
复制
// sub.h
#pragma once

int sub(int x, int y);
代码语言:javascript
复制
// sub.c
#include "sub.h"

int sub(int x, int y)
{
    return x - y;
}

静态库的生成

静态库生成指令:ar -rc lib静态库名.a 需要形成静态库的文件ar是gnu归档工具,rc表示replace and create

静态库的形成本质上是将所有的.o文件打包,因此需要先生成.o文件

发布静态库

发布静态库就是自己的lib拷贝给比人

例如上图是我自己制作的一个简单静态库,只需要将mylib拷贝给别人即可。

使用静态库

将自己的mylib拷贝到一个test文件夹中,然后写一个main.c文件,用于测试静态库的使用

mian.c:

代码语言:javascript
复制
#include "add.h"    
#include "sub.h"                                                                      
#include <stdio.h>           
                               
  int main()                   
  {                            
      printf("1 + 2 = %d\n", add(1, 2));    
      printf("1 - 2 = %d\n", sub(1, 2));    
                               
      return 0;                                                        
  } 

编译:

这里报错了,说找不到对应的头文件 头文件一般有以下两种方式来包含头文件:

  1. 使用<>来包含头文件,表示到系统指定目录下去查找头文件
  2. 使用" "来包含头文件,这种方式一般用于包含自己所写的头文件中,表示在当前源文件的统计目录下查找头文件,找到了就用,没找到再去系统指定目录下进行查找,所以对于库提供的头文件我们也可以使用 " " 进行包含。

main.c文件中,就是使用" "来包含我所写的头文件,但是还是会报错,理由:使用" "所包含的头文件,会告诉编译器在main.c同级目录下(即test目录下)查找对应的头文件,但是add.hsub.h文件在test文件中的mylib文件中,因此无法找到。

解决上述有三种方式:

  1. 将头文件直接拷贝到当前目录下
  2. 在代码中头文件的路劲补全,如:#include " /mylib/include/add.h "
  3. 在执行 gcc 指令编译的时候加上 -I 选项,指定编译器搜索头文件的路径 系统默认的指定路劲:/usr/include

使用方法3:gcc main.c -I ./mylib/include

此时依然没有编译成功,此时不是找不到头文件,而是链接错误。gcc在编译的时候,只会去默认路径下查找打包的头文件,不会去/mylib/include中查找,编译器在gcc是就找不到我的酷libmyc.a,也就是编译链接失败。

此时可以形成main.o文件:

解决此错误有两种方法:

  1. 将我们的库拷贝到系统的指定路径下,并不能完全解决,还需要指定库的名称
  2. 在使用 gcc 的时候添加对应的选项
  • -L 指定库路径
  • -l 指定库名

为什么在搜索头文件的时候仅需指定路径呢?当你编译程序时,编译器会首先在这些默认路径下搜索所需的头文件。 在代码中已经写了头文件的具体名称,所以仅需指定头文件的路径即可。而一个路径下可以有多个库,如果只指定路劲,编译器还是不知道该去链接哪个库,因此还要在后面使用 -l 选项指定待链接的库的具体名称。

注意:去掉前缀 lib 和 后缀 .a 才是一个库的名称,建议 -l 后面紧跟库的名称。一般在使用第三方库的时候,可能不需要带 -I 或者 -L,但是 -l 指定库的名称是一定需要到,因为 gcc 默认只能找到系统调用和语言层面的库。

安装静态库

静态库的安装本质上就拷贝到系统的特定目录下。

卸载静态库

卸载静态库本质是将.h文件和自己的静态库从默认的路劲中删除,此时就无法通过静态库来运行程序。

动态库

动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。

动态库的制作

简单制作一个动态库

代码语言:javascript
复制
// add.h
#pragma once

int add(int x, int y);
代码语言:javascript
复制
// add.c
#include "add.h"

int add(int x, int y)
{
    return x + y;
}
代码语言:javascript
复制
// sub.h
#pragma once

int sub(int x, int y);
代码语言:javascript
复制
// sub.c
#include "sub.h"

int sub(int x, int y)
{
    return x - y;
}

动态库的生成

  • fPIC:产生位置无关码(position independent code)
  • shared: 表示生成共享库格式
  • 库名规则:libxxx.so

libmyc.so就是生成的动态库

动态库的发布

将动态库拷贝到刚刚我们静态库的位置

使用动态库

mian.c:

代码语言:javascript
复制
#include "add.h"    
#include "sub.h"                                                                      
#include <stdio.h>           
                               
  int main()                   
  {                            
      printf("1 + 2 = %d\n", add(1, 2));    
      printf("1 - 2 = %d\n", sub(1, 2));    
                               
      return 0;                                                        
  } 

按照静态库的使用方法,来使用静态库:

虽然生成了可执行文件,但是可执行文件出错了

使用ldd a.out时,发现libmyc.so => not found,动态库没有被找到,编译期间已经告诉系统对应的头文件以及库的位置,但是这是告诉编译器,没有告诉操作系统,因此编译通过,但是无法运行。 动态库要在程序运行的时候要找到动态库加载运行。静态库为什么没有这个问题?因为静态库在编译期间已经将库中的代码拷贝到可执行程序内部了,加载和库就没有关系了。

解决该问题,有以下四种方法:

  1. 将库文件拷贝到系统默认的库路(/lib64/usr/lib64),不推荐使用这种方法,因为修改了系统规定的库,降低了系统的健康指数
  2. 在系统默认的库路径(/lib64/usr/lib64)下建立软链接
  1. 将自己库所在的路径,添加到系统的环境变量 LD_LIBRARY_PATH 中,该环境变量就是专门用来搜索动态库的

但是重新启动系统后,就找不到该环境变量,如果想让系统启动时自动添加该路径到 LD_LIBRARY_PATH 环境变量中,可以通过修改 ~/.bash_profile 中的配置去实现,但是不推荐这么写,不建议修改环境变量。

  1. 如果想让我们的库和系统、语言自带的库一样,在程序运行的时候可以自动被找到,那我们可以/etc/ld.so.conf.d 路径下添加一个 .conf 结尾的配置文件

该配置文件里面的内容就是我们自己动态库所在的路径。添加完后执行 ldconfig 指令,将所有的配置文件重现加载一下,然后程序就能够正常运行。

此时程序就可以正常运行:

动态库VS静态库

动静态库同时存在时,默认连接的是动态库:

此时对应的可执行程序的体积很小:

那么如何使用静态库? 只需在后面加一个-static

此时对应的可执行文件体积很大:

如果你没有使用-static并且只提供.a,只能静态库连接当前的.a库,其他库正常动态连接。

-static的意义是什么呢? 必须强制的将程序进行静态连接,这就要求连接的任何库都必须提供对应的静态库版本。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-06-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 静态库
    • 静态库制作
      • 静态库的生成
        • 发布静态库
          • 使用静态库
            • 安装静态库
              • 卸载静态库
              • 动态库
                • 动态库的制作
                  • 动态库的生成
                    • 动态库的发布
                      • 使用动态库
                      • 动态库VS静态库
                      领券
                      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档