精华内容
下载资源
问答
  • 静态的调用方法 第一种:项目设置中引用,在项目的属性中设置。 第二种:在代码中使用 #pragma comment(lib,"lib文件名") 第一种方法: 步骤一: 右键单击项目—>属性—>配置属性—>链接器...
    一.使用方法
    静态库的调用方法
    第一种:项目设置中引用,在项目的属性中设置。
    第二种:在代码中使用 #pragma comment(lib,"lib文件名")

    第一种方法:
    步骤一:
    右键单击项目—>属性—>配置属性—>链接器—>常规—>附加库目录
    在其中填入lib库的目录,也可以是相对或绝对路径。

    步骤二:
    右键单击项目—>属性—>配置属性—>链接器—>输入—>附加依赖项
    在其中填入lib库的名称,如:Test.lib
    其等价于程序中的#pragma comment(lib,"*.lib")

    第二种方法:
    #progma comment(lib,"lib文件名"); 中lib文件名也可以是带相对路径或是绝对路径的lib。
    #pragma comment(lib,"..\\lib文件名")
    如果是不带路径的lib文件名,则可以通过第一种方法的步骤一的方式来指定该lib的路径。


    动态库的隐式调用有两种方法(与静态库的两种引用方法一样):
    第一种:项目设置中引用,在项目的属性中设置,在其中填入lib库的名称,如:Test.lib
    第二种:在代码中使用 #progma comment(lib,"lib文件名");

    编译链接时需要.lib,执行时需要.dll。


    动态库的显示调用方法:
    1、创建一个函数指针,其指针数据类型要与调用的 DLL 引出函数相吻
    合。
    2、通过 Win32 API 函数LoadLibrary()显式的调用DLL,此函数返回
    DLL 的实例句柄。
    3、通过 Win32 API 函数GetProcAddress()获取要调用的DLL 的函数地
    址,把结果赋给自定义函数的指针类型。
    4、使用函数指针来调用 DLL 函数。
    5、最后调用完成后,通过 Win32 API 函数FreeLibrary()释放DLL 函数
    二.静态库动态库原理

    下面内容来自 https://blog.csdn.net/sya_inn/article/details/53981440

    采用lib文件调用DLL(采用Lib文件的调用方式又被称为静态调用)

    静态调用定义:

    静态调用,也称为隐式调用,由编译系统完成对DLL的加载和应用程序结束时DLL卸载的编码(Windows系统负责对DLL调用次数的计数),调用方式简单,能够满足通常的要求。通常采用的调用方式是把产生动态连接库时产生的.LIB文件加入到应用程序的工程中,想使用DLL中的函数时,只须在源文件中声明一下。 LIB文件包含了每一个DLL导出函数的符号名和可选择的标识号以及DLL文件名,不含有实际的代码(这种lib文件被称为导入库,如果包含实际的可执行代码,则被称为静态库)。Lib文件包含的信息进入到生成的应用程序中,被调用的DLL文件会在应用程序加载时同时加载在到内存中。 

    Lib文件的分类以及本质(重要)

    程序编译的时候,链接器需要使用此文件。里面封装了具体的函数实现,变量定义等。而函数接口,变量申明还是通过.h文件给出来。目前以lib后缀的库有两种:一种为静态链接库(StaticLibary,以下简称静态库”);另一种为动态连接库(DLL,以下简称动态库”)的导入库(Import Libary,以下简称导入库):注意另一种libDLL到入库。静态库和导入库(注意千万不要把lib文件的导入库,静态库的概念和DLL的动态调用,静态调用搞混淆了)的区别很大,他们实质是不一样的东西。静态库本身就包含了实际执行代码、符号表等等,而对于导入库而言,其实际的执行代码位于动态库中,导入库只包含了地址符号表,各个函数地址等等,确保程序找到对应函数的一些基本地址信息。

    静态Lib库(可执行文件中间文件)

    静态库是一个或者多个obj文件的打包,所以有人干脆把从obj文件生成lib的过程称为Archive,即合并到一起。比如你链接一个静态库,如果其中有错,它会准确的找到是哪个obj有错,即静态lib只是壳子。如果只有lib文件,那么这个lib文件是静态编译出来的,索引和实现都在其中。静态编译的lib文件有好处:给用户安装时就不需要再挂动态库了。但也有缺点,就是导致应用程序比较大,而且失去了动态库的灵活性,在版本升级时,同时要发布新的应用程序才行。

    导入lib库(符号集合)

    有两个文件,而一个是导入库(.LIB)文件,一个是DLL文件,导入库文件包含被DLL导出的函数的名称和位置,DLL包含实际的函数和数据,应用程序使用LIB文件链接到所需要使用的DLL文件,库中的函数和数据并不复制到可执行文件中,因此在应用程序的可执行文件中,存放的不是被调用的函数代码,而是DLL中所要调用的函数的内存地址,这样当一个或多个应用程序运行是再把程序代码和被调用的函数代码链接起来,从而节省了内存资源。从上面的说明可以看出,DLL.LIB文件必须随应用程序一起发行,否则应用程序将会产生错误。

    如果既不想用静态Lib库,又不想用导入Lib库,就需要动态调用DLL了:需要自己LoadLibary调入DLL文件,然后再手工GetProcAddress获得对应函数了(动态调用DLL),前提是需要对DLL内部函数申明熟悉。

    三.C语言中的.h和.cpp 来自 https://www.cnblogs.com/laojie4321/archive/2012/03/30/2425015.html

    简单的说其实要理解C文件与头文件(即.h)有什么不同之处,首先需要弄明白编译器的工作过程,一般说来编译器会做以下几个过程:

          1.预处理阶段

      2.词法与语法分析阶段          (1.2理解为预编译阶段)

      3.编译阶段,首先编译成纯汇编语句,再将之汇编成跟CPU相关的二进制码,生成各个目标文件 (.obj文件)

      4.连接阶段,将各个目标文件中的各段代码进行绝对地址定位,生成跟特定平台相关的可执行文件,当然,最后还可以用objcopy生成纯二进制码,也就是去掉了文件格式信息。(生成.exe文件)

     

      编译器在编译时是以C文件为单位进行的,也就是说如果你的项目中一个C文件都没有,那么你的项目将无法编译,连接器是以目标文件为单位,它将一个或多个目标文件进行函数与变量的重定位,生成最终的可执行文件,在PC上的程序开发,一般都有一个main函数,这是各个编译器的约定,当然,你如果自己写连接器脚本的话,可以不用main函数作为程序入口!!!!

      (main .c文件 目标文件 可执行文件)

      有了这些基础知识,再言归正传,为了生成一个最终的可执行文件,就需要一些目标文件,也就是需要C文件,而这些C文件中又需要一个main函数作为可执行程序的入口,那么我们就从一个C文件入手,假定这个C文件内容如下:

     

      #include <stdio.h>

      #include "mytest.h"

      int main(int argc,char **argv)

      { 

        test = 25;

        printf("test.................%d\n",test);

      }

      mytest.h头文件内容如下:

      int test;

     

      现在以这个例子来讲解编译器的工作:

      1.预处理阶段:编译器以C文件作为一个单元,首先读这个C文件,发现第一句与第二句是包含一个头文件,就会在所有搜索路径中寻找这两个文件,找到之后,就会将相应头文件中再去处理宏,变量,函数声明,嵌套的头文件包含等,检测依赖关系,进行宏替换,看是否有重复定义与声明的情况发生,最后将那些文件中所有的东东全部扫描进这个当前的C文件中,形成一个中间"C文件"

      2.编译阶段,在上一步中相当于将那个头文件中的test变量扫描进了一个中间C文件,那么test变量就变成了这个文件中的一个全局变量,此时就将所有这个中间C文件的所有变量,函数分配空间,将各个函数编译成二进制码,按照特定目标文件格式生成目标文件,在这种格式的目标文件中进行各个全局变量,函数的符号描述,将这些二进制码按照一定的标准组织成一个目标文件

      3.连接阶段,将上一步成生的各个目标文件,根据一些参数,连接生成最终的可执行文件,主要的工作就是重定位各个目标文件的函数,变量等,相当于将个目标文件中的二进制码按一定的规范合到一个文件中再回到C文件与头文件各写什么内容的话题上:理论上来说C文件与头文件里的内容,只要是C语言所支持的,无论写什么都可以的,比如你在头文件中写函数体,只要在任何一个C文件包含此头文件就可以将这个函数编译成目标文件的一部分(编译是以C文件为单位的,如果不在任何C文件中包含此头文件的话,这段代码就形同虚设),你可以在C文件中进行函数声明,变量声明,结构体声明,这也不成问题!!!那为何一定要分成头文件与C文件呢?又为何一般都在头件中进行函数,变量声明,宏声明,结构体声明呢?而在C文件中去进行变量定义,函数实现呢??原因如下

      1.如果在头文件中实现一个函数体,那么如果在多个C文件中引用它,而且又同时编译多个C文件,将其生成的目标文件连接成一个可执行文件,在每个引用此头文件的C文件所生成的目标文件中,都有一份这个函数的代码,如果这段函数又没有定义成局部函数,那么在连接时,就会发现多个相同的函数,就会报错 

      2.如果在头文件中定义全局变量,并且将此全局变量赋初值,那么在多个引用此头文件的C文件中同样存在相同变量名的拷贝,关键是此变量被赋了初值,所以编译器就会将此变量放入DATA段,最终在连接阶段,会在DATA段中存在多个相同的变量,它无法将这些变量统一成一个变量,也就是仅为此变量分配一个空间,而不是多份空间,假定这个变量在头文件没有赋初值,编译器就会将之放入 BSS段,连接器会对BSS段的多个同名变量仅分配一个存储空间

      3.如果在C文件中声明宏,结构体,函数等,那么我要在另一个C文件中引用相应的宏,结构体,就必须再做一次重复的工作,如果我改了一个C文件中的一个声明,那么又忘了改其它C文件中的声明,这不就出了大问题了,程序的逻辑就变成了你不可想象的了,如果把这些公共的东东放在一个头文件中,想用它的C文件就只需要引用一个就OK了!!!这样岂不方便,要改某个声明的时候,只需要动一下头文件就行了

      4.在头文件中声明结构体,函数等,当你需要将你的代码封装成一个库,让别人来用你的代码,你又不想公布源码,那么人家如何利用你的库呢?也就是如何利用你的库中的各个函数呢??一种方法是公布源码,别人想怎么用就怎么用,另一种是提供头文件,别人从头文件中看你的函数原型,这样人家才知道如何调用你写的函数,就如同你调用printf函数一样,里面的参数是怎样的??你是怎么知道的??还不是看人家的头文件中的相关声明啊!!!当然这些东东都成了C标准,就算不看人家的头文件,你一样可以知道怎么使用

     

      c语言中.c和.h文件的困惑

      本质上没有任何区别。 只不过一般:.h文件是头文件,内含函数声明、宏定义、结构体定义等内容

      .c文件是程序文件,内含函数实现,变量定义等内容。而且是什么后缀也没有关系,只不过编译器会默认对某些后缀的文件采取某些动作。你可以强制编译器把任何后缀的文件都当作c文件来编。

      这样分开写成两个文件是一个良好的编程风格。

      而且,比方说 我在aaa.h里定义了一个函数的声明,然后我在aaa.h的同一个目录下建立aaa.c ,aaa.c里定义了这个函数的实现,然后是在main函数所在.c文件里#include这个aaa.h 然后我就可以使用这个函数了。 main在运行时就会找到这个定义了这个函数的aaa.c文件。

    //感觉作者下面说的有点问题  

    这是因为:

      main函数为标准C/C++的程序入口,编译器会先找到该函数所在的文件

      假定编译程序编译myproj.c(其中含main())时,发现它include了mylib.h(其中声明了函数void test()),那么此时编译器将按照事先设定的路径(Include路径列表及代码文件所在的路径)查找与之同名的实现文件(扩展名为.cpp或.c,此例中为mylib.c),如果找到该文件,并在其中找到该函数(此例中为void test())的实现代码,则继续编译;如果在指定目录找不到实现文件,或者在该文件及后续的各include文件中未找到实现代码,则返回一个编译错误.其实include的过程完全可以"看成"是一个文件拼接的过程,将声明和实现分别写在头文件及C文件中,或者将二者同时写在头文件中,理论上没有本质的区别。

     

      以上是所谓动态方式。

    //感觉作者上面说的有点问题-----------------在后面会说说网友的评论和自己的观点

      对于静态方式,基本所有的C/C++编译器都支持一种链接方式被称为Static Link,即所谓静态链接。

      在这种方式下,我们所要做的,就是写出包含函数,类等等声明的头文件(a.h,b.h,...),以及他们对应的实现文件(a.cpp,b.cpp,...),编译程序会将其编译为静态的库文件(a.lib,b.lib,...)。在随后的代码重用过程中,我们只需要提供相应的头文件(.h)和相应的库文件(.lib),就可以使用过去的代码了。

      相对动态方式而言,静态方式的好处是实现代码的隐蔽性,即C++中提倡的"接口对外,实现代码不可见"。有利于库文件的转发.

     

      如果说难题最难的部分是基本概念,可能很多人都会持反对意见,但实际上也确实如此。我高中的时候学物理,老师抓的重点就是概念--概念一定要搞清,于是难题也成了容易题。如果你能分析清楚一道物理难题存在着几个物理过程,每一个过程都遵守那一条物理定律(比如动量守恒、牛II定律、能量守恒),那么就很轻松的根据定律列出这个过程的方程,N个过程必定是N个N元方程,难题也就迎刃而解。即便是高中的物理竞赛难题,最难之处也不过在于:

      (1)、混淆你的概念,让你无法分析出几个物理过程,或某个物理过程遵循的那条物理定律;

      (2)、存在高次方程,列出方程也解不出。而后者已经是数学的范畴了,所以说,最难之处还在于掌握清晰的概念;

      程序设计也是如此,如果概念很清晰,那基本上没什么难题(会难在数学上,比如算法的选择、时间空间与效率的取舍、稳定与资源的平衡上)。但是,要掌握清晰的概念也没那么容易。比如下面这个例子,看看你有没有很清晰透彻的认识。 //a.h void foo(); //a.c #include "a.h" //我的问题出来了:这句话是要,还是不要? void foo() { return; } //main.c #include "a.h" int main(int argc, char *argv[]) { foo(); return 0; }

     

      针对上面的代码,请回答三个问题: a.c 中的 #include "a.h" 这句话是不是多余的?

      为什么经常见 xx.c 里面 include 对应的 xx.h?

      如果 a.c 中不写,那么编译器是不是会自动把 .h 文件里面的东西跟同名的 .c 文件绑定在一起?(不会)

      (请针对上面3道题仔细考虑10分钟,莫要着急看下面的解释。:) 考虑的越多,下面理解的就越深。)

      好了,时间到!请忘掉上面的3道题,以及对这三道题引发出的你的想法,然后再听我慢慢道来。正确的概念是:从C编译器角度看,.h和.c皆是浮云,就是改名为.txt、.doc也没有大的分别。换句话说,就是.h和.c没啥必然联系。.h中一般放的是同名.c文件中定义的变量、数组、函数的声明,需要让.c外部使用的声明。这个声明有啥用?只是让需要用这些声明的地方方便引用。因为 #include "xx.h" 这个宏其实际意思就是把当前这一行删掉,把 xx.h 中的内容原封不动的插入在当前行的位置。由于想写这些函数声明的地方非常多(每一个调用 xx.c 中函数的地方,都要在使用前声明一下子),所以用 #include "xx.h" 这个宏就简化了许多行代码--让预处理器自己替换好了。也就是说,xx.h 其实只是让需要写 xx.c 中函数声明的地方调用(可以少写几行字),至于 include 这个 .h 文件是谁,是 .h 还是 .c,还是与这个 .h 同名的 .c,都没有任何必然关系。

      这样你可能会说:啊?那我平时只想调用 xx.c 中的某个函数,却 include了 xx.h 文件,岂不是宏替换后出现了很多无用的声明?没错,确实引入了很多垃圾,但是它却省了你不少笔墨,并且整个版面也看起来清爽的多。鱼与熊掌不可得兼,就是这个道理。反正多些声明(.h一般只用来放声明,而放不定义,参见拙著"过马路,左右看")也无害处,又不会影响编译,何乐而不为呢?

     

      翻回头再看上面的3个问题,很好解答了吧?答:不一定。这个例子中显然是多余的。但是如果.c中的函数也需要调用同个.c中的其它函数,那么这个.c往往会include同名的.h,这样就不需要为声明和调用顺序而发愁了(C语言要求使用之前必须声明,而include同名.h一般会放在.c的开头)。有很多工程甚至把这种写法约定为代码规范,以规范出清晰的代码来。

     

      答:1中已经回答过了。

      答:不会。问这个问题的人绝对是概念不清,要不就是想混水摸鱼。非常讨厌的是中国的很多考试出的都是这种烂题,生怕别人有个清楚的概念了,绝对要把考生搞晕。

      搞清楚语法和概念说易也易,说难也难。窍门有三点: 不要晕着头工作,要抽空多思考思考,多看看书;

      看书要看好书,问人要问强人。烂书和烂人都会给你一个错误的概念,误导你;

      勤能补拙是良训,一分辛苦一分才;

     

      (1)通过头文件来调用库功能。在很多场合,源代码不便(或不准)向用户公布,只要向用户提供头文件和二进制的库即可。用户只需要按照头文件中的接口声明来调用库功能,而不必关心接口怎么实现的。编译器会从库中提取相应的代码。

      (2)头文件能加强类型安全检查。如果某个接口被实现或被使用时,其方式与头文件中的声明不一致,编译器就会指出错误,这一简单的规则能大大减轻程序员调试、改错的负担。

      头文件用来存放函数原型。

      头文件如何来关联源文件?

     

      这个问题实际上是说,已知头文件"a.h"声明了一系列函数(仅有函数原型,没有函数实现),"b.cpp"中实现了这些函数,那么如果我想在"c.cpp"中使用"a.h"中声明的这些在"b.cpp"中实现的函数,通常都是在"c.cpp"中使用#include "a.h",那么c.cpp是怎样找到b.cpp中的实现呢?

      其实.cpp和.h文件名称没有任何直接关系,很多编译器都可以接受其他扩展名。

     

      谭浩强老师的《C程序设计》一书中提到,编译器预处理时,要对#include命令进行"文件包含处理":将headfile.h的全部内容复制到#include "headfile.h"处。这也正说明了,为什么很多编译器并不care到底这个文件的后缀名是什么----因为#include预处理就是完成了一个"复制并插入代码"的工作。

      程序编译的时候,并不会去找b.cpp文件中的函数实现,只有在link的时候才进行这个工作。我们在b.cpp或c.cpp中用#include "a.h"实际上是引入相关声明,使得编译可以通过,程序并不关心实现是在哪里,是怎么实现的。源文件编译后成生了目标文件(.o或.obj文件),目标文件中,这些函数和变量就视作一个个符号。在link的时候,需要在makefile里面说明需要连接哪个.o或.obj文件(在这里是b.cpp生成的.o或.obj文件),此时,连接器会去这个.o或.obj文件中找在b.cpp中实现的函数,再把他们build到makefile中指定的那个可以执行文件中。

     (非常重要)

      在VC中,一帮情况下不需要自己写makefile,只需要将需要的文件都包括在project中,VC会自动帮你把makefile写好。

    下面需要总结下,为什么静态库的引用,以及动态库的静态引用需要使用.h文件,而动态库的动态引用不需要.h文件。

    这个就是基本概念的问题,因为前两种引用,我们想直接使用其中的函数,那么我们就要声明这个函数,而.h文件中包含了这个声明,所以我们就要用到.h文件,而动态库的动态引用,是通过加载的方式得到了dll的句柄,同时通过GetProcAddress()获取到函数的地址,然后我们是拿地址直接调用函数,所以不需要对函数进行声明了。所以不需要.h文件。



    展开全文
  • 多个静态库链接为一个动态库,提供统一的接口给外部使用。 看一下例子的目录文件: 编译后将生成 libAdd.a libMutiply.a , 然后将这两个.a静态库和apl_myApi.o 链接成为一个动态库 libMyApi.so。提供这个动态库...

    目标:

    将多个静态库链接为一个动态库,提供统一的接口给外部使用。

    看一下例子的目录文件:


    编译后将生成 libAdd.a libMutiply.a , 然后将这两个.a静态库和apl_myApi.o 链接成为一个动态库 libMyApi.so。提供这个动态库给app.cpp使用。

    (1)apl_add.cpp 及 apl_add.h 的内容

    #include "apl_add.h"
    
    int add(int a, int b)
    {
    	return a + b;
    }
    
    int add(int, int);
    (2) apl_mutiply.cpp 及 apl_mutiply.h的内容

    #include "apl_mutiply.h"
    
    int mutiply(int a, int b)
    {
    	return a * b;
    }
    
    #include "apl_mutiply.h"
    
    int mutiply(int a, int b);
    
    (3) apl_myApi.cpp 及 apl_myApi.h的内容

    #include "apl_add.h"
    #include "apl_mutiply.h"
    #include "apl_myApi.h"
    
    int my_add(int a, int b)
    {
    	return add(a,b);
    }
    
    int my_mutiply(int a, int b)
    {
    	return mutiply(a, b);
    }

    int my_add(int a, int b);
    int my_mutiply(int, int);

    (4)app.cpp内容

    #include <iostream>
    #include "apl_myApi.h"
    using namespace std;
    
    int main()
    {
    	int a = 10, b = 15;
    	int sum = my_add(a, b);
    	cout << "sum = " << sum << endl;
    	int mut = my_mutiply(a, b);
    	cout << "mutiply = " << mut << endl;
    	return 0;
    }
    (5)allMake.sh 这个shell脚本是编译全部文件,内容如下:

    #!/bin/bash
    
    echo "start building libAdd.a libMutiply.a libMyApi_so.so app......"
    
    g++ -c -fPIC apl_add.cpp
    ar -rcs libAdd.a apl_add.o
    
    g++ -c -fPIC apl_mutiply.cpp
    ar -rcs libMutiply.a apl_mutiply.o
    
    # two static .a lib link to a dynamic .so lib
    g++ -c -fPIC apl_myApi.cpp
    g++ -shared -fPIC apl_myApi.o -o libMyApi_so.so -L. -lAdd -lMutiply
    
    g++ -c app.cpp
    g++ app.o -o app -L. -lMyApi_so
    
    sudo cp libMyApi_so.so /usr/lib
    echo "build done.........."
    
    
    
    注意: 这往下是想把LibAdd.a 和 libMutiply.a 链接成LibMyApi_a.a, 发现不行在编译app.cpp的时候还是需要将这两个库加上不然编译不过。
    或者直接将apl_add.o 和apl_mutiply.o 和 apl_myApi.o 一起编译成 libMyApi.a。
    echo "start building libAdd.a libMutiply.a libMyApi_a.a app_s......"
    
    g++ -c apl_add.cpp
    ar -rcs libAdd.a apl_add.o
    
    g++ -c apl_mutiply.cpp
    ar -rcs libMutiply.a apl_mutiply.o
    
    # two static .a lib link to a static .a lib
    g++ -c apl_myApi.cpp
    ar -rcs libMyApi_a.a apl_myApi.o #apl_add.o apl_mutiply.o #`ar -x ./libAdd.a ./libMutiply.a`
    
    g++ -c app.cpp
    g++ app.o -o app_s -L. -lMyApi_a -lAdd -lMutiply
    
    echo "end building................"
    (6)clearAll.sh shell脚本是清除目标文件
    #!/bin/bash
    
    sudo rm -rf *.a *.o *.so app *.bak


    例子比较简单,记录下生成静态库和动态库的套路。


    补充:

    问题描述:

    假如apl_add.c,apl_add.h 用gcc 编译为aplAdd.a静态库; apl_mutiply.cpp, apl_mutiply.h 用g++编译为aplMutiply.a静态库。 这时候如果apl_myApi.cpp在包含头文件 apl_add.h的时候如果没有使用extern "C" {} ,编译也不会报错,但是生成的libMyApi.so 在被app.cpp链接的时候会报错:apl_add.c中的函数add没有定义。原因是因为

    aplAdd.a是gcc编译出来的,而编译链接libMyApi.so使用的是g++编译器。因此在apl_myApi.cpp中必须这样:


    extern "C"

    {

         #include "apl_add.h"

    }

    展开全文
  • h头文件是编译时必须的,lib是链接时需要的,dll是运行时需要的。   附加依赖项的是.lib不是.dll,若生成了dll,则肯定也生成lib文件。如果要完成源代码的编译和链接,有头文件和lib就够了。如果要使需要动态连接...

    转自:http://blog.csdn.net/phenixyf/article/details/42237079


    h头文件是编译时必须的,lib是链接时需要的,dll是运行时需要的。

     

    附加依赖项的是.lib不是.dll,若生成了dll,则肯定也生成lib文件。如果要完成源代码的编译和链接,有头文件和lib就够了。如果要使需要动态连接的程序运行起来,有dll就够了。在开发和调试阶段,当然最好都有。

     

    .h .lib .dll三者的关系是:

    h文件作用是:声明函数接口

    lib文件作用是:二进制函数实现代码或函数在dll文件中的索引地址

    dll文件作用是:函数可执行文件

     

    当我们在自己的程序中引用了一个h文件里的函数,编链器怎么知道该调用哪个dll文件呢?这就是lib文件的作用:告诉链接器调用的函数在哪个dll中,函数执行代码在dll中的什么位置,这也就是为什么需要附加依赖项 .lib文件,它起到桥梁的作用。如果生成静态库文件,则没有dll ,只有lib,这时函数可执行代码部分也在lib文件中


    一 以lib为后缀的两种库和动态库

    目前以lib后缀的库有两种,一种为静态链接库(Static Libary,以下简称“静态库”),另一种为动态连接库(dll,以下简称“动态库”)的导入库(Import Libary,以下简称“导入库”)。


    静态库是一个或者多个obj文件的打包,所以有人干脆把从obj文件生成lib的过程称为Archive,即合并到一起。比如你链接一个静态库,如果其中有错,它会准确的找到是哪个obj有错,即静态lib只是壳子。导入库一般对应动态库,方便程序静态载入动态链接库,否则你可能就需要自己LoadLibary调入dll文件,然后再手工GetProcAddress获得对应函数了。有了导入库,你只需要链接导入库(lib)后按照头文件函数接口的声明调用函数就可以了。


    静态库和导入库的区别很大,他们实质是不一样的东西。静态库本身就包含了实际执行代码、符号表等等,而对于导入库而言,其实际的执行代码位于动态库中,导入库只包含了地址符号表等,确保程序找到对应函数的一些基本地址信息。


    一般的动态库程序有lib文件和dll文件。lib文件是必须在编译期就连接到应用程序中的,而dll文件是运行期才会被调用的。如果有dll文件,那么对应的lib文件一般是一些索引信息,具体的实现在dll文件中。如果只有lib文件,那么这个lib文件是静态编译出来的,索引和实现都在其中。


    静态库与动态库的优缺点

    静态编译的lib文件有好处:给用户安装时就不需要再挂动态库了。但也有缺点,就是导致应用程序比较大,而且失去了动态库的灵活性,在版本升级时,同时要发布新的应用程序才行。

     

    在动态库的情况下,有两个文件,而一个是引入库(lib)文件,一个是dll文件。引入库文件包含被dll导出的函数的名称和位置,dll包含实际的函数和数据,应用程序使用lib文件链接到所需要使用的dll文件,库中的函数和数据并不复制到可执行文件中,因此在应用程序的可执行文件中,存放的不是被调用的函数代码,而是dll中所要调用的函数的内存地址。这样当一个或多个应用程序运行是再把程序代码和被调用的函数代码链接起来,从而节省了内存资源。从上面的说明可以看出,dll和.lib文件必须随应用程序一起发行,否则应用程序将会产生错误。


    二 静态链接库(lib)与动态链接库(dll)的区别

    1  区别一

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

     

    静态链接库与动态链接库都是共享代码的方式,如果采用静态链接库,则无论你愿不愿意,lib中的指令都全部被直接包含在最终生成的exe文件中了。但是若使用dll,该dll不必被包含在最终exe文件中,exe文件执行时可以“动态”地引用和卸载这个与exe独立的dll文件。


    2  区别二

    静态链接库和动态链接库的另外一个区别在于静态链接库中不能再包含其他的动态链接库或者静态库,而在动态链接库中还可以再包含其他的动态或静态链接库。


    问题1:每一个lib文件就是若干函数(假设只有函数)的定义

    通过#include包含这些函数声明的头文件后,指定编译器链接相应的库文件,我们的应用程序就可以使用lib文件中的函数。在IDE环境下,一般是一次指定所有用到的库文件,编译器自己寻找每个模块需要的库;在命令行编译环境下,需要指定每个模块调用的库。

     

    问题2:cpp文件和静态链接库有什么用

    cpp文件是源代码,库文件是编译后的二进制代码,比如你可以调用Windows的API,但是不能看到其源代码一样。

     

    问题3:静态链接库中的lib文件只要用到,则整个lib文件的内容都放进了exe文件中,那它是被编译进去还是链接的时候连接进去的呢?

    是在链接的时候将lib链接到目标代码中。


    三 在VS工程中,添加c/c++工程中外部头文件及库的基本步骤

    1、添加工程的头文件目录:工程---属性---配置属性---c/c++---常规---附加包含目录:加上头文件存放目录。
    2、添加文件引用的lib静态库路径:工程---属性---配置属性---链接器---常规---附加库目录:加上lib文件存放目录。
             然后添加工程引用的lib文件名:工程---属性---配置属性---链接器---输入---附加依赖项:加上lib文件名。
    3、添加工程引用的dll动态库:把引用的dll放到工程的可执行文件所在的目录下。


    注意:第一步可以不用,直接在工程里加入动态库的头文件,在使用代码处引用这个头文件。


    举例一

    1  静态库的创建

    在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等)就来自这种静态库。


    2  静态库的使用

    下面来看看怎么使用这个库,在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一起连接


    举例二

    1  用VC++生成静态库文件

    今天闲着没事做,自己写了一点小笔记,不知道对于新手有没用,高手就不用看了,作为新手的我斗胆来发表一个笔记,就是静态库文件的封装过程,使用VC++6.0编写,下面是正文,也许我的用语并不专业。

    以前我们写C/C++源文件的时候,都是先将各个写好的源文件编译,编译生成的是目标文件机器码,即.obj文件(目标文件的扩展名不一定是.obj文件)。

    我们调用的标准C/C++函数机器码实际被封装于标准C/C++静态库文件中,即扩展名为.lib的文件中。

    最后链接器将我们编译的各个目标文件里的机器码和静态库(标准C/C++库)中的函数机器码链接到一起形成一个扩展名为.exe的可执行文件模块。

     

    在这里我们叙述将C/C++源文件编译链接成一个静态库文件,但它不是可执行模块,它体内含有可执行机器码。静态库文件就像一个仓库或者容器,里面封装了一些可执行机器码。这些机器码是我们用程序设计语言,比如C/C++源文件编译后生成的机器码。

     

    下面将讨论将C/C++源文件编译并链接成一个静态库文件的过程。

    在VC++6.0中选择File-New-Win32 Static Library,写好工程名创建好工作空间后再选择菜单中New-File来为工程添加C或者C++ 源文件.

    假如我们为该工程添加了一个名为lib_c.c和一个名为lib_cpp.cpp的源文件

    //lib_c.c中的内容

    extern int Add(int x,int y) //该函数是一个外部函数,任何文件都可以访问它

    {

        return x+y;

    }

    extern int data_c

    //这是一个外部全局变量,任何文件可以访问它

    //lib_cpp.cpp中的内容

    extern “C” int

            reduce(int x,int y)//这里加了个”C”表示允许C源文件访问这个C++函数代码

    {

        return x-y;

    }

    extern “C” int data_cpp=2;

    注意以下几点

    1)  当“extern”关键字修饰在函数或全局变量的定义中时,表示该函数或全局变量任何文件可以访问,“extern”关键字可以省略不写,缺省下就是”extern”

    当“extern”关键字修饰在函数声明或全局变量声明中时,表示限定当前文件只能引用用“extern”关键字修饰定义的函数或全局变量.

    2)  当”static”关键字修饰在函数或全局变量的定义中时,表示该函数或全局变量只能由本文件中加了”static”关键字修饰的函数声明或全局变量声明来引用。

    当”static”关键字修饰在函数声明或全局变量声明中时,表示限定当前文件只能引用用“static”关键字修饰定义的函数或全局变量。

    3)  在CPP源文件的函数和全局变量定义中加了个”C”表示允许C源文件访问该函数和全局变量。如果是C++源文件访它们的话则可加可不加.注意这”C”要大写。

     

    接下来就要将写好的C/C++源文件进行编译和链接,最后会生成一个扩展名为.lib的文件,该文件就是静态库文件了,该静态库文件是不能直接运行的,我们所编译的C/C++源文件的机器码就已经被封装进这个用VC++6.0创建的静态库文件里面去了。


    2  将编译好的静态库像标准库那样使用

    如何将编写好的静态库文件像使用C/C++标准库那样使用,下面将继续讨论

    1)  用VC++6.0新建一个工程名为TEST,添加一个名为TEST.c的源文件到该工程,因为我们将测试一下,将我们编写的库文件里的函数或者全局变量的机器码链接到我们这个TEST.c源文件中去,假设我们生成的库文件名为TEST.lib,先拷贝如下范例代码到TEST.c中

    //TEST.c

    #include <stdio.h>

    extern int  Add(int x,int y);//当前文件只能访问“extern”关键字修饰定义的Add函数

    extern int reduce(int x,int y);//当前文件只能访问“extern”关键字修饰定义的reduce函数

    #pragma comment(lib,"TEST.lib") //指示链接器到字符串所表示的文件路径中去找库文件

    int main()

    {

        printf("%d\n",Add(2,3));

        printf("%d\n",reduce(3,2));

        return 0;

    }

    这里我们要声明静态库中已知的函数或全局变量的声明

    方法一,#pragmacomment(lib,"TEST.lib")这条指令告诉链接器到字符串所表示的路径下去找库文件,这里我将库文件放到了当前工程目录下,也可以不写这句.

    方法二,可以直接在VC++6.0中设置依次选择tools、options、directories、library files菜单或选项,填入库文件路径(只键入库文件所在目录路径而不能输入库文件名),这只是告诉链接器库文件所在目录的路径,还没告诉链接器库文件名,方法是VC++6.0中设置依次选择project-settings-link 在object/library modules: 这栏输入库文件名字然后就OK了

    2)  当用C++源文件的目标文件和库文件的代码链接时有一点小改变,这里就不浪费口舌了,假设我们新建了一个工程并添加了一个名为TEST.CPP的源文件,拷贝如下范例代码到TEST.CPP中

    //TEST.cpp

    #include <stdio.h>

    extern “C” int

           Add(int x,int y); //表示引用的是C函数代码

    extern int

          reduce(int x,int y);

    #pragma comment(lib,"TEST.lib")

    int main()

    {

        printf("%d\n",Add(2,3));

        printf("%d\n",reduce(3,2));

        return 0;

    }

    在这个C++源文件里引用C函数代码同样要加个”C”,但是在C源文件引用C++函数代码不能加”C++”,编译会报错,只能在C++文件函数定义中加”C”.

    只有C++才支持这种引用方式,也许因为只有C++兼容C而没有C兼容C++这一原则。


    展开全文
  • 1.进入存放所有.cpp和.h文件的文件夹路径 2.输入 g++ -c alien.cpp events.cpp gas_stations.cpp main.cpp officer.cpp planets.cpp spaceship.cpp spaceships.cpp trading_stations.cpp -std=c++11 3.输入 g++ ...

    g++编译多文件

    1.进入存放所有.cpp和.h文件的文件夹路径

    2.输入 g++ -c alien.cpp events.cpp gas_stations.cpp main.cpp officer.cpp planets.cpp spaceship.cpp spaceships.cpp trading_stations.cpp -std=c++11

    3.输入 g++ alien.o events.o gas_stations.o officer.o planets.o spaceship.o spaceships.o trading_stations.o main.o -o JSE

    4.输入 JSE.exe n  (注:0 <= n <= 50)
     

    使用makefile文件编译

    JSE:main.o alien.o events.o gas_stations.o officer.o planets.o spaceship.o spaceships.o trading_stations.o
    	g++ -o JSE alien.o events.o gas_stations.o officer.o planets.o spaceship.o spaceships.o trading_stations.o main.o
    alien.o:alien.cpp alien.h
    	g++ -c alien.cpp -std=c++11
    events.o:events.cpp events.h
    	g++ -c events.cpp -std=c++11
    gas_stations.o:gas_stations.cpp gas_stations.h
    	g++ -c gas_stations.cpp -std=c++11
    officer.o:officer.cpp officer.h
    	g++ -c officer.cpp -std=c++11
    planets.o:planets.cpp planets.h
    	g++ -c planets.cpp -std=c++11
    spaceship.o:spaceship.cpp spaceship.h
    	g++ -c spaceship.cpp -std=c++11
    spaceships.o:spaceships.cpp spaceships.h
    	g++ -c spaceships.cpp -std=c++11
    trading_stations.o:trading_stations.cpp trading_stations.h
    	g++ -c trading_stations.cpp -std=c++11
    main.o:main.cpp spaceship.h events.h spaceships.h planets.h trading_stations.h gas_stations.h
    	g++ -c main.cpp -std=c++11
    
    clean:
    	del *.o

    win cmd/powershell 编译命令:mingw32-make.exe

    ubuntu  terminal 编译命令: make

    其它一些远程操作命令: gmake

     

    输入命令:make clean   则执行 del *.o

    展开全文
  • windows的动态库:xxx.dll2.3. linux的动态库:libxxx.so2.4. linux的静态库:xxx.a三. 自己编译库3.1 编译产生windows的静态库3.1.1代码准备:3.1.2 static_lib.cpp:3.1.3 static_lib.h:3.2 编译...
  • 应用程序在向进程多动态库方向发展。工程项目听着很高大上,其实和创建正常的单进程项目一样的简单。区别仅在于创建工程类型的选择和重复创建工程的次数不同。 在Qt下创建工程的步骤如下: ...
  • so 动态库文件和静态库文件区别

    千次阅读 2019-10-24 15:49:52
    so 动态库文件和静态库文件区别 在Linux操作系统中,普遍使用ELF格式作为可执行程序或者程序生成过程中的中间格式。ELF(Executable and Linking Format,可执行连接格式)。 ELF文件分类 ELF文件格式包括三种主要...
  • 先看两篇博文,作为基础知识。如果对C/C++编译链接过程都了解的话,可以跳过不看。 http://www.firedragonpzy.com.cn/index.php/archives/2556 ... 一、 编译不同目录下的多个文件 各个文件的布局如下:
  • 动态库*.so在linux下用c和c++编程时经常会碰到,最近在网站找了几篇文章介绍动态库的编译和链接,总算搞懂了这之前一直不太了解得东东,这里做笔记,也为其它正为动态库链接库而苦恼的兄弟们提供一点帮助。...
  • c++mfc头文件库文件动态库 目录(?)[+] .h头文件是编译时必须的,lib是链接时需要的,dll是运行时需要的。 附加依赖项的是.lib不是.dll,若生成了DLL,则肯定也生成 LIB文件。如果要完成源代码的...
  • 打包生成一静态库&动态库

    千次阅读 2018-04-07 16:13:57
    静态库和动态库之间不同之处就在于代码被载入的时刻不同。静态库的代码在编译过程中已经被载入可执行程序,因此体积比较大;动态库(共享库)的代码是在可执行程序运行时被载入内存的,在编译过程中仅简单的引用,...
  • 我是一认真的人,要么不写,要么我就把步骤、截图、代码,一 一 附上,方便大家参考学习。 1、为什么要生成DLL和lib?  就是为了给VS工程调用(比如C#调用、C++工程)。 2、C#为什么要调用DLL和lib? C++写的...
  • 静态生成及使用 calc.h #ifndef __MY_LIB_H__ #define __MY_LIB_H__ int add(int a, int b); #endif calc.c #include "calc.h" #include <stdio.h> int sum(int i, int j) { return (i+j); } ...
  • 动态链接库文件的扩展名一般是dll,也有可能是drv、sys和fon,它和可执行文件(exe)非常类似,区别在于DLL中虽然包含了可执行代码却不能单独执行,而应由Windows应用程序直接或间接调用。 动态链接是相对于静态...
  • ELF文件格式   在Linux下,可执行文件/动态库文件/目标文件(可重定向文件)都是同一种文件格式,我们把它称之为ELF文件格式。...  动态库文件 header table 都有,因为链接器在链接的时候需要...
  • 创建和使用动态库 使用def文件

    万次阅读 2017-08-01 08:53:34
    包含window.h文件,这是vs2013自动生成的,但是你在建立项目的时候,选择了空项目,所以就没有,如果没选择空项目就会有这文件,但是他会包含的其他的依赖文件,这里都去除了,我们是要生成一纯净的动态...
  • GDB如何从Coredump文件恢复动态库信息

    万次阅读 2014-04-08 14:29:11
    在Linux生成Coredump文件时程序并没有对动态链接库文件信息进行特殊处理,但GDB在载入Coredump文件时却能正确加载所有的动态链接包括程序运行中调用dlopen动态载入的so文件,其原理是什么呢?这里通过对GDB源码...
  • 近期正在做在Ubuntu环境下将C++程序封装成动态库文件和静态库文件,期间不知道掉了多少坑,在这里写下来以免忘记工作是将实现MTCNN的C++源代码封装成动态库文件和静态库文件,原先在github上找到了一依据opencv和...
  • 一、静态库和动态库1、静态库(.lib) 函数和数据被编译进一二进制文件(通常扩展名为.LIB)。在使用静态库的情况下,在编译链接可执行文件时,链接器从库中复制... 2、动态库(.lib文件和.dll文件) 在使用动态
  • 文章目录一、写在前面二、framework打包动态库2.1 有关framework的误区2.2 五种 Mach-O 类型2.2.1 Executable2.2.2 Bundle2.2.3 Relocatable Object File2.2.4 Dynamic Library2.2.5 Static Library2.3 对framework...
  • 要理解C文件与头文件有什么不同之处,首先需要弄明白编译器的工作过程,一般说来编译器会做以下几过程: 1.预处理阶段 2.词法与语法分析阶段 3.编译阶段,首先编译成纯汇编语句,再将之汇编成跟CPU相关的二进制码...
  • vs生成动态库及使用动态库

    千次阅读 2017-05-25 21:51:30
    动态库(.dll):动态库又称动态链接库英文为DLL,是Dynamic Link Library 的缩写形式,DLL是一个包含可由多个程序同时使用的代码和数据的库,DLL不是可执行文件。动态链接提供了一种方法,使进程可以调用不属于其可...
  • 上一章节我们学会了如何使用Android Studio开发JNI,但是上一章节针对的是单个cpp文件,我们做项目开发时肯定不止一cpp文件,假如这些cpp文件能够放在一单独的文件夹下面肯定会更方便我们的操作,但是这些该如何...
  • so可以共多个进程调用,不同进程调用同一个so文件,所使用so文件不同;so原文件不需要main函数;实例,1.通过mysqlTest.c中的函数mysql(),生成一个libmysql.so链接#include&lt;stdio.h&gt; #include&...
  • C++静态库与动态库的区别

    千次阅读 2019-08-31 16:11:52
    引用动态库编译成可执行文件(跟静态库方式一样): g++ TestDynamicLibrary.cpp -L../DynamicLibrary -ldynmath 然后运行:./a.out,发现竟然报错了!!! 可能大家会猜测,是因为动态库跟测试程序不是一...
  • iOS制作动态库

    千次阅读 2019-03-04 18:55:38
    动态库:以.tbd(之前叫.dylib) 和 .framework 为文件后缀名。 静态库与动态库的区别 静态库:链接时会被完整的复制到可执行文件中,被次使用就有份拷贝。 动态库:链接时不复制,程序运行时由系统动态加载到内存...
  • .c或.cpp文件生成可执行文件.exe分为两过程,即编译过程和链接过程。 编译是把文本形式源代码翻译为机器语言形式的目标文件的过程。 链接是把目标文件、操作系统的启动代码和用到的库文件进行组织,形成最终生成...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 147,122
精华内容 58,848
关键字:

动态库包含多个h文件处理