【Linux】-再谈动静态库-手把手教你自己制作一个动静态库

在这里插入图片描述
💖作者:小树苗渴望变成参天大树🎈
🎉作者宣言:认真写好每一篇博客💤
🎊作者gitee:gitee✨
💞作者专栏:C语言,数据结构初阶,Linux,C++ 动态规划算法🎄
如 果 你 喜 欢 作 者 的 文 章 ,就 给 作 者 点 点 关 注 吧!

文章目录

  • 前言
  • 一、再谈静态库
    • (1)站在库的制作者角度
    • (2)站在库的使用者角度
  • 二、再谈动态库
    • (1)站在库的制作者角度
    • (2)站在库的使用者角度
  • 三、总结


前言

我们上篇讲解完文件系统和软硬件链接,今天我们在此来谈一个熟悉的话题–动静态库,我们之前也说过这个话题,但是讲解了一个网吧的例子,让大家看到我们的动静态库的区别,以及程序是怎么去进行动静态库的调用,就相当于我们还停留在了解,今天博主带大家写一个动静态库,然后写程序使用我们自己的动静态库去操作,这样大家就可以更加的理解动静态库了。话不多说,我们开始进入正文的讲解。


讲解思路:

  1. 站在库的制作者角度
  2. 站在库的使用者角度
  3. 动态库是怎么被加载的

一、再谈静态库

(1)站在库的制作者角度

我们在之前说过,我们的库是函数的定义,还需要一个.h文件,他是库的声明,因为我们已经简单的使用了多文件开发,显然两个源文件是编译不起来的,所以必须要有头文件。静态库打包后的后缀是.a
我们需要写一个源文件定义,还有一个头文件进行声明:

mymath.h

#include<stdio.h>

extern int myerror;

int add(int,int);
int sub(int,int);
int mul(int,int);
int div(int,int);

mymath.c

#include "mymath.h"

int myerror=0;
int add(int a,int b)
{
    return a+b;
}
int sub(int a,int b)
{
    return a-b;
}
int mul(int a,int b)
{
    return a*b;
}
int div(int a,int b)
{
    if(b==0)//出现错误讲错误码设置为1
    {
        myerror=1;
        return -1;
    }
    return a+b;
}

main.c

#include "mymath.h"

int main()
{
    printf("1+1=%d",add(1,1));//调用一个这个头文件里面的函数,就好比我们printf是stdio.h里面的函数。
    return 0;
}

我们可以直接把我们的库文件(mymath.c)和头文件(mymath.h)直接给用户,让main.c去使用,这就和我们平时进行多文件开发一下的。但是我们今天使用的是静态库链接,我们是看不到库文件里面的具体实现的,所以我们需要将这个库文件进行打包就行了。

我们包含头文件的时候,将程序进行编译链接在形成可执行程序,所以我们自己的源文件(main.c)和库文件在链接的时候才会发生,此时前面两个文件都经过了编译,都变成了.o文件,只需要把.o文件打包给源文件(main.c)就行了。
在这里插入图片描述
makefile:

lib=libmymath.a  //静态库文件一般都是.a的后缀

$(lib):mymath.o
	ar -rc $@ $^ //通过这个命令打包成.a文件
mymath.o:mymath.c  //先编译成.o文件
	gcc -c $^

.PHONY:clean
clean:
	rm -rf *.o *.a lib

.PHONY:output
output://创建一个目录将属于静态库的内容放在一个目录里面,别人就可以通过这个lib文件,就可以把所有的静态库相关的文件都下载下来
	mkdir -p lib/include
	mkdir -p lib/mymathlib
	cp *.h lib/include
	cp *.a lib/mymathlib

在这里插入图片描述
此时我们就自己制作了一个静态库。

(2)站在库的使用者角度

我们怎么去使用这个静态库呢??我们虽然生成了静态库,但是我们调用自己写的静态库,使用gcc编译的时候会报错
在这里插入图片描述

造成错误的原因是因为,我们的gcc是编译器,会去系统默认的路径下找对应的库文件和头文件,我们自己写的gcc并不知道,所以我们需要手动告诉gcc库文件和头文件在哪,所以需要使用下面的指令
gcc main.c -I ./lib/include -L ./lib/mymathlib -I:是找到对应的头文件,-L:是找到对应的库文件
在这里插入图片描述
在这里插入图片描述

居然还不可以,原因是我们找到了对应的库文件所在的路径,但是必须要指定的链接的是哪个文件,即使里面只有一个静态库,也需要指定,所有需要在加一个选项 -lmymath 静态库的名称是去前缀和后缀,才是他真正的名字。选项和名字直接最好不要加空格。
在这里插入图片描述


再来看一个问题
我们查看可执行程序可以使用使用ldd/file查看是什么链接方式
在这里插入图片描述
我们没有看到mymath这个静态库啊,原因是我们默认的使用的是动态链接方式,但是你没有动态库,有静态库的文件,gcc不是阻挡编译,没有动态库,就相当于使用动态链接,用的是静态库文件,因为使用什么链接方式就展示什么类型的库文件,所以这里面看不到我们的静态类型的库文件,也希望大家遇到这个问题不要困惑。


我们发现这么一串编译太不好了,有两种方式解决这个问题

  1. 放到系统默认路径下
    系统默认的头文件在 /usr/include
    在这里插入图片描述
    系统默认的库文件在/usr/lib64
    在这里插入图片描述

我们可以把自己制作的头文件和库文件放在这些默认的搜索路径下:
在这里插入图片描述
我们发现-l还是要加上的。

  1. 软连接的方式
    其实我们不推荐将自己制作的库放到默认的系统搜索路径下,万一对原路径下的库文件造成污染就麻烦了,所以我们不需要将自己的库文件放到默认搜索路径下,使用软连接
    来看操作:
sudo ln -s /home/xdh/gitcode/linux/动静态库/lib/include /usr/include/myinc
sudo ln -s /home/xdh/gitcode/linux/动静态库/lib/mymathlib/libmymath.a /usr/lib64/libmymath.a
//大家一定要看好路径,如果按照我这个创建的路径,就可以直接使用这个指令了

在这里插入图片描述

我们看到使用软连接也可以将我们的问题解决掉,但是也需要加-l,大家应该会发现我们将路径复制到默认路径下,实际就是在安装的过程,我们把对应的文件下载下来,为什么要安装,就是放在默认的搜索路径下,方便去执行。


结论就是第三方库在使用gcc的时候必须加上-l选项。第一方和第二方可以理解为系统和语言

静态库我们就讲解完毕了,我们来看动态库

二、再谈动态库

按照老思路去讲解

(1)站在库的制作者角度

博主将多写几个库文件,一起打包,让大家也看看。我峨嵋你动态库打包后的默认后缀是.so
还是需要写一个头文件和两个库文件
myprint.h

#pragma once 

#include<stdio.h>

void print();

myprint.c

#include "myprint.h"

void print()
{
    printf("hello dy\n");
    printf("hello dy\n");
    printf("hello dy\n");
    printf("hello dy\n");
}

mylog.h

#pragma once 

#include<stdio.h>

void Log(const char*);

mylog.c

#include "mylog.h"

void Log(const char* str)
{
    printf("warning:%s\n",str);
}

我们已经有了两个库文件,那我们应该怎么什么对应的动态库文件呢?首先动态库也是和静态库一样在链接的时候才会和原文件产生关系,所以我们将我们自己写的两个库文件需要先编译生成对应的.o文件,在进行打包成动态库文件。

肯定是有点区别的
gcc -fPIC -c mylog.c myprint.c
在这里插入图片描述
生成.o文件,难道是和静态库一样使用ar就可以了吗,答案肯定不是的,我们可以直接只有下面的指令去操作
gcc -shared -o libmymethod.so mylog.o myprint.o
在这里插入图片描述


重点:

  1. 我们发现动态库可以直接使用gcc进行打包,而静态库不行,我们可以理解为动态库是gcc的亲儿子,而静态库是干儿子,我们的gcc已经内置编码可以直接打包成动态库,这也可以简单理解为我们的链接方式默认就是动态库,但是用户最大,使用- static的时候还是静态链接,使用静态库,只有静态库的时候也是使用静态库。
  2. 我们发现.so是可执行文件,而.a文件是不可执行文件,这是因为,我们动态库是所有程序共享的,但我们自己写的源文件编译好,准备去执行的时候,还需要将动态库文件一起加载到内存进行执行,而静态库在形成可执行程序之前,就讲自己对应的代码复制到源文件里面了,执行的时候,那源文件加载内存就行了,和静态库文件就没有关系了
  3. 我们的打包成动态库文件去掉- shared,是不是就是我们一开始学的多个文件生成可执行程序,可我不想生成可执行文件,所有加了一个- shared,说这个的目的就是让大家更好的理解。

上面先不给大家演示效果,我先将下面的操作讲解完毕,在演示,我想将动静态库一起通过一个makefile同时编译,相信在进程程序替换的时候,已经说过怎么同时生成量恶搞可执行程序,默认值生成先出现的那一个,所以我们要设置为目标,接下来我们来看具体操作

makefile:

static-lib=libmymath.a
dy-lib=libmymethod.so 

.PHONY:all
all:$(static-lib) $(dy-lib)


$(static-lib):mymath.o
        ar -rc $@ $^
mymath.o:mymath.c
        gcc -c $^


$(dy-lib):mylog.o myprint.o
        gcc -shared -o $@ $^
mylog.o:mylog.c
        gcc -fPIC -c $^
myprint.o:myprint.c
        gcc -fPIC -c $^

.PHONY:clean
clean:
        rm -rf *.o *.a *.so mylib 

.PHONY:output
output:
        mkdir -p mylib/include
        mkdir -p mylib/lib
        cp *.h mylib/include
        cp *.a mylib/lib
        cp *.so mylib/lib

我们使用这个makefile先来测试一下之前静态库的代码,看看我们写的对不对,博主就直接在gcc下指定具体路径了去测试了。

重点:不管是那种类型的库文件,前缀都一定要是lib,不让就会出现问题

在这里插入图片描述
结果也同时可以使用自己制作的静态库,但是这个结果我们发现不对,错误码应该是1,这个和我们测试没有关系,就是printf自己的特性,他是从右往左进行赋值的,在没有调用函数的时候,错误码就已经被赋值了,提前算好就行了。
在这里插入图片描述
这样的问题就解决了。

(2)站在库的使用者角度

我们通过上面方法,制作出属于我们自己的动态库,现在要来看看怎么去使用它。
我们使用make一键生成动态库,看效果
在这里插入图片描述

mylib里面就是包含我们自己制作的动静库文件,大家可以选择将两者分开,这里博主就没有分开了,因为不影响演示,接下来我们就来测试。

我们来写一个程序去使用我们的动态库
mytest.c

#include"mylog.h"
#include"myprint.h"
int main()
{
    print();
    Log("hello log function");
    return 0;
}

gcc mytest -o mytest
在这里插入图片描述
显然我们直接编译成可执行程序是不行的。这个问题不用多说,找不到路径,我们使用下面的命令,指定路径试试看
gcc mytest.c -I ../mylib/include -L ../mylib/lib -lmymethod -o mytest
在这里插入图片描述
我们看到可以形成可执行文件了,但是我们执行的时候去还是报错,这是为什么??
我都已经把指定路径告诉你了,让你去这个路径下面去查找,你怎么还是不行呢??
首先我们要搞懂一个问题,这个告诉你,你指的是谁(gcc)gcc只管编译,形成可执行程序,大家还记得我最上面说的重点吗,但可执行程序开始运行的时候,动态库也是要加载到内存的,刚才指定路径值告诉了gcc,没有告诉我系统你的动态库在哪里,所以才会导致我们可以形成可知心个程序,去执行不了,既然找到问题的答案了,那我们就解决这个问题,让系统知道就好了。
为什么静态库没有这个问题呢,原因就是我们在使用静态库链接的时候,代码是拷贝到可执行代码中的,形成可执行程序之后,加载到内存就和静态库没有关系了。只要编译时能找到就行了。

解决问题:
我们有四种结果办法,博主都会一一介绍的。

  1. 将动态库放到默认系统默认路径下
sudo cp ./mylib/include/myprint.h /usr/include/myprint.h
sudo cp ./mylib/include/mylog.h /usr/include/mylog.h
sudo cp ./mylib/lib/libmymethod.so /usr/lib64/libmymethod.so

在这里插入图片描述

  1. 使用软链接的方式
sudo ln -s /home/xdh/gitcode/linux/动静态库/mylib/lib/libmymethod.so /usr/lib64/libmymethod.so

在这里插入图片描述

我直接将动态库和系统默认路径进行软链接,我程序都没有重新生成,结果就可以直接跑了。大家应该可以理解这个道理。这也侧面体现出来软链接的作用以及重要性

  1. 使用环境变量去操作
    我们为什么可以这么自然而然的使用我们的系统默认路径,原因是我们有环境变量,那我们把我们对应的动态库文件放到环境变量里面就可以了
 LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/xdh/gitcode/linux/动静态库/mylib/lib

在这里插入图片描述

所以环境变量还是非常重要的

  1. 添加到系统的配置文件里面
    第三种在下次打开shell的时候,就又不行了,所以放到配置文件里面就可以了
    在这里插入图片描述
    在这里插入图片描述
    我们自己添加一个配置文件,把我们对应的动态库文件的路径放进去

在这里插入图片描述

我们在使用配置文件的时候一定要注意权限,还有这个配置文件要更新一下才可以使用,下次在重启的时候,还是可以对应的动态库的。

解决疑惑:

  1. 我们发现我们把对应的库文件放系统知道就行了,为什么头文件不需要让系统知道呢?原因是我们在编译的时候就标识过了我们自己写的可执行程序是在.o文件的时候进行链接的,而头文件在编译的时候就展开到库文件里面了然后形成了.o文件,所以不需要.
  2. 那这么多方法我们使用哪一个呢??答案是最多还是第一种,虽然第一种不建议,为什么不建议,我在一开始就标注了,我们自己写的不建议,但是我们以后几乎使用的都是别人成熟的库,所以不用担心对自己系统有啥影响,所以可以直接放到系统默认的搜索路径下,所有就有了安装。

三、总结

对于动静态库大家应该都不陌生了,我们以后去公司有很大可能使用公司自己写的库去开发,这时候就需要我们自己去将公司的库配置到自己的电脑,这也是你刚开始入职的时候,要频繁的安装软甲的原因,所以大家了解原理了,到时候也不用担心了,也希望读者可以学到更多知识,我们下篇再见。

😃

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/146022.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

计算机缺失vcruntime140.dll如何修复?超简单的5个解决方法

在我们日常使用电脑的过程中&#xff0c;可能会遇到各种各样的问题和错误提示。其中&#xff0c;一个比较常见的错误提示就是“vcruntime140.dll丢失”。这个错误通常发生在我们尝试运行某个程序或应用时&#xff0c;系统无法找到或加载所需的vcruntime140.dll文件。 vcruntime…

Ubuntu安装mysql(解决ubuntu Access denied for user ‘root‘@‘localhost‘报错)

1、安装mysql sudo apt-get install mysql-server 上述命令会安装以下包&#xff1a; apparmor mysql-client-5.7 mysql-common mysql-server mysql-server-5.7 mysql-server-core-5.7 因此无需再安装mysql-client等。安装过程会提示设置mysql root用户的密码&#xff0c;设…

Java —— 继承

目录 1. 为什么需要继承 2. 继承概念 3. 继承的语法 4. 父类成员访问 4.1 子类中访问父类的成员变量 1. 子类和父类不存在同名成员变量 2. 子类和父类成员变量同名 4.2 子类中访问父类的成员方法 1. 成员方法名字不同 2. 成员方法名字相同 5. super关键字 6. 子类构…

Istio学习笔记-体验istio

参考Istio 入门(三)&#xff1a;体验 Istio、微服务部署、可观测性 - 痴者工良 - 博客园 (cnblogs.com) 在本章中&#xff0c;我们将会学习到如何部署一套微服务、如何使用 Istio 暴露服务到集群外&#xff0c;并且如何使用可观测性组件监测流量和系统指标。 本章教程示例使用…

【JAVA学习笔记】70 - 反射

项目代码 https://github.com/yinhai1114/Java_Learning_Code/tree/main/IDEA_Chapter23/src 反射 一、反射的引出 package com.yinhai.reflection.question;import com.yinhai.Cat;import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IO…

全链路自动化测试

背景 从 SOA 架构到现在大行其道的微服务架构&#xff0c;系统越拆越小&#xff0c;整体架构的复杂度也是直线上升&#xff0c;我们一直老生常谈的微服务架构下的技术难点及解决方案也日渐成熟&#xff08;包括典型的数据一致性&#xff0c;系统调用带来的一致性问题&#xff0…

vue day1(主要是指令)

1、引包 或者&#xff1a;cdn网址 2、创建实例&#xff0c;初始化渲染 3、插值表达式 {{}} 表达式&#xff1a;可以被求值的代码 4、响应式数据&#xff1a;数据发生变化&#xff0c;视图自动更新&#xff08;底层是dom操作&#xff09; data中数据会被添加到实例上&#x…

微信自动添加好友

简要描述&#xff1a; 添加微信好友 请求URL&#xff1a; http://域名地址/addUser 请求方式&#xff1a; POST 请求头Headers&#xff1a; Content-Type&#xff1a;application/jsonAuthorization&#xff1a;login接口返回 参数&#xff1a; 参数名必选类型说明wId…

易点易动固定资产管理系统:RFID技术助力快速盘点数万固定资产

在当今的企业管理中&#xff0c;高效和准确的固定资产盘点是至关重要的。传统的资产盘点方法通常耗时且易出错&#xff0c;这在快节奏的商业环境中是企业难以承受的。易点易动固定资产管理系统通过采用射频识别&#xff08;RFID&#xff09;技术&#xff0c;为企业提供了一种革…

竞赛 题目:基于python的验证码识别 - 机器视觉 验证码识别

文章目录 0 前言1 项目简介2 验证码识别步骤2.1 灰度处理&二值化2.2 去除边框2.3 图像降噪2.4 字符切割2.5 识别 3 基于tensorflow的验证码识别3.1 数据集3.2 基于tf的神经网络训练代码 4 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于pyt…

【教3妹学编辑-mysql】mybatis查询条件遇到的坑及解决方案

2哥 :3妹&#xff0c;今天怎么下班这么晚啊。 3妹&#xff1a;嗨&#xff0c;别提了&#xff0c;今天线上出bug了&#xff0c; 排查了好久。 2哥&#xff1a;啊&#xff0c;什么问题呀&#xff1f; 3妹&#xff1a;我们内部的一个管理系统报错了&#xff0c; 最近排查下来是myb…

Gempy 实现地理位置3D模型的展示以及导出

1. 首先安装python gempy 包 pip install gempy python 版本 3.10 这个很重要,版本不同可能会报错 2. gdal 可能会报错, 一下地址根据python版本下载,然后移入到python解释器环境中, Script文件中,然后cmd ,pip install 文件名安装即可 Releases cgohlke/geospatial-wheels …

NI和EttusResearchUSRP设备之间的区别

NI和EttusResearchUSRP设备之间的区别 概述 USRP&#xff08;通用软件无线电外设&#xff09;设备是业界领先的商软件定义无线电&#xff08;SDR&#xff09;。全球数以千计的工程师使用USRPSDR来快速设计、原型设计和部署无线系统。它们以两个不同的品牌进行营销和销售&…

【电源专题】低功耗设备如何解决POE协议要求的PD最小功耗?

要让PD正常工作起来除了需要与PSE握手协商外,还要求PD有一个最小功耗输出。 其原因是如果PD没有在一定时间内给出一个最小功耗,那么PSE将会认为PD设备断开而自动关闭,将功率分配给其他网口。对于不同的类别PD,其要求也不一样。如下所示为Type 1/2/2/4最小电流的要求:如类…

力扣 225. 用队列实现栈(C语言实现)

目录 1.解题思路2.代码实现 1.解题思路 这道题如果使用C会好写的多&#xff0c;因为可以使用C提供的队列来实现&#xff0c;但如果使用C语言则必须手写一个队列来实现&#xff0c;在这里我用了我前面文章中实现好的队列来解答&#xff0c;首先因为队列是先进先出&#xff0c;而…

您的计算机已被Mallox勒索病毒感染?恢复您的数据的方法在这里!

尊敬的读者&#xff1a; 随着科技的迅速发展&#xff0c;网络安全问题日益凸显&#xff0c;其中勒索病毒是一种极具威胁性的恶意软件。在这些勒索病毒中&#xff0c;.mallox 勒索病毒尤为突出&#xff0c;它能够加密用户的数据文件&#xff0c;要求支付赎金才能解密。本文将介…

2021年03月 Scratch(一级)真题解析#中国电子学会#全国青少年软件编程等级考试

一、单选题(共25题,每题2分,共50分) 第1题 花花幼儿园有三个班。根据下面三句话,请你猜一猜,哪个班级人数最多? (1)中班比小班少 (2)中班比大班少 (3)大班比小班多 A:小班 B:中班 C:大班 D:三个班级一样多 答案:C 根据(1)(2)可以知道中班人数最少,根…

功能测试自动化测试流程

1概述 本流程是描述软件功能自动化测试过程中的步骤、内容与方法&#xff0c;明确各阶段的职责、活动与产出物。 2流程活动图 3活动说明 3.1测试计划&#xff08;可选&#xff09; 与以前的测试计划过程一致&#xff0c;只是在原来的测试计划中&#xff0c;添加对项目…

驾驭数据与人工智能是人才培养的时代命题

2023年11月11日全国近千名计算机教育工作者共聚“海南博鳌亚洲论坛大酒店”&#xff0c;以“产教融合&#xff0c;供需共赢”为主题&#xff0c;“服务国家创新驱动发展&#xff0c;顺应全球新一轮科技革命和产业变革的趋势&#xff0c;培养集学科、技术和产业需求相融合的IT新…

二十、泛型(7)

本章概要 动态类型安全泛型异常混型 C 中的混型与接口混合使用装饰器模式与动态代理混合 动态类型安全 因为可以向 Java 5 之前的代码传递泛型集合&#xff0c;所以旧式代码仍旧有可能会破坏你的集合。Java 5 的 java.util.Collections 中有一组便利工具&#xff0c;可以解…