编码拓展:链接库

news2024/12/25 9:29:07

一.认识链接库

1.1库

计算机中,有些文件专门用于存储可以重复使用的代码块,例如功能实用的函数或者类,我们通常将它们称为库文件,简称“库”(Library)。

以 C 语言为例,如下为大家展示的就是一个函数库:

//Math.c
int add(int a, int b) {
    return  a + b;
}
int sub(int a, int b) {
    return  a - b;
}

这是我自己编写的一个Math.c文件,如果我们现在要写一个计算器的项目或其他项目但需要使用加法减法运算,不可避免的要多次使用加法和减法。Math.c 就相当于一个库文件,用法也很简单,直接将它添加到某一个 C 语言项目中,就可以直接调用文件中的 2个函数,每个函数可以调用多次。

1.2链接库

现实生活中,类似 Math.c 这种“开源”的库文件很难找到,多数程序员并不会直接分享源代码,他们更愿意分享库文件的二进制版本——链接库。

所谓链接库,其实就是将开源的库文件(例如上面提到的 Math.c)进行编译、打包操作后得到的二进制文件(.obj文件)。虽然链接库是二进制文件,但无法独立运行,必须等待其它程序调用,才会被载入内存。

1.3C语言项目运行过程

一个完整的 C 语言项目可能包含多个 .c 源文件,项目的运行需要经过“编译”和“链接”两个过程:

  • 编译:由编译器逐个对源文件做词法分析、语法分析、语义分析等操作,最终生成多个目标文件。每个目标文件都是二进制文件,但由于它们会相互调用对方的函数或变量,还可能会调用某些链接库文件中的函数或变量,编译器无法跨文件找到它们确切的存储地址,所以这些目标文件无法单独执行。
  • 链接:对于各个目标文件中缺失的函数和变量的存储地址,由链接器负责修复,并最终将所有的目标文件链接库组织成一个可执行文件(.exe文件)。

注意,一个目标文件中使用的函数或变量,可能定义在其他的目标文件中,也可能定义在某个链接库文件中。

1.4链接方式

链接器完成完成链接工作的方式有两种,分别是:

  • 无论缺失的地址位于其它目标文件还是链接库,链接库都会逐个找到各目标文件中缺失的地址。采用此链接方式生成的可执行文件,可以独立载入内存运行;

  • 链接器先从所有目标文件中找到部分缺失的地址,然后将所有目标文件组织成一个可执行文件。如此生成的可执行文件,仍缺失部分函数和变量的地址,待文件执行时,需连同所有的链接库文件一起载入内存,再由链接器完成剩余的地址修复工作,才能正常执行。

我们通常将第一种链接方式称为静态链接,用到的链接库称为静态链接库;第二种链接方式中,链接所有目标文件的方法仍属静态链接,而载入内存后进行的链接操作称为动态链接,用到的链接库称为动态链接库

也就是说,程序完成链接操作的方式有两种,一种是在生成可执行文件之前完成所有链接操作,使用的库文件称为静态链接库;另一种是将部分链接操作推迟到程序执行时才进行,此过程使用的库文件称为动态链接库

1.5静态链接库

静态链接库用来和所有的目标文件一起组织成可执行文件,生成的可执行文件可以独立运行。

采用静态链接库完成链接操作,存在诸多缺点。首先,可执行文件内部拷贝了所有目标文件和静态链接库的指令和数据,可执行文件本身的体积会很大。当系统中存在多个链接同一个静态库的可执行文件时,每个可执行文件中都存有一份静态库的指令和数据,就会造成内存空间的极大浪费。

1.6动态链接库

实际上,动态链接库是 Windows 平台上对动态链接过程所用库文件的叫法,Linux 平台上习惯称为共享库或者共享对象文件,它们表达的是一个意思,这里注意理解。

具体来讲,对于一个以动态链接方式运行的项目,首先由静态链接器将所有的目标文件组织成一个可执行文件,运行时将所需的动态链接库全部载入内存,由动态链接器完成可执行文件和动态库文件的链接工作。

和静态链接库相比,动态链接库可以很好地解决空间浪费和更新困难的问题。动态链接库和可执行文件是分别载入内存的,因此动态链接库的体积通常会小一些。当有多个程序使用同一个动态链接库时,所有程序可以共享一份动态链接库的指令和数据,避免了空间的浪费。采用动态链接的方式也可以方便程序的更新和升级,当程序的某个模块更新后,只需要将旧的模块替换掉,程序运行时会自动将所有模板载入内存并动态地链接在一起。

二.静态链接库的创建和使用

这里静态链接库创建和使用,以及下面的动态链接库,都是以DevC++为例完成的,其他相关软件也可以完成。

2.1静态链接库的创建

步骤:

  • 1.打开Dev-C++,选择“新建-项目”
  • 2.选择“静态链接库”,并选择编程语言(C或C++看你选择),同时设置名字和保存位置
  • 3.在文件里添加函数或变量
  • 4.“保存”后点击“编译”
  • 5.在项目的位置找到一个.a文件即成功

2.2静态链接库的使用

2.2.1方法一:使用项目

步骤:

  • 1.新建一个“控制台程序”
  • 2.选择编程语言(C或C++看你选择),同时设置名字和保存位置
  • 3.点击“项目属性”中的“参数”
  • 4.点击“加入库或对象”,把刚刚产生的.a文件加入进去
  • 5.点击“确定”
  • 6.调用刚刚的库函数时,需要写出原函数的声明,然后正常使用。
#include <stdio.h>
#include <stdlib.h>

/* run this program using the console pauser or add your own getch, system("pause") or input loop */
void hello();
int add(int a,int b);          //必须和库文件的声明一模一样
int main(int argc, char *argv[]) {
	int a=100,b=99;
	hello();
	printf("%d",add(a,b));
	return 0;
}

这种方式还有其他的操作,八九不离十,基本都是这样。

这里给出一个参考链接:方法一的其他形式操作

2.2.2方法二:修改编译选项

步骤:

  • 1.将自己的库重命名,命名格式为“libxxxxxxx.a”
  • 2.新建一个源代码(这里不是控制台程序了)
  • 3.打开“工具-编译选项”
  • 4.找到“在连接器命令行加入以下命令”,在那个框里加入以下命令(如果框里已经有命令了,就在末尾加一个空格,再输入下面的命令):-l你的库名(去掉开头的lib和结尾的.a)
  • 5.找到“目录-库”,添加你建的静态链接库文件路径
  • 6.把原函数写在一个头文件(.h文件)里
  • 7.导入头文件就可以正常使用了

这里给出一个参考链接:方法二参考

三.动态链接库的创建和使用

3.1动态链接库的创建

步骤:

  • 1.打开Dev-C++,选择“新建-项目”

  • 2.选择“动态链接库”,并选择编程语言(C或C++看你选择),同时设置名字和保存位置

  • 3.在.h文件里添加函数声明,在.c文件里定义函数,原来里面就有的全部删了,那就是一个坑,看也看不懂,还报一些其他错误,然后按我们熟悉的方式编写代码

    注意:一定要改文件名字,默认dll.h和dllmain.c组合与测试程序main.c一起会报错,尽量只有一个main.c文件

  • 4.“保存”后点击“编译”,不报错才算成功

  • 5.在项目的位置找到一个.dll文件和一个.a文件即成功

为了防止出现和我一样的错误,在这里弄了整整几个小时,饭也没吃,我这里给一个简单代码例子:

.h文件:

#ifndef ADD_H
#define ADD_H

#ifdef __cplusplus
extern "C" {
#endif

int add(int a, int b);

#ifdef __cplusplus
}
#endif

#endif /* ADD_H */

.c文件:

/* Replace "dll.h" with the name of your header */
#include "dll.h"
#include <windows.h>
#include <stdio.h> 
#include <stdlib.h>

int add(int a, int b)
{
    return a + b;
}

3.2动态链接库的使用

步骤:

  • 1.新建一个“控制台程序”
  • 2.选择编程语言(C或C++看你选择),同时设置名字和保存位置
  • 3.编写测试的cpp
  • 4.和上面一样,添加.a文件
  • 5.编译运行

这里最重要的一步是编写测试CPP,这相比静态链接库的最大难点就在这里,你必须了解一些windows.h库中的函数,不然你有可能看也看不懂。

在这里我还是给出对应的测试函数例子:

#include <stdio.h>
#include <stdlib.h>
#include "dll.h"
#include <windows.h>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */



int main()
{
    // 加载动态链接库
    HINSTANCE hDll = LoadLibrary("first.dll");
    if (hDll == NULL) {
        printf("Failed to load library.\n");
        return 1;
    }

    // 导入函数
    int (*pAdd)(int, int) = (int (*)(int, int))GetProcAddress(hDll, "add");
    if (pAdd == NULL) {
        printf("Failed to get function address.\n");
        return 1;
    }

    // 使用函数
    int result = pAdd(3, 5);
    printf("Result: %d\n", result);

    // 卸载动态链接库
    FreeLibrary(hDll);
    return 0;
}

四.编写测试CPP的常用知识

4.1 GetProcAddress函数

GetProcAddress是Windows操作系统提供的函数,用于在运行时获取动态链接库中导出函数的地址。在window.h库中,它的声明如下:

FARPROC GetProcAddress(HMODULE hModule, LPCSTR lpProcName);
  • GetProcAddress的返回值是一个函数指针,即FARPROC类型,它指向被导出函数的地址。

  • hModule参数指定了需要获取函数地址的动态链接库模块句柄,可以使用LoadLibrary函数获得。

  • lpProcName参数指定了需要获取的函数名称。

4.2 HINSTANCE数据类型

HINSTANCE是Windows操作系统中的一个数据类型,它被用作模块实例的句柄,用于标识可执行文件或动态链接库在内存中的一个实例。每次加载可执行文件或动态链接库时,都会创建一个新的模块实例,并分配一个唯一的HINSTANCE值。

HINSTANCE通常用来表示应用程序或动态链接库的实例句柄。当我们加载一个动态链接库时,LoadLibrary函数将返回这个动态链接库的模块句柄(即HMODULE),而这个模块句柄也可以被强制转换为HINSTANCE类型使用。

补充:当LoadLibrary函数返回值为0时,表示加载动态链接库失败。

4.3 加载和卸载

函数: LoadLibrary()函数与 FreeLibrary()函数

  • LoadLibrary():加载库文件;
  • FreeLibrary():卸载已加载的库文件。

这里不解释。

五.运行结果

jdAI.jpg

六.个人感受

先声明这些操作都是在windows环境下操作,这一部分的资料网上确实比较少,还都不讲原理的,这里把原理和实际操作都弄了一遍,真的不容易,为了这一篇博客,整整弄了一天,饭也没吃,当然这个知识点属于冷门知识点,了解就行,我们一般用的比较少。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JUC高级十-并发加锁原理之AbstractQueuedSynchronizer(AQS)

1. 前置知识 公平锁和非公平锁可重入锁自旋锁LockSupport数据结构之双向链表设计模式之模板设计模式 AQS重要性 JAVA ------>JVM AQS ------>AQS 2. AQS入门级别理论知识 2.1 是什么? 2.1.1 字面意思 Abstract Queued Synchronizer----抽象的队列同步器 源码位置: …

【Microsoft Edge】如何彻底卸载 Edge

文章目录 一、问题描述二、卸载 Edge2.1 卸载正式版 Edge2.2 卸载非正式版 Edge2.2.1 卸载通用的 WebView22.2.2 卸载 Canary 版 Edge2.2.3 卸载其他版本2.3 卸载 Edge Update 2.4 卸载 Edge 的 Appx 额外安装残留2.5 删除日志文件2.6 我就是想全把 Edge 都删了 一、问题描述 …

预测模型的局部评价?

预测模型的局部评价 为什么要进行局部评价&#xff1f; 首先是临床决策曲线分析通常会给预测模型的使用规定一个阈值范围&#xff0c;相应地预测模型的评价也应该局限在这个范围之内才是合理的&#xff1b; 其次&#xff0c;全局性地评价往往不够敏感&#xff0c;即好的模型和坏…

本地搭建属于自己的ChatGPT:基于Python+ChatGLM-6b+Streamlit+QDrant+DuckDuckGo

本地部署chatglm及缓解时效性问题的思路&#xff1a; 模型使用chatglm-6b 4bit&#xff0c;推理使用hugging face&#xff0c;前端应用使用streamlit或者gradio。 微调对显存要求较高&#xff0c;还没试验。可以结合LoRA进行微调。 缓解时效性问题&#xff1a;通过本地数据库…

C语言实现银行家算法

一.银行家算法 1.由来 银行家算法最初是由荷兰计算机科学家艾兹赫尔迪杰斯特拉&#xff08;Edsger W. Dijkstra&#xff09;于1965年提出的。当时他正致力于解决多道程序设计中产生的死锁问题。在多道程序设计中&#xff0c;由于不同进程之间共享有限的系统资源&#xff0c;如…

【JavaEE初阶】多线程(一)认识线程 线程的创建 Thread的用法

摄影分享&#xff01; 文章目录 认识线程&#xff08;Thread&#xff09;概念执行多线程编程创建线程的写法1.继承Thread&#xff0c;重写run2.实现Runnable接口3.使用匿名内部类&#xff0c;继承Thread4.使用匿名内部类&#xff0c;实现Runable5.使用Lambda表达式 Thread用法…

C语言模拟银行排队叫号(链队)

一.队列 队列是一种具有先进先出&#xff08;FIFO&#xff09;特性的线性数据结构&#xff0c;它只允许在队列的两端进行插入和删除操作。队列的一端称为队尾&#xff08;rear&#xff09;&#xff0c;另一端称为队头&#xff08;front&#xff09;。新元素总是插入在队列的队…

怎么把m4a转换成mp3,分享几个方法给大家!

录音文件中经常出现m4a后缀的音频格式&#xff0c;但通常只能在特定的音频播放器中播放。如果你想把m4a转换成mp3&#xff0c;下面是四种简单易行的方法&#xff0c;适用于Windows 10操作系统。 方法一&#xff1a;使用记灵在线工具转换m4a成mp3 工具地址&#xff1a;记灵在线…

elsticsearch入门

查看所有索引&#xff08;表&#xff09; 向索引&#xff08;表&#xff09;中添加数据&#xff1a; 自定义id添加数据&#xff1a; 自定义id添加数据&#xff1a;方式二 查询数据&#xff1a; 查询索引&#xff08;表&#xff09;中全部数据&#xff1a; 全量修改单条数据&…

File类与IO流

1. java.io.File类的使用 1.1 概述 File类及本章下的各种流&#xff0c;都定义在java.io包下。一个File对象代表硬盘或网络中可能存在的一个文件或者文件目录&#xff08;俗称文件夹&#xff09;&#xff0c;与平台无关。&#xff08;体会万事万物皆对象&#xff09;File 能新…

【Leetcode -剑指Offer 22.链表中倒数第k个结点 -203.移除链表元素】

Leetcode Leetcode -剑指Offer 22.链表中倒数第k个结点Leetcode -203.移除链表元素 Leetcode -剑指Offer 22.链表中倒数第k个结点 题目&#xff1a;输入一个链表&#xff0c;输出该链表中倒数第k个节点。为了符合大多数人的习惯&#xff0c;本题从1开始计数&#xff0c;即链表…

数据结构(三)—— 哈希表

文章目录 一、哈希表积累1.1 哈希map1.2 哈希set 二、哈希表基础三、题3.1 242 有效的字母异位词3.2 349 两个数组的交集3.3 202 快乐数3.4 1 两数之和3.5 54 四数相加II 一、哈希表积累 什么时候想到用哈希法&#xff1a;当要需要查询一个元素是否出现过、判断一个元素是否出…

awvs安装批量扫描

文章目录 安装批量扫描 安装 1.2.AWVS下载   该工具可在官方网站下载&#xff0c;但免费下载的是14天试用版本。   官网下载&#xff1a;AWVS   百度云下载&#xff1a;百度云下载https://pan.baidu.com/s/1UO7GzL0CMemJ_TMQnHNOuA?pwdg1bm 提取码&#xff1a;g1bm 批…

浏览器状态同步和路由-前端路由和服务端路由原理

目录 前端路由和History API 浏览文境&#xff08;Browser Context&#xff09; 会话历史&#xff08;Session History&#xff09; History API history.go()切换当前会话&#xff0c;并不改变会话栈 history.back() history.go(-1) & history.forward() history.…

5.4、服务器编程基本框架和两种高效的事件处理模式

5.4、服务器编程基本框架和两种高效的事件处理模式 1.服务器编程基本框架2.两种高效的事件处理模式①Reactor模式②Proactor模式③模拟Proactor模式 1.服务器编程基本框架 模块功能I/O 处理单元处理客户连接&#xff0c;读写网络数据逻辑单元业务进程或线程网络存储单元数据库、…

18.网络爬虫—Scrapy实战演示

网络爬虫—Scrapy实战演示 Scrapy Shell简介进入shell调试网站启动Scrapy Shell 查看目标网站获取网站源代码常用方法调试xpath提取数据Scrapy请求子页面请求及返回处理创建项目创建爬虫 数据解析写入csv文件后记 前言&#xff1a; &#x1f3d8;️&#x1f3d8;️个人简介&…

Java,jdbc,jvm

1、数据删除 物理删除 直接发送delete语句 就是物理删除 这种删除 删除之后不可恢复逻辑删除 本质是更新 0 表示不可用 1 可用状态 update emp set is_active where id ?2、数据更新 1 显示所有数据 2. 点击修改按钮 此时 应该执行的动作-查询该用户信息 目的是将当前用户…

并发编程的那些事

目录 一、并发编程的目的 二、线程和进程2.1 什么是线程2.2 进程2.3 一个普通Java 程序包含哪些线程 三、并发、并行四、线程的六个状态五、wait 和sleep的区别5.1 位于不同的类5.2 关于锁的释放 一、并发编程的目的 并发编程的目的是为了让程序运行得更快&#xff0c;但是&…

类ChatGPT逐行代码解读(1/2):从零起步实现Transformer、ChatGLM-6B

前言 最近一直在做类ChatGPT项目的部署 微调&#xff0c;关注比较多的是两个&#xff1a;一个LLaMA&#xff0c;一个ChatGLM&#xff0c;会发现有不少模型是基于这两个模型去做微调的&#xff0c;说到微调&#xff0c;那具体怎么微调呢&#xff0c;因此又详细了解了一下微调代…

ggplot中坐标轴和图例的相关处理

文章目录 改变坐标轴和图例的名称方法1, labs()方法2&#xff0c;scale_xxx_discrete/continuous() 删除坐标轴和图例的名称方法1&#xff0c; labs()方法2&#xff0c;scale_xxx_discrete/continuous()方法3&#xff0c;theme()方法4&#xff0c;guides()可以去图例名称 改变图…