【四、静态库与动态库(共享库)】揭开链接库的神秘面纱:手把手教你制作静态链接库与动态链接库(一)

简介: 【四、静态库与动态库(共享库)】揭开链接库的神秘面纱:手把手教你制作静态链接库与动态链接库

一、什么是链接库

有时候我们对外提供功能的时候,可能不希望对方看到源码,我们就可以制作成库文件,把库文件和头文件给到对方就可以达到提供功能又不暴露源码的目的。链接库就是指将库文件编译后打包为一个二进制文件,这些二进制文件会在程序调用的时候加载到内存中。实际上,一个或多个源文件编译为目标文件后,这个文件中所引用的外部的符号需要通过链接来找到这部分缺失的地址。而链接的方式又分为两种,如果是在生成可执行文件之前就已经把所有的链接操作完成了,这种链接称为静态链接,这种库文件称为静态链接库;如果是在程序执行的时候才进行链接,这种称为动态链接,对应的库文件称为动态链接库。也正因为如此,使用静态库时生成的可执行文件是可以独立运行的,因为他不再需要外部的内容,而动态库编译生成的可执行文件就无法单独运行,因为他在运行时,才会去链接所引用的外部地址。

1. 静态链接库

静态库会直接加载到代码段,他和所有的目标文件一起链接成可执行文件,生成可执行文件后可以独立运行。但是,正因为静态库会直接加载到内存的代码段,可执行文件的内部都拷贝了所有目标文件和静态库的指令和数据,编译生成的可执行文件会比较大。并且,如果整个系统中有多个链接统一静态库的可执行文件时,每个可执行文件都要拷贝一份静态库的指令和数据,这就造成了空间浪费,因为他们拷贝的数据都是同样的内容。最后,如果一旦静态库文件有代码更新,就需要重新编译链接重新生成整个可执行文件,更新升级麻烦。 在 Linux 系统中,静态链接库文件的名称通常为 libxxx .a,在 Windows 系统中,静态链接库文件的后缀名为 .lib。

2. 动态链接库

其实,动态库这个称呼本身是对 Windows 平台上动态链接所用的库文件的一种称呼,在 Linux 下,一般称为共享库。动态库是在运行时加载到内存的共享库段,这样,如果很多程序都要用到静态库的时候,就会节省大量内存,因为它不像静态库那样加载到代码段,而是是在运行时载入内存的共享库段,当多个程序要用到同一个动态库时,所有程序可以共享这个共享库段的指令和数据。动态链接的实现是这样的,在编译时首先由静态链接器将所有的目标文件链接为一个可执行文件,等到程序运行时会将要用到的动态库加载到内存的共享库段,由动态链接器完成可执行文件和动态库文件的链接工作,可以理解为按需载入内存(在需要用到的时候,才会载入内存)。动态库大大方便了程序的升级和更改,只要用新的动态库文件替换旧的动态库文件即可,在运行时,会自动连接新的库文件。但是正因为动态库运行时载入的这个特点,使用动态库的可执行文件在运行时,会略慢一些,但整体来说,运行速度的性能损失,远远小于内存节省带来的收益。 在Linux系统中,动态链接库的名称通常为 libxxx.so,在 Windows 系统中,动态链接库的后缀名为 .dll。GCC 编译器在生成可执行文件时,默认会优先使用动态链接库完成链接,如果当前系统环境中没有程序文件所需要的动态链接库,GCC 便会选择静态链接库进行静态链接。如果两种库文件都没有找到,则链接失败。

3. 库文件与头文件

我们在发布库文件的同时,要将库文件和头文件一起发布,头文件中存储了变量、函数或者类等这些功能模块的声明部分,库文件中存储了各模块具体的实现部分。也就是说,头文件中定义了调用库文件中功能模块的接口。头文件的存在也实现了这样一种功能,当我们对外提供功能时,可以通过库文件来隐藏源码实现,功能的使用方只需要根据头文件所提供的接口来调用功能模块即可。

4. 库文件的引用

当我们使用 GCC 编译和链接程序时,GCC 默认会链接 libc.a 或者 libc.so这两个标准库,但是对于其他的库(非标准库、第三方库等),就需要手动去添加链接库。通过 GCC -l 选项来指定库名,直接在 -l 后面加库名即可。 ( -l 是小写的 L )

正常情况下,我们指定了要使用的库名时,GCC 会自动在标准库目录中搜索文件,例如在CentOS中是 /usr/lib 目录。但是,如果想链接位于其它目录中的库,比如说我们自己建的库,或者我们要引用别人提供的库,就需要在编译时显示指定库的路径。指定方法有三种:

① 像指定普通头文件的路径一样,为 GCC 显示指定该库文件的完整路径与文件名 -I /目录名

② 通过 GCC 的 -L 选项,为GCC增加搜索目录,可以使用多个 -L 选项,或者在一个选项内使用冒号 : 分割来指定多个搜索路径。

③ 把库文件所在的目录加到环境变量 LIBRARYPATH 中。

二、自己动手制作静态链接库

准备工作,共准备4个文件,目录结构如下

my_print.h 文件内容如下

#ifndef _TEST_H
#define _TEST_H
#include "stdio.h"
#include "stdlib.h"
void print_array_int(int* array, int len);
void print_array_char(char* array, int len);
void print_array_string(char* array[], int len);
void print_hello();
#endif

my_print.c 文件内容如下

#include "my_print.h"
void print_array_int(int* array, int len)
{   
    int i = 0; 
    printf("array: ");
    for(i = 0; i < len; i++)
    {   
        printf("%d ", array[i]);
    }
    printf("\n");
}
void print_array_char(char* array, int len)
{
  i = 0;
    printf("array: ");
    for(i = 0; i < len; i++)
    {
        printf("%c ", array[i]);
    }
     printf("\n");
}
void print_array_string(char* array[], int len)
{
    int i = 0;
    printf("array: ");
    for(i = 0; i < len; i++)
    {
        printf("%s ", array[i]);
    }
    printf("\n");
}
• 1
• 2
• 3
• 4
• 5
• 6
• 7
• 8
• 9
• 10
• 11
• 12
• 13
• 14
• 15
• 16
• 17
• 18
• 19
• 20
• 21
• 22
• 23
• 24
• 25
• 26
• 27
• 28
• 29
• 30
• 31
• 32
• 33
• 34

main.c 文件内容如下

#include <stdio.h>
#include <stdlib.h>
#include "my_print.h"
int main()
{
    int a1[10];
    int i = 0;
    for(i = 0; i < 10; i++)
    {
       a1[i] = i + 1;
    }
    print_array_int(a1, 10);
    print_hello();
    return 0;
}

print_hello.c 文件内容如下

#include "my_print.h"
void print_hello()
{
  printf("hello Linux ...\n");
}


相关文章
|
jenkins 持续交付
Jenkins Pipeline 参数化构建
Jenkins Pipeline 参数化构建
723 0
【计算机网络】第三章 数据链路层(可靠传输)
【计算机网络】第三章 数据链路层(可靠传输)
|
存储 Java
【数据结构】优先级队列(堆)从实现到应用详解
本文介绍了优先级队列的概念及其底层数据结构——堆。优先级队列根据元素的优先级而非插入顺序进行出队操作。JDK1.8中的`PriorityQueue`使用堆实现,堆分为大根堆和小根堆。大根堆中每个节点的值都不小于其子节点的值,小根堆则相反。文章详细讲解了如何通过数组模拟实现堆,并提供了创建、插入、删除以及获取堆顶元素的具体步骤。此外,还介绍了堆排序及解决Top K问题的应用,并展示了Java中`PriorityQueue`的基本用法和注意事项。
462 5
【数据结构】优先级队列(堆)从实现到应用详解
|
8月前
|
Ubuntu 安全 Unix
Linux和Ubuntu有什么区别
综上所述,Linux和Ubuntu之间存在明显的区别。Linux是一种操作系统内核,而Ubuntu是基于Linux内核的发行版本,具有更好的易用性、社区支持和软件仓库。用户可以根据自己的需求选择不同的Linux发行版本,如果需要一个稳定、易于使用的桌面环境,Ubuntu是一个不错的选择。如果需要更加灵活和定制性强的系统,其他Linux发行版本可能更加适合。
|
监控 负载均衡 安全
Elasticsearch 集群某一节点修改 IP 后无法启动问题复盘
Elasticsearch 集群某一节点修改 IP 后无法启动问题复盘
|
XML 数据库 Android开发
探索Android开发:从入门到精通的旅程
在这篇文章中,我们将一起踏上一段激动人心的旅程,通过深入浅出的方式,解锁Android开发的秘密。无论你是编程新手还是有经验的开发者,本文都将为你提供宝贵的知识和技能,帮助你构建出色的Android应用。我们将从基础概念开始,逐步深入到高级技巧和最佳实践,最终实现从初学者到专家的转变。让我们开始吧!
343 3
|
缓存 算法 网络协议
一文详细理解计算机网络 - 数据链路层(考试和面试必备)
这篇文章详细介绍了计算机网络中数据链路层的概念、基本问题、点对点信道和广播信道的数据链路协议(如PPP和CSMA/CD),以及局域网和以太网的相关知识。
4220 0
一文详细理解计算机网络 - 数据链路层(考试和面试必备)
|
存储 缓存 JSON
详解HTTP四种请求:POST、GET、DELETE、PUT
【4月更文挑战第3天】
74867 5
详解HTTP四种请求:POST、GET、DELETE、PUT
|
监控 JavaScript Shell
AutoxJS脚本保姆级教程
AutoxJS脚本保姆级教程
1222 3

热门文章

最新文章