“lib静态库”和“导入lib库”这些词汇相信我们经常听说了吧。但是lib怎么来的,怎么使用的我们很多人还真不知道哦。我也是专门研究学习才发现的。所以在此详细讲述下,分享给大家。想详细了解完整的编译链接的过程,可以看看《C++零基础入门课程》的第三章,很不错的。
先感叹一下:怎么努力还是被抛弃了:悲剧的人生莫过Lib库了!!如果你不懂,仔细看完下面的介绍你就懂了。
lib库实际上分为两种,一种是静态链接lib库或者叫做静态lib库,另一种叫做动态链接库dll库的lib导入库或称为lib导入库。这两个库是不一样的,很多人都分不清楚,很容易混淆。下面我给你解释清楚,你就不会再糊涂了。
第一种是静态lib,包含了所有的代码实现的,是源代码文件.c或.cpp文件编译生成的,这个lib库就是文本形式的源代码编译后的二进制形式代码。
第二种就是lib导入库,这个库只是dll文件中的所有函数在dll文件中的地址的说明。
从两种库的说明可以看出,静态lib文件里是包含了所有的代码的,所以只要导入后,使用链接器链接生成exe文件后,那么exe文件就可以直接使用exe 内部的代码了。这个链接lib库的过程就相当于把lib库里的所有二进制的代码复制到exe文件中。所以,链接完后,静态lib库文件就不需要了。最后, 我们只要exe就行了。这个lib在链接完后,就已经失去价值了。因为exe文件中已经有了它的一份拷贝,已经不需要依靠它了。嘿嘿,exe文件似乎有点绝情哦,哈哈哈。但是,每次编译链接生成exe时都需要这个静态lib库哦。这意思是说,最后你给别人的只需要给一个exe就行了,就不必将lib也给别人。在写代码时,我们要调用lib库里的函数,我们是通过提供的头文件来知道lib静态库里都有些什么函数的。
这个lib导入库可以说明dll的内部结构,简直就对dll内部了如指掌,我们通过lib导入库,就可以轻松调用到dll里面的函数。而我们在程序中使用 dll的时候,我们导入lib导入库之后,然后将dll放入项目中,就可以直接使用,就好像dll里的代码就和我们写的cpp源代码文件一样。所以非常方便,这也是lib导入库存在的理由了。
而dll的使用,还可以直接使用API函数来获取dll内部的函数的地址,然后将函数类型转换为正确的函数类型。而这些函数类型声明就在提供的头文件中了。头文件和我们自己写的头文件一样的。这个过程呢,就非常麻烦了,这才有上面那个lib导入库的存在的空间了。
如果直接动态通过API函数来使用dll内部的函数,就不需要lib导入库了。也就是说,使用lib导入库库和通过API直接使用dll里的函数,是使用dll的两种方式。所以二选一即可。
同样,lib导入库在编译链接后,那么dll里的函数地址结构等信息也都复制到exe里面了。所以,最后生成程序之后,lib导入库和静态lib一样的命运,那就是被抛弃了。哎,现实已经很残酷了,在计算机里,更是残酷。
对于静态lib的生成和使用,以及dll和lib导入库的生成和使用,则在下一节介绍。
我们经常要用到别人的静态lib库,那他们是怎么来的呢?要清楚的掌握如何生成静态lib库的,我们不妨自己来写一个简单的静态库,然后自己使用。生成后,你将源文件中的头文件和这个lib文件放在其他项目里就可以使用了。如果你最终给别人,请使用Release版本生成后给别人哦。
在前一节,已经解释了这几个概念之间的区别和关系。你也可以参考《C++语言零基础入门教程:3.3 链接代码是什么,为什么需要链接,如何链接代码》获得更加丰富的技术介绍。
那么我们经常要用到别人的静态lib库,那他们是怎么来的呢?要清楚的掌握如何生成静态lib库的,我们不妨自己来写一个简单的静态库,然后自己使用。
我们还是创建一个控制台程序项目。 创建控制台项目,所有的VS都是一样的。那么,我们添加一个头文件qtwidgetsapplication2.h。在VS中,就是右击“解决方案资源管理器”下项目的“头文件”,然后添加 “新建项”,选择头文件,命名为test.h即可。然后在“源文件”右击,选择“新建项”,然后选择源文件,然后命名为qtwidgetsapplication2.cpp。当然这个名字自己随便取。然后在qtwidgetsapplication2.cpp文件开头包含以下头文件,因为源文件中函数的声明都会放在头文件中。而且,因为是创建lib静态库,最后的函数声明需要放在头文件中供别人使用的。别人拿到静态lib库时一定有一个对应的头文件。所以,函数声明和函数定义分别分到头文件和源文件中。在源(.cpp)文件中包含头文件的代码如下:
#include "qtwidgetsapplication2.h"
如果你自己定义名字,那么这个包含的头文件,一定要是你定义的头文件。名字无关紧要,不过在包含头文件才是将头文件和源文件关联起来的时候。头文件和源文件名称也可以不一样。这些都无所谓的。
我们在头文件写一个函数的声明如下:
void show(char* szMsg);
这一句代码就表示了声明,因为这个函数会涉及到标准输出,所以我们在头文件里包含一个标准输入输出头文件。只要在头文件中包含了,源文件也就可以使用了。当然,你也可以在源文件中包含标准输入输出头文件。随你了,反正如果在头文件中也要用的上的,就直接放在头文件中包含,这样就省事些。
好了,头文件就这么多,头文件qtwidgetsapplication2.h的代码如下:
#include
void show(char* szMsg);
在源文件qtwidgetsapplication2.cpp中,我们写show函数的定义,源文件qtwidgetsapplication2.cpp的代码如下:
#include "test.h"
void show(char* szMsg)
{
printf(szMsg);
}
就这么多,代码写完了。不对呀,这个怎么和C程序一样呢??是的,好戏才刚刚开始。问题不在于代码怎么写,代码和正常一样的写,不要以为生成静态lib库有什么了不起的哦。
那么生成静态lib库,关键点在于生成的目标文件的格式了。我们要在项目属性里设置下。
单击VS的主菜单“项目”,然后单击“XXX属性(P)...”,在弹出的属性页中,我们单击左边的“配置属性”下的“常规”,然后在右边可以看到一系列可以设置的值,如下图所示:
从图中可以看到,点击“项目默认值”的“配置类型”右边的下拉框,然后可以看到有好几个选项:生成文件、应用程序(.exe)(默认的)、动态库(.dll)、静态库(.lib)和实用工具。那么此时我们选择“静态库(lib)”,确定即可。
好了,就这样,我们再来点击VS菜单的“重新生成解决方案”,然后看底下的“输出”窗口,看到如下提示:
提示说全部重新生成:成功1个,失败0个,跳过0个。表示没有任何错误。上面提示D:\documents\qtstudy\QtWidgetsApplication2\x64\Debug\QtWidgetsApplication2.lib,你看到了lib文件,你说代表什么,这说明生成lib文件成功了。如果失败,是看不到这个的。
然后到项目文件夹看看lib文件。我们直接在“解决方法资源管理器”最顶部“解决方案XXX(1个项目)”项右击,单击“在文件资源管理器中打开文件夹(X)”,这样就快速打开了项目文件夹哦。
如果你的VS界面上的工具栏显示的是Debug,那么就是调试模式,那么生成的lib文件在Debug文件夹下。如果是Release模式,则生成的在Release文件夹下。我的工具栏这个位置设置的是Debug,如下图所示:
这个可以自由选择,如果在编码阶段,最好是Debug,方便调试,如果最终发布,就调成发布版的Release版。
打开文件夹后,看到是这样的:
去“Debug”文件夹看看,是不是真的出现了lib文件呢?看下图:
是不是挺简单的。自己也动手试试吧。不过最后要交待几句。
因为是静态库,所以你这个库是生成给别人用的,或者是在其他程序使用的,那么就不需要你写main函数了。实际上,这个库就是一个工具库,只需要一些函数就行了。在头文件中有函数声明,在代码中有函数的定义,就可以了。不要在自己的静态库中写main函数哦,不然别人在使用你的静态库可能会出问题的,因为会有多个main函数。一般是没有人这么做的,或者说熟手就不会了,但是新手有时候不清楚就真的带上了。
那么最后你将源文件中的头文件(.h)和这个lib文件放在其他项目里就可以使用了。如果你最终给别人,请使用Release版本生成后给别人哦。
另外在项目属性中可选.dll和.lib和.exe,根据自己的需求进行生成
如果写过MFC程序,程序给别人运行的时候,总是提示什么MFC的dll不存在,这个是一个很麻烦的问题。我们要静态链接,其实就是要引入静态lib库,这样的话,最终链接后,就打包到exe了。引入静态lib就是这里的关键哦。其他的都和普通的程序一 样。添加头文件和包含头文件,没有其他不一样的地方。不一样的就是没有提供函数的定义。那么静态lib的函数定义就在这个文件中,所以我们引入后,就补全了函数定义了。而这个引入静态lib库,最终也完成了将代码打包到了exe。
在第二节中,我们学会了如何创建自己的静态lib库了。通过自己的实践,我们就很清楚人家的静态lib库是如何来的了。那么我们接下来的任务就是使用静态lib库了。
先来说说我们经常疑问的问题就是如何静态链接一个第三方库。那么这个问题,是因为对此一无所知导致的。只要看了这一系列的文章,这都不是问题。因为之前我也这么疑问过。
那么我们为什么要静态链接呢?我们写过MFC程序,程序给别人运行的时候,总是提示什么MFC的dll不存在,这个是一个很麻烦的问题。我们也懒得去一个个找到这个dll放在exe文件一起,这样发给别人真心麻烦。还有,我们总不喜欢一大堆东西去找一个exe运行,很多人小白哦,根本就不知 道去双击exe运行。那你只给exe的时候,他看上去还有选择吗?那肯定是直接双击exe。对于小白这个,都不是关键。
MFC程序项目默认的是共享DLL方式,也就是动态链接的。动态链接意味着链接的时候并没有把MFC库打包到exe中,所以,当运行程序的电脑没有MFC库的时候,就缺少了MFC库。
对于程序文件大小不限制的情况下,尽可能使用静态链接。这样分享软件时候,就方便了。只不过就是静态链接的程序会大一点点而已。
然而,我们很多时候写的并不是MFC程序,那么此时要静态链接,如何做?比如我们在使用opencv时,如果使用的是静态链接,那就不用带上一堆dll 了。那些dll那么多看上去都恐怖。然而,我们知道要静态链接,但是很多人也不知道如何静态链接,甚至连需要拿什么来链接都不清楚。是IDE设置还是需要准备特定的文件呢?
有时候我们只得到了dll文件和头文件还有与dll对应的lib导入库文件,这个时候是没有办法静态链接的哦。这个关键在于需要有静态lib,这个静态 lib库包含了所有的二进制代码,才能链接。lib导入库是没有代码的,只是对dll的说明。所以,第一步如果你不清楚,一直在找如何用dll静态链接, 一辈子都搞不定这个问题。
如果是开源库或第三方,一般是提供两种方式的,或者你要静态链接,就下载静态lib版本。有了东西才好办事。
好了,我们下面来看看如何静态链接第三方库,或者说是如何使用静态lib库吧。
你先要有一个建立好的项目,然后,将你得到的lib文件和头文件放在你项目的源文件(.cpp)文件夹下,也就是和.c或.cpp文件放在一起。
好了,文件准备好后, 我们在VS中的“解决方案资源管理器”的“头文件”项右击,添加“现有项”,然后添加刚才得到的第三方库的头文件。
我们这里就添加生成的静态lib库的头文件。然后在我们自己的源文件main.cpp中写好main函数,在开头包含qtwidgetsapplication2.h,然后在main调用qtwidgetsapplication2头文件声明的函数show。我们使用静态lib库的项目是生成exe哦。如下图所示:
然后我们直接按F5调试运行,这样会自动编译链接运行。然后发现了问题,提示:
1>m.obj : error LNK2001: 无法解析的外部符号 "void __cdecl show(char *)" (?show@@YAXPAD@Z)
1>C:\Users\wdx\documents\visual studio 2015\Projects\Console_1\Release\Console_1.exe : fatal error LNK1120: 1 个无法解析的外部命令
这个“无法解析的外部命令”和“fatal error LNK1120”想必是大家经常碰见的吧。这里提示的是show函数无法解析。虽然后函数声明,但是却找不到函数定义在哪里。看到这个错误,你应该知道你 的函数没有写定义。因为这个是静态lib库的,所以,你就应该想到,这个是没有引入静态lib库的原因哦。
是的,我们要静态链接,其实就是要引入静态lib库,这样的话,最终链接后,就打包到exe了。引入静态lib就是这里的关键哦。其他的都和普通的程序一 样。添加头文件和包含头文件,没有其他不一样的地方。不一样的就是没有提供函数的定义。那么静态lib的函数定义就在这个文件中,所以我们引入后,就补全 了函数定义了。而这个引入静态lib库,最终也完成了将代码打包到了exe。
好了。我们先用VS的项目设置来引入。依次点击下面的菜单:“项目” -> “XXX项目”,弹出项目属性页,然后找到“配置属性”-> "链接器" -> “输入” -> “附加依赖项”,如下图所示:
从图中可以看到,这里面已经有了一些静态lib库了。然后单击一下“附加依赖项”选中后就出现了下拉箭头,再点击右边的下拉箭头,然后点击编辑,如下图所示:
然后弹出编辑框,输入我们放在项目源码文件夹的lib文件名,如下图所示:
然后确定即可。我们再按VS的菜单“生成”->“重新生成解决方方案”,成功!如下图所示:
这里要一下上一篇提到的,如果在生成lib的项目中写了main函数,情况会如何?我们实验了一下,然后将生成的lib引入重新生成解决方案,出现了如下的错误:
1>m.obj : error LNK2005: _main 已经在 Console_1.lib(test.obj) 中定义
1>C:\Users\wdx\documents\visual studio 2015\Projects\Console_1\Release\Console_1.exe : fatal error LNK1169: 找到一个或多个多重定义的符号
错误提示_main已经定义,“找到一个或多个多重定义的符号”,说明我们现在的项目中的main和静态lib库中的冲突了。这也证明了上一节中说的,不能在静态lib库中写main函数咯。
参考:https://mp.weixin.qq.com/s/VcicslMbWtuJ6YUJfBPxiw