• 动态库和静态库


    静态连接库就是把(lib)文件中用到的函数代码直接链接进目标程序,程序运行的时候不再需要其它的库文件;动态链接就是把调用的函数所在文件模块(DLL)和调用函数在文件中的位置等信息链接进目标程序,程序运行的时候再从DLL中寻找相应函数代码,因此需要相应DLL文件的支持。  

    静态链接库与动态链接库都是共享代码的方式,如果采用静态链接库,则无论你愿不愿意,lib 中的指令都全部被直接包含在最终生成的 EXE 文件中了。但是若使用 DLL,该 DLL 不必被包含在最终 EXE 文件中,EXE 文件执行时可以“动态”地引用和卸载这个与 EXE 独立的 DLL 文件。静态链接库和动态链接库的另外一个区别在于静态链接库中不能再包含其他的动态链接库或者静态库,而在动态链接库中还可以再包含其他的动态或静态链接库。


    “每一个lib文件就是若干函数(假设只有函数)的定义” 
    lib库有两种,一种是包含了函数所在DLL文件和文件中函数位置的信息,称为导出库;一种是包含函数代码本身,一般现有的DLL,用的是前一种库;以前在DOS下的TC/BC等,是后一种库。包含函数原型声明的,是头文件(.h)。  

      lib有静态lib和动态lib之分。

      静态lib将导出声明和实现都放在lib中。编译后所有代码都嵌入到宿主程序

      动态lib相当于一个h文件,是对实现部分(.dll文件)的导出部分的声明。编译后只是将导出声明部分编译到宿主程序中,运行时候需要相应的dll文件支持


      
    “通过#include包含这些函数声明的头文件后,我们的应用程序就可以使用lib文件中的函数”

    还要指定编译器链接相应的库文件。在IDE环境下,一般是一次指定所有用到的库文件,编译器自己寻找每个模块需要的库;在命令行编译环境下,需要指定每个模块调用的库。 

    “那他和直接给出那个函数定义的文件,比如.cpp文件,和头文件有什么区别,静态链接库有什么用” 
    cpp文件是源代码,库文件是编译后的二进制代码,比如你可以调用Windows的API,但是不能看到其源代码一样。 

    “还有不明白的是,静态链接库中的lib文件只要用到,则整个lib文件的内容都放进了exe文件中,那它是被编译进去还是链接的时候连接进去的呢?” 
    是在链接的时候将lib链接到目标代码中。

     

    静态链接库(Lib)
    在VC++6.0中new一个名称为libTest的static library工程,

    并新建lib.h和lib.cpp两个文件,lib.h和lib.cpp的源代码如下:

    //文件:lib.h
    #ifndef LIB_H
    #define LIB_H
    extern "C" int add(int x,int y);   //声明为C编译、连接方式的外部函数
    #endif

    //文件:lib.cpp
    #include "lib.h"
    int add(int x,int y)
    {
    return x + y;
    }


      编译这个工程就得到了一个.lib文件,这个文件就是一个函数库,它提供了add的功能。将头文件和.lib文件提交给用户后,用户就可以直接使用其中的add函数了。

      标准Turbo C2.0中的C库函数(我们用来的scanf、printf、memcpy、strcpy等)就来自这种静态库。

    下面来看看怎么使用这个库,在libTest工程所在的工作区内new一个libCall工程。libCall工程仅包含一个main.cpp文件,它演示了静态链接库的调用方法,其源代码如下:

     

     

    #include <stdio.h>
    #include "../lib.h"//不可丢失
    #pragma comment( lib, "..//debug//libTest.lib" )  //指定与静态库一起连接
    int main(int argc, char* argv[])
    {
    printf( "2 + 3 = %d", add( 2, 3 ) );
    }


      静态链接库的调用就是这么简单,或许我们每天都在用,可是我们没有明白这个概念。代码中#pragma comment( lib , "..//debug//libTest.lib" )的意思是指本文件生成的.obj文件应与libTest.lib一起连接


    ==================================================================================================================

    我们通常把一些公用函数制作成函数库,供其它程序使用。


      函数库分为静态库和动态库两种。

      静态库在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库。

      动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入,因此在程序运行时还需要动态库存在。

      本文主要通过举例来说明在Linux中如何创建静态库和动态库,以及使用它们。

      在创建函数库前,我们先来准备举例用的源程序,并将函数库的源程序编译成.o文件。

      第1步:编辑得到举例的程序--hello.h、hello.c和main.c;

      hello.h(见程序1)为该函数库的头文件。

      hello.c(见程序2)是函数库的源程序,其中包含公用函数hello,该函数将在屏幕上输出"Hello XXX!"。

      main.c(见程序3)为测试库文件的主程序,在主程序中调用了公用函数hello。

      程序1: hello.h

      #ifndef HELLO_H

      #define HELLO_H

      void hello(const char *name);

      #endif //HELLO_H

      程序2: hello.c

      #include 

      void hello(const char *name)

      {

      printf("Hello %s!/n", name);

      }

      程序3: main.c

      #include "hello.h"

      int main()

      {

      hello("everyone");

      return 0;

      }

      第2步:将hello.c编译成.o文件;

      无论静态库,还是动态库,都是由.o文件创建的。因此,我们必须将源程序hello.c通过gcc先编译成.o文件。

      在系统提示符下键入以下命令得到hello.o文件。

      # gcc -c hello.c

      #

      (注1:本文不介绍各命令使用和其参数功能,若希望详细了解它们,请参考其他文档。)

      (注2:首字符"#"是系统提示符,不需要键入,下文相同。)

      我们运行ls命令看看是否生存了hello.o文件。

      # ls

      hello.c hello.h hello.o main.c

      #

      (注3:首字符不是"#"为系统运行结果,下文相同。)

      在ls命令结果中,我们看到了hello.o文件,本步操作完成。

      下面我们先来看看如何创建静态库,以及使用它。

      第3步:由.o文件创建静态库;

      静态库文件名的命名规范是以lib为前缀,紧接着跟静态库名,扩展名为.a。例如:我们将创建的静态库名为myhello,则静态库文件名就是libmyhello.a。在创建和使用静态库时,需要注意这点。创建静态库用ar命令。

      在系统提示符下键入以下命令将创建静态库文件libmyhello.a。

      # ar cr libmyhello.a hello.o

      #

      我们同样运行ls命令查看结果:

      # ls

      hello.c hello.h hello.o libmyhello.a main.c

      #

      ls命令结果中有libmyhello.a。

      第4步:在程序中使用静态库;

      静态库制作完了,如何使用它内部的函数呢?只需要在使用到这些公用函数的源程序中包含这些公用函数的原型声明,然后在用gcc命令生成目标文件时指明静态库名,gcc将会从静态库中将公用函数连接到目标文件中。注意,gcc会在静态库名前加上前缀lib,然后追加扩展名.a得到的静态库文件名来查找静态库文件。

      在程序3:main.c中,我们包含了静态库的头文件hello.h,然后在主程序main中直接调用公用函数hello。下面先生成目标程序hello,然后运行hello程序看看结果如何。

      # gcc -o hello main.c -L. -lmyhello

      # ./hello

      Hello everyone!

      #

      我们删除静态库文件试试公用函数hello是否真的连接到目标文件 hello中了。

      # rm libmyhello.a

      rm: remove regular file `libmyhello.a'? y

      # ./hello

      Hello everyone!

      #

      程序照常运行,静态库中的公用函数已经连接到目标文件中了。

      我们继续看看如何在Linux中创建动态库。我们还是从.o文件开始。

      第5步:由.o文件创建动态库文件;

      动态库文件名命名规范和静态库文件名命名规范类似,也是在动态库名增加前缀lib,但其文件扩展名为.so。例如:我们将创建的动态库名为myhello,则动态库文件名就是libmyhello.so。用gcc来创建动态库。

      在系统提示符下键入以下命令得到动态库文件libmyhello.so。

      # gcc -shared -fPCI -o libmyhello.so hello.o

      #

      我们照样使用ls命令看看动态库文件是否生成。

      # ls

      hello.c hello.h hello.o libmyhello.so main.c

      #

      第6步:在程序中使用动态库;

      在程序中使用动态库和使用静态库完全一样,也是在使用到这些公用函数的源程序中包含这些公用函数的原型声明,然后在用gcc命令生成目标文件时指明动态库名进行编译。我们先运行gcc命令生成目标文件,再运行它看看结果。

      # gcc -o hello main.c -L. -lmyhello

      # ./hello

      ./hello: error while loading shared libraries: libmyhello.so: cannot open shared object file: No such file or directory

      #

      哦!出错了。快看看错误提示,原来是找不到动态库文件libmyhello.so。程序在运行时,会在/usr/lib和/lib等目录中查找需要的动态库文件。若找到,则载入动态库,否则将提示类似上述错误而终止程序运行。我们将文件 libmyhello.so复制到目录/usr/lib中,再试试。

      # mv libmyhello.so /usr/lib

      # ./hello

      Hello everyone!

      #

      成功了。这也进一步说明了动态库在程序运行时是需要的。

      我们回过头看看,发现使用静态库和使用动态库编译成目标程序使用的gcc命令完全一样,那当静态库和动态库同名时,gcc命令会使用哪个库文件呢?抱着对问题必究到底的心情,来试试看。

      先删除 除.c和.h外的 所有文件,恢复成我们刚刚编辑完举例程序状态。

      # rm -f hello hello.o /usr/lib/libmyhello.so

      # ls

      hello.c hello.h main.c

      #

      在来创建静态库文件libmyhello.a和动态库文件libmyhello.so。

      # gcc -c hello.c

      # ar cr libmyhello.a hello.o

      # gcc -shared -fPCI -o libmyhello.so hello.o

      # ls

      hello.c hello.h hello.o libmyhello.a libmyhello.so main.c

      #

      通过上述最后一条ls命令,可以发现静态库文件libmyhello.a和动态库文件libmyhello.so都已经生成,并都在当前目录中。然后,我们运行gcc命令来使用函数库myhello生成目标文件hello,并运行程序 hello。

      # gcc -o hello main.c -L. -lmyhello

      # ./hello

      ./hello: error while loading shared libraries: libmyhello.so: cannot open shared object file: No such file or directory

      #

      从程序hello运行的结果中很容易知道,当静态库和动态库同名时, gcc命令将优先使用动态库。




  • 相关阅读:
    Spring Boot 2.x基础教程:Swagger接口分类与各元素排序问题详解
    Spring Boot 2.x基础教程:JSR-303实现请求参数校验
    Spring Boot 2.x基础教程:使用Swagger2构建强大的API文档
    Spring Boot 2.x基础教程:构建RESTful API与单元测试
    IntelliJ中Git突然不能用,报错 xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools)
    OpenWrite插件上架Google商店,插件安装更加容易!用户安全更有保障!
    为什么我建议大家将原创文章分发到多个平台?
    Spring Cloud与Dubbo的完美融合之手「Spring Cloud Alibaba」
    程序员干私活搞副业,个税问题搞清楚没?今年到手的2000明年还是2000吗?
    RabbitMQ延迟消息的延迟极限是多少?
  • 原文地址:https://www.cnblogs.com/huangmr0811/p/5570974.html
Copyright © 2020-2023  润新知