当前位置: 首页 > news >正文

【Linux】动静态库

目录

写在前面的话

如何编写静态库库

编写静态库

ar命令

Makefile自动化形成静态库

如何使用编写的静态库

1.拷贝到系统路径中

2.指定路径搜索

如何编写动态库

编写动态库

完善Makefile

如何使用编写的动态库

指定路径搜索(不可行及原因)

环境变量LD_LIBRARY_PATH

修改配置文件


写在前面的话

        本文章主要讲解了动静态库的编写以及使用。在了解静态链接和动态链接的基础上,观看本文的效果会更好。

        欢迎阅读我之前写的:动静态链接,里面有关于动态和静态链接的详细介绍,而且也有对动静态库的一些基本认识,欢迎阅读哦.

如何编写静态库

        这里我们将编写两个类型的库:动态库(.so) 和 静态库(.a).

编写静态库

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

        需要说明:库里不能包含main函数,因为是要别人用的。写main函数的话就会和别人的main函数冲突了.

        我现在分别创建了4个文件:mymath.h  mymath.c  myprint.h  myprint.c.

分别写入以下内容:

  • mymath.h:
#pragma once    
#include<stdio.h>    extern int addToTarget(int start, int end);//声明函数
  • mymath.c
  #include "mymath.h"                                             #             //这段函数的作用是计算start-end之间的和int addToTarget(int start, int end)                               {                                                                int sum = 0;                                                   int i = start;                                                 for(i; i <= end; i++){                                                                                                                                 sum += i;                                                    }                                                              return sum;                                                    }  
  • myprint.h
#pragma once     #include<stdio.h>    
#include<time.h>    extern void Print(const char* str);  
  • myprint.c
#include "myprint.h"                                                            //这段函数功能是输出传入的字符串,后面加上时间戳                                                                    
void Print(const char* str)                                                          
{printf("%s[%d]\n",str,(int)(time(NULL)));
}       

        然后我们编写一个main.c文件,来调用这些函数.

#include "myprint.h"    
#include "mymath.h"    int main()    
{    Print("hello,world");                                                                                                               int res = addToTarget(1,100);    printf("res: %d\n",res);    return 0;    
}    

         指令编译所有.c文件,目标生成my.exe可执行文件:

gcc main.c mymath.c myprint.c -o my.exe

        然后我们执行my.exe文件,得到了我们预期的结果:

         这只是我们的正常使用,毕竟源代码.c文件就在我们手上。


        当我们把.h文件和 .c文件编译成的.o文件,给别人,别人可以用吗?

        答案是可以的,我们首先把两个.c文件编译成.o文件:

gcc -o myprint.o -c myprint.c
gcc -o mymath.o -c mymath.c

        然后我们换到另一个文件中,把两个.o和两个.h文件复制到这个文件中,然后将main.c也编译成main.o文件,此时我们将这三个.o文件一起编译,形成可执行程序。

gcc main.o mymath.o myprint.o -o my.exe

        我们发现照样可以成功运行:


        但问题是我们这里有太多的.o文件,发给别人也不方便,而且容易丢失,编译的时候还得都写上,这也未必太麻烦了。

所以此时我们需要把这些.o文件打包,这个打包的过程就是形成静态库的过程. 

ar命令

        把这些.o文件打包需要用到ar命令,命令格式如下:

ar -[选项] lib+库文件名+.a 所有.o文件

选项这里只说r(replace替换),c(create创建)就可以了,就足以让我们创建静态库了.

        其中需要注意的是:要形成的静态库的名字 前缀必须是lib,后缀必须是.a,中间可以随便起名字.

        比如我想把所有的.o文件打包成名字叫hello的静态库,指令如下:

ar -rc libhello.a main.o myprint.o mymath.o

这样就形成了.

Makefile自动化形成静态库

        有了以上的认知,我们便对一个文件形成静态库的过程有了大概的了解:

 .c文件 ---> .o文件 ---> 打包形成静态库

既然是这么一套固定的流程,那么我们完全可以用Makefile来完成这些工作。需要注意的是搞清各个文件的依赖关系,然后再进行编写。所以最后的Makefile编写如下:

libhello.a: mymath.o myprint.o                                                                                                        ar -rc libhello.a mymath.o myprint.o    
mymath.o : mymath.c    gcc -o mymath.o -c mymath.c    
myprint.o: myprint.c    gcc -o myprint.o -c myprint.c    .PHONY:clean    
clean:    rm -rf *.o libhello.a    

        这样,我们不需要再手动编译了,直接使用make就好了.


        但这样只是将.o文件打包了,还有.h文件,就是通常#include<>的那些,一般都是.h文件

.h声明,然后从.o打包形成的库里面找

所以我们还需要做一件工作:将.h和.o文件规整到一起.在Makefile加入以内容:

.PHONY:hello    
hello:    mkdir -p hello/lib    mkdir -p hello/include    cp -rf *.h hello/include    cp -rf *.a hello/lib  

        即创建了三个目录,hello,lib和include,把当前所有的.h文件放到include文件夹中,所有.a库文件放到lib文件夹中。

 这里首先把资源全部清理一下,然后make编译形成.o文件,再make hello 形成hello目录,里面包含了两个目录include 和 lib ,分别保存.h文件和库文件.


如何使用编写的静态库

1.拷贝到系统路径中

        头文件gcc系统默认搜索路径是:/usr/include

        库文件gcc系统默认搜索路径是:/lib64 或者 /usr/lib64

sudo cp hello/include/*.h /usr/include/    //拷贝头文件
sudo cp hello/lib/*.a /lib64               //拷贝库文件

拷贝完成后,我们再次编译main.c

        发现还是不行,这是由于我们平常使用的是系统自带的库,而我们编写的是第三方库,所以我们编译时需要指定链接哪一个库.

gcc main.c -lhello

        注意,-l后面直接加库的名字,不加前缀lib和后缀.a.

        此时我们再编译,便运行成功了.

 这种把自己的库拷贝到系统路径下的行为就叫做库的安装.

但是一般不建议采用这种办法。会污染别人已经写好的库。

2.指定路径搜索

我们可以直接在编译的时候加上头文件和库文件的路径.

gcc main.c -I ./hello/include/ -L ./hello/lib/ -lhello

        选项:-I(i的大写)表示头文件的搜索路径

                   -L表示库文件的搜索路径

                   -l(小写的L)表示在特定路径下,要使用哪一个库. 

如何编写动态库

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

编写动态库

        同样地,我么也需要先将.c文件编译成.o文件,但此时需要加上选项  -fPIC.

gcc -fPIC -c myprint.c -o myprint.o
gcc -fPIC -c mymath.c -o mymath.o

选项-fPIC是指生成位置独立代码(Position Independent Code,PIC)。它通常用于创建可在不同内存地址空间加载的共享库(动态链接库)。

具体来说,-fPIC选项的作用是告诉编译器生成与位置无关的代码,这些代码可以在内存中的任何位置加载和执行,与共享库的加载地址无关。这是通过使用相对偏移而不是绝对地址来访问全局变量和函数等的。

看不懂没有关系,我们后面会继续细说的.

然后我们需要将这些.o文件它打包成动态库.它没有单独的命令,使用gcc进行打包,但是需要加上 -shared的选项,如下:

gcc -shared myprint.o mymath.o -o libhello.so

        这样我们就完成了动态库的打包和编写.

       由于流程是固定的,所以我们依然可以使用Makefile来完成。

完善Makefile

        首先我们需要先将.c编译诚.o文件,并打包成动态库

        然后需要将生成的动态库也放到我们之前创建的lib目录中。

        完善后的效果如下(框起来的是新增的内容):

.PHONY:all    
all:libhello.so libhello.a    libhello.so:mymath_d.o myprint_d.o    gcc -shared mymath_d.o myprint_d.o -o libhello.so    
mymath_d.o:mymath.c    gcc -c -fPIC mymath.c -o mymath_d.o    
myprint_d.o:myprint.c    gcc -c -fPIC myprint.c -o myprint_d.o                                                                                               libhello.a: mymath.o myprint.o    ar -rc libhello.a mymath.o myprint.o    
mymath.o : mymath.c    gcc -o mymath.o -c mymath.c    
myprint.o: myprint.c    gcc -o myprint.o -c myprint.c    .PHONY:output    
output:    mkdir -p hello/lib    mkdir -p hello/include    cp -rf *.h hello/include    cp -rf *.a hello/lib    cp -rf *.so hello/lib    
.PHONY:clean    
clean:    rm -rf *.o libhello.a output  

 

         此时我们即可以形成动态库,也可以形成静态库.

        然后此时我们make编译,再make output将文件拷贝到指定目录中.

 此时便把动态库也成功放入到lib路径下了.

此时我们便可以利用tar指令打包发送到网上,然后让别人使用了。

如何使用编写的动态库

指定路径搜索(不可行及原因)

首先,静态库中的第一种拷贝到系统路径里这种方法一定是可行的.

第二种方法,这条指令:

gcc main.c -I ./hello/include/ -L ./hello/lib/ -lhello

我们首先要知道,gcc默认使用的是动态链接,即使用动态库.

        a.当库中只有静态库时,gcc就只能针对该库进行静态链接.

        b.当动静态库同时存在时,默认使用的就是动态库

        c.如果动静态库同时存在,强制使用静态库,那就需要再上面指令后面加上 -static

表示的是:摒弃优先使用动态库的原则,而是直接使用静态库的方法.

代码如下:

gcc main.c -I ./hello/include/ -L ./hello/lib/ -lhello -static

 我们同样地利用上面那一条指令进行编译生成可执行程序后,我们把可执行程序移动到上一级目录.然后运行它。

 此时我们再运行,发现报错了,就是找不到这个共享的文件,这是为什么呢、我们不是已经指定路径了吗,为什么还找不到呢?

        我们要明白,动态库是一个独立的文件,动态库可以和可执行程序分批加载!

        而我们指定路径是给gcc指定查找的,是告诉了gcc的动态库的路径。

        当我们运行和加载的时候,就和gcc没有关系了,所以我们要告诉操作系统或者加载器  这个动态库的路径!

        如果此时新来一个进程,也需要用相同的动态库,操作系统只需要将物理内存中动态库数据经过页表映射到对应进程的地址空间 中的共享区的位置,这样代码区的代码遇到动态库中的函数直接去共享区去找。

        

那为什么静态库不用呢?

        静态链接的一旦编译好,就和库没有关系了,因为已经把库中代码拷贝到自己写的C/C++代码中了.即已经在可执行程序中了.

环境变量LD_LIBRARY_PATH

        这个是库加载时搜索的路径.我们只需要把我们的库的路径导入到这个环境变量里面即可.

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:你的库文件路径

导入完成后,此时我们便可以成功运行了

        但这个方法有一个缺点,就是每次退出后,我们导入的这些环境变量就会清空,下次还需要重新导入,比较麻烦。

修改配置文件

        我们既不想在系统中安装我们的库,又想永久保存,那该怎么办呢?

        我们只需要在/etc/ld.so.conf.d/路径下 随便创建一个文件.然后vim打开文件,输入你动态库文件路径,然后退出保存即可.

sudo vim /etc/ld.so.conf.d/mylib.conf

        然后输入你的库文件路径即可,然后退出,执行重新加载指令

sudo ldconfig

        这样也可以照样正常运行了.

        到这里动静态库就讲完了,感谢您的阅读哦~

相关文章:

【Linux】动静态库

目录 写在前面的话 如何编写静态库库 编写静态库 ar命令 Makefile自动化形成静态库 如何使用编写的静态库 1.拷贝到系统路径中 2.指定路径搜索 如何编写动态库 编写动态库 完善Makefile 如何使用编写的动态库 指定路径搜索(不可行及原因) 环境变量LD_LIBRARY_PAT…...

《kubernetes权威指南》-第一章学习笔记

1.什么是kubernetes&#xff1f; kubernetes是一个全新的基于容器技术的分布式架构领先方案。 2.为什么要用kubernetes&#xff1f; 使用kubernetes提供的解决方案能够减少30%的开发成本&#xff0c;并且能够将开发人员的精力更加集中于业务本身&#xff0c;同时可以降低系统…...

ubuntu 18.04 磁盘太满无法进入系统

安装了一个压缩包&#xff0c;装了一半提示磁盘空间少导致安装失败。我也没在意&#xff0c;退出虚拟机打算扩展硬盘。等我在虚拟机设置中完成扩展操作&#xff0c;准备进入虚拟机内部进行操作时&#xff0c;发现登录不进去了 shift 登入GUN GRUB设置项的问题 网上都是在开机…...

基于LNMP配置WordPress建站时出现的问题汇总

目录 wordpress上传文件报错问题描述原因分析&#xff1a;解决方案&#xff1a; wordpress裁剪图片报错问题描述原因分析&#xff1a;解决方案&#xff1a; 配置固定链接和伪链接 wordpress上传文件报错 WP内部错误&#xff0c;在上传文件时发生了错误&#xff0c;显示权限不足…...

【Spring Cloud】Gateway的配置与使用

文章目录 前言第一步&#xff0c;创建一个springboot工程第二步&#xff0c;添加依赖第三步&#xff0c;编写yml文件第四步&#xff0c;启动主启动类总结 前言 Gateway其实是springcloud 原生的东西&#xff0c;但是我还是想放在这里讲&#xff0c;因为我们使用nacos时&#x…...

概念、框架简介--ruoyi学习(一)

开始进行ruoyi框架的学习&#xff0c;比起其他的前后端不分离的&#xff0c;这个起码看的清晰一些吧。 这一节主要是看了ruoyi的官方文档后&#xff0c;记录了以下不懂的概念&#xff0c;并且整理了ruoyi框架中的相关内容。 一些概念 前端 store store是状态管理库&#x…...

IDEA的基础使用——【初识IDEA】

IDEA的基础使用——【初识IDEA】 文章目录 IDEA简介前言官网 IDEA的下载与安装选择下载路径勾选自己需要的其余按默认选项进行即可 目录简介安装目录简介 运行Hello WorldIDEA快捷键常用模板模板一&#xff1a;psvm&#xff08;main&#xff09;模板二&#xff1a;模板三&#…...

LeetCode刷题总结-动态规划篇

LeetCode刷题总结-动态规划篇 本文总结LeetCode上有动态规划的算法题&#xff0c;推荐刷题总数为54道。具体考点分析如下图&#xff1a; 1.中心扩展法 题号&#xff1a;132. 分割回文串 II&#xff0c;难度困难 2.背包问题 题号&#xff1a;140. 单词拆分 II&#xff0c;难…...

el-table使用xlsx实现导入文件编辑功能

需求&#xff1a;列表根据xlsx文件导入后&#xff0c;和列表进行对比&#xff0c;之后实现编辑功能 1.下载xlsx 我下的是之前的版本&#xff0c;新版不知道兼不兼容&#xff0c;这个包900多k npm install xlsx0.14.5 2.在需要使用表格导入的页面引入 import XLSX from &quo…...

Android9、11 有线网络开关设置

Android9、11 有线网络开关设置 Android9、11 有线网络开关设置_android 以太网开关_峥嵘life的博客-CSDN博客...

【MySQL】mysql问题 | [ERROR] unknown variable ‘column-statistics=0‘

一、说明 1、用到一个开源项目&#xff0c;dbBkTool[asurplus] 2、这个项目用于MySQL定时备份的 3、然后有个执行的时候&#xff0c;发下报错 [ERROR] unknown variable column-statistics0 二、解决 1、把MySQL客户端升级到8.0.19之后&#xff0c;就不报错了 2、column-stat…...

ElasticSearch 7.x

前言 elastic表示可伸缩&#xff0c;search表示查询。所以es的核心即为查询。通常情况下&#xff0c;我们的数据可以分为三类&#xff1a;结构化数据、非结构化数据、半结构化数据。 结构化数据&#xff1a;一般会用特定的结构来组织和管理数据&#xff0c;表现为二维表结构。…...

MVC乱码问题

RequestMapping(value "insert",produces {"text/html;charsetutf-8"}) //前端响应回去加响应头&#xff0c;解决乱码问题,这个还跟JSP响应头还不一样&#xff0c;这是响应的字符串&#xff0c;纯文本&#xff0c;那个前端的是out.Writer()对象&#xff…...

1004. 最大连续1的个数 III

题目描述&#xff1a; 主要思路&#xff1a; 刚看到这个问题首先想到的是二分答案&#xff0c;二分长度&#xff0c;然后利用滑动窗口判断是否可以达成。 class Solution { public:bool find(int x,vector<int> nums, int k){int now0;for(int i0,j0;i<nums.size();…...

【机器学习】西瓜书学习心得及课后习题参考答案—第3章线性模型

过了一遍第三章&#xff0c;大致理解了内容&#xff0c;认识了线性回归模型&#xff0c;对数几率回归模型&#xff0c;线性判别分析方法&#xff0c;以及多分类学习&#xff0c;其中有很多数学推理过程以参考他人现有思想为主&#xff0c;没有亲手去推。 术语学习 线性模型 l…...

面试官问我:一个 TCP 连接可以发多少个 HTTP 请求?我竟然回答不上来...

一道经典的面试题是从 URL 在浏览器被被输入到页面展现的过程中发生了什么&#xff0c;大多数回答都是说请求响应之后 DOM 怎么被构建&#xff0c;被绘制出来。但是你有没有想过&#xff0c;收到的 HTML 如果包含几十个图片标签&#xff0c;这些图片是以什么方式、什么顺序、建…...

树莓派Pico|RP2040|官方文档|在MS Windows上构建“Hello World”及环境配置

9.2. 在MS Windows上构建 在Microsoft Windows 10或Windows 11上安装工具链与其他平台有些不同。然而安装后&#xff0c;RP2040的构建代码基本类似。  警告 官方不支持在Windows 7或8上使用Raspberry Pi Pico&#xff0c;但在Windows 7或8上可以使其工作。 9.2.1. 安装工具…...

全球公链进展| 2023/7/31

一周速览 过去一周&#xff0c;明星项目动态如下&#xff1a; 第114次以太坊核心开发者共识会议&#xff1a;Devnet #8 最早下周推出 Layer2网络Shibarium跨链桥已上线公开测试 Optimism 推出「Law of Chains」v0.1 版本 Sui 通过 SIP#6 &#xff0c;允许开发人员构建流动…...

Spring源码(三)Spring Bean生命周期

Bean的生命周期就是指&#xff1a;在Spring中&#xff0c;一个Bean是如何生成的&#xff0c;如何销毁的 Bean生命周期流程图 1、生成BeanDefinition Spring启动的时候会进行扫描&#xff0c;会先调用org.springframework.context.annotation.ClassPathScanningCandidateCompo…...

【iOS】Cydia Impactor 错误:file http.hpp; line:37; what: _assert(code == 200)

Cydia Impactor 报错&#xff0c;信息如下 file http.hpp; line:37; what: _assert(code 200)解决方案&#xff1a;Cydia Impactor 已被弃用&#xff0c;切换到sideloadly 即可&#xff0c;亲测成功&#xff0c;并且支持双重验证登录 csdn备份地址 HERE...

Chapter03-Authentication vulnerabilities

文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...

C++ Visual Studio 2017厂商给的源码没有.sln文件 易兆微芯片下载工具加开机动画下载。

1.先用Visual Studio 2017打开Yichip YC31xx loader.vcxproj&#xff0c;再用Visual Studio 2022打开。再保侟就有.sln文件了。 易兆微芯片下载工具加开机动画下载 ExtraDownloadFile1Info.\logo.bin|0|0|10D2000|0 MFC应用兼容CMD 在BOOL CYichipYC31xxloaderDlg::OnIni…...

什么是Ansible Jinja2

理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具&#xff0c;可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板&#xff0c;允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板&#xff0c;并通…...

C++使用 new 来创建动态数组

问题&#xff1a; 不能使用变量定义数组大小 原因&#xff1a; 这是因为数组在内存中是连续存储的&#xff0c;编译器需要在编译阶段就确定数组的大小&#xff0c;以便正确地分配内存空间。如果允许使用变量来定义数组的大小&#xff0c;那么编译器就无法在编译时确定数组的大…...

Docker 本地安装 mysql 数据库

Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker &#xff1b;并安装。 基础操作不再赘述。 打开 macOS 终端&#xff0c;开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...

【笔记】WSL 中 Rust 安装与测试完整记录

#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统&#xff1a;Ubuntu 24.04 LTS (WSL2)架构&#xff1a;x86_64 (GNU/Linux)Rust 版本&#xff1a;rustc 1.87.0 (2025-05-09)Cargo 版本&#xff1a;cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...

C# 表达式和运算符(求值顺序)

求值顺序 表达式可以由许多嵌套的子表达式构成。子表达式的求值顺序可以使表达式的最终值发生 变化。 例如&#xff0c;已知表达式3*52&#xff0c;依照子表达式的求值顺序&#xff0c;有两种可能的结果&#xff0c;如图9-3所示。 如果乘法先执行&#xff0c;结果是17。如果5…...

Docker拉取MySQL后数据库连接失败的解决方案

在使用Docker部署MySQL时&#xff0c;拉取并启动容器后&#xff0c;有时可能会遇到数据库连接失败的问题。这种问题可能由多种原因导致&#xff0c;包括配置错误、网络设置问题、权限问题等。本文将分析可能的原因&#xff0c;并提供解决方案。 一、确认MySQL容器的运行状态 …...

论文阅读:Matting by Generation

今天介绍一篇关于 matting 抠图的文章&#xff0c;抠图也算是计算机视觉里面非常经典的一个任务了。从早期的经典算法到如今的深度学习算法&#xff0c;已经有很多的工作和这个任务相关。这两年 diffusion 模型很火&#xff0c;大家又开始用 diffusion 模型做各种 CV 任务了&am…...

Python第七周作业

Python第七周作业 文章目录 Python第七周作业 1.使用open以只读模式打开文件data.txt&#xff0c;并逐行打印内容 2.使用pathlib模块获取当前脚本的绝对路径&#xff0c;并创建logs目录&#xff08;若不存在&#xff09; 3.递归遍历目录data&#xff0c;输出所有.csv文件的路径…...