静态库、动态库、静态链接、动态链接、系统运行库混合、MD MT默认库冲突问题

一、静态库项目

静态库lib:(注意和“静态运行库”区分)   就是.lib文件,一个.c或.cpp会编译成一个.obj,多个.obj可以组合成一个.lib库。lib=多个obj。静态库其实是二进制型式的代码源程序

1. 静态库的制作不用链接

静态库lib其实只是二进制型式的代码源程序,因此做lib时,不需要进行链接link,只是把c或cpp文件编译成.obj文件,再组合成一个.lib文件。

静态库项目不需要链接,在项目“配置属性”中,不会出现“链接器”选项,也不用选择其它的lib库。没有所谓的静态链接、动态链接。

lib库程序也会调用系统运行库。因为lib库只是源程序,是和我们的源程序整合,所以是看我们最终程序链接exe或dll时,是采用静态链接还是动态链接。

2. 静态库的项目

比如1.exe项目,调用静态库A.lib,而A.lib又需要调用b.lib、c.lib。

在A.lib项目中,编译时不需要文件b.lib、c.lib,只需要他们的头文件.h就可以了,这样可以生成A.lib文件。

在1.exe项目中,链接时需要文件A.lib、b.lib、c.lib,和A的头文件,才能成功。

所以做1.exe时比较麻烦,需要有所有关联的库。

3. cmake构建生成A.lib项目时,为什么需要指定b.lib、c.lib位置?

现在大型开源项目,可以根据子库的存在情况来选择某项功能是否可用,如果某个子库不存在,则就取消这个子库的功能。

所以你要先编译好b.lib、c.lib,这样用cmake构建生成A.lib项目时才会正常。

cmake是用来构建生成不同平台的A.lib项目的,不是编译。比如:

podofo开源代码→cmake构建生成win平台的VS项目podofo.sln→VS打开podofo.sln并编译生成podofo.lib

podofo开源代码→cmake构建生成linux平台的项目podofo→linux的gcc打开podofo并编译生成podofo.lib

podofo需要用到某个必须的库如zlib.lib,如果cmake时找不到zlib.lib就出错,无法继续。

podofo需要用到某个可选的库,如果cmake时找不到,则取消这个库功能,并给出警告而已。到时编译生成的podofo.lib就没有这个功能了。

二、动态库项目

动态库dll:(注意和“运态运行库”区分)   就是.dll文件,注意dll是一个可执行文件,所以必须要经过链接。动态库其实是可运行程序

1. 动态库的制作需要链接

动态库dll其实是可执行程序,因此做dll时,需要进行链接link,链接后有一个.lib文件和一个.dll文件。.lib文件是方便其它项目编程引用的,里面只是把调用指向给.dll文件,实际的运行程序在.dll文件里。

如果我们只拿到一个.dll文件,而没有相应的.lib文件、头文件,也是可以在我们的程序中直接加载这个.dll文件来用的!

2. 动态库的项目

比如1.exe项目,调用静态库A.dll,而A.dll又需要调用b.lib、c.lib。

在A.dll项目中,编译链接需要文件b.lib、c.lib(以及他们的头文件.h),才能成功生成A.dll!

在1.exe项目中,编译链接时只需要文件A.lib和A的头文件。  运行时,需要A.dll文件一起。

即A.dll是可运行程序,把它用到的库都整合了,后续项目引用时就很简单,所以做1.exe时很简单。

所以:

简单功能的库项目,如zlib,做成静态库lib就行,要做成dll也可以。

大型库项目,比如podofo库,做成动态库dll,会给后续exe程序引用带来很大的方便。

三、系统运行库

1. 运行库CRT:  是微软提供的基础库!即run-time库,run-time libraries,就是WINDOWS提供的run-time库(系统运行库),有分静态版本、动态版本。也叫做CRT

比如stdio.h、stdlib.h里的函数,像printf()等的代码,就在运行库中。  即运行库是最底层的库了。

2. 静态链接、动态链接: 是针对“链接link和微软的运行库” 来说的。

(1)静态运行库=运行库静态版=静态链接=静态版:    

微软提供的lib版的run-time库,比如LIBCMT.lib,库函数程序源代码都在LIBCMT.lib里,用户链接自已exe或dll时,会整合到其中。

采用运行库静态版,叫静态链接。我的exe中带有运行库代码,可单独运行。

用户程序 的静态链接:  我们做自己的exe,采用“运行库静态版”,比如LIBCMT.lib,把运行库代码整合到其中。

动态库   的静态链接:  我们做自己的dll,同理。

(2)动态运行库=运行库动态版=动态链接=动态版:    

微软提供的dll版的run-time库,比如msvcrt.dll+MSVCRT.LIB,实际运行代码都在msvcrt.dll里,而MSVCRT.lib里面只是指向调用msvcrt.dll的代码,只是方便用户链接自己的exe或dll用的。  这时,这个msvcrt.dll就可以做成系统组件,让多个用户程序共享共用。

采用运行库动态版,叫动态链接。我的exe运行时,需要msvcrt.dll。

用户程序 的动态链接:  我们做自己的exe,采用“运行库动态版”,比如msvcrt.dll+MSVCRT.LIB,把MSVCRT.LIB代码整合到其中,实际运行时是调用msvcrt.dll文件。

动态库   的动态链接:  我们做自己的dll,同理。

3. 程序运行时,缺少运行库的问题

程序运行时,需要的运行库版本和VC编译器有关,比如vc6编译的程序是链接到msvcrt.dll,vc2005是msvcr80.dll,vc2008是msvcr90.dll,vc2012是msvcr110.dll等。

新的windows系统会有旧版的运行库,而旧的windows系统就没有新版的运行库了,所以程序在别人电脑中可能会缺少运行库而不能运行,要安装相应的微软运行库包才行。

是否可以把比如msvcr80.dll等文件拷到自己exe同一目录,这样不安装微软运行库包就能运行?   微软给出的答案是必须安装运行库,拷贝的不行。应该是这样实际运行时,还是会提示缺文件。

四、做成动态库dll还是静态库lib?多个系统运行库运行的问题:

我们的一个项目,或者网上下的一个项目比如zlib,构建做成动态库dll、静态库lib的区别:

1.做成静态库zlib.lib,则只是编译出这个项目的obj,不含运行库,用户未来要动态链接或静态链接都可以。

2.做成动态库zlib.dll,dll是可执行的,故需要进行链接,必须含运行库。

2.1 含静态运行库,则zlib.dll内部会有当前运行库代码,可能会与用户未来exe文件用的运行库版本不一致。

2.2 含动态运行库,则zlib.dll需调用所需的运行库dll,可能会与用户未来exe文件用的运行库版本不一致。

  1. 全部子库都做成静态库lib,用户最终exe也做成静态版,则已经整合了系统运行库静态库,运行时不需要调用系统运行库动态库dll。

  1. 全部子库都做成静态库lib的话,然后用户最终exe做成动态版,则运行时需要调用系统运行库动态库dll。

  1. 只要有其中一个子库做成“动态运行库的”动态库dll的话,即使用户最终exe做成静态版内置运行库代码,运行时也需要调用运行库动态库dll,因为这个子库需要调用运行库动态库dll,这时同一个进程都会有2个系统运行库在运行。

  1. 全部的子库做成“动态运行库的”动态库dll,用户最终exe也做成动态版,大家都去调用系统动态运行库。--微软默认是这种方式,即大家都做动态版,并且最好大家调用的动态运行库版本要一样。

  1. 如果有一个子库做成“静态运行库的”动态库dll的话,内部会内置系统运行库代码。 如果用户最终exe做成静态版,exe内部又有一个运行库代码。   如果用户最终exe做成动态版,exe也要去调用系统运行库。    无论哪种方式,同一个进程都会有2个系统运行库在运行。  并且,如果每个dll子库都静态内置运行库代码,那最终这个程序将超大无比,没有必要!

总结一下:

子库项目

子库链接方式

主exe链接方式

推荐否

运行时

动态库dll

不调用系统运行库

无所谓

动态库dll

动(相同运行库版本)

1个系统动态运行库(1动)

动态库dll

动(另一运行库版本)

×

2个不同版本系统动态运行库在运行(2动)

动态库dll

×

2个系统运行库在运行(1静1动)

动态库dll

×

2个系统运行库在运行(1静1动)

动态库dll

×

2个系统运行库在运行(2个静)

静态库lib

不用链接

1个系统动态运行库(1动)

静态库lib

不用链接

1个系统静态运行库,不需要系统动态运行库(1静)

dll库用静态链接出来,就会出现问题。但是,dll就是共用的,一般也不会用静态链接来做。

dll库用动态链接出来,最好还需要考虑各个dll、exe用的运行库版本一致,最好要用同一个VC版本进行编译。

2. 二个系统运行库在运行的缺点

一个1.exe进程中拥有多个runtime运行库副本,产生运行库混合,可能会导致问题。比如运行时,1.dll运行一个系统运行库,1.exe又调用另一个系统运行库,二者的heap各自独立。这样1.dll中如果有malloc,返回一个内存空间的指针给1.exe,当1.exe用完后,不能进行free,因为free是调用它的“系统运行库”的free了,这样会出错,  只能再让1.dll进行free, 但这样用起来就不方便了,编程时必须要小心。

一般来说,运行多个运行库一般也没关系的,运行没问题不冲突就行。

zlib DLL_FAQ里有一个说明:

10. Why are you saying that ZLIB1.DLL and my application must be linked to the same C run-time (CRT) library? I linked my application and my DLLs to different C libraries (e.g. my application to a static library, and my DLLs to MSVCRT.DLL), and everything works fine.

- If a user library invokes only pure Win32 API (accessible via <windows.h> and the related headers), its DLL build will work in any context.    But if this library invokes standard C API, things get more complicated.

There is a single Win32 library in a Win32 system.  Every function in this library resides in a single DLL module, that is safe to call from anywhere.   On the other hand, there are multiple versions of the C library, and each of them has its own separate internal state.   Standalone executables and user DLLs that call standard C functions must link to a C run-time (CRT) library, be it static or shared (DLL).    Intermixing occurs when an executable (not necessarily standalone) and a DLL are linked to different CRTs, and both are running in the same process.

Intermixing multiple CRTs is possible, as long as their internal states are kept intact. The Microsoft Knowledge Base articles KB94248 "HOWTO: Use the C Run-Time" and KB140584 "HOWTO: Link with the Correct C Run-Time (CRT) Library" mention the potential problems raised by intermixing.

If intermixing works for you, it's because your application and DLLs are avoiding the corruption of each of the CRTs' internal states, maybe by careful design, or maybe by fortune.

Also note that linking ZLIB1.DLL to non-Microsoft CRTs, such as those provided by Borland, raises similar problems.

10.  为什么要说zlib1.dll和我的应用程序必须链接到同一个C运行时(CRT)库?我将我的应用程序和dll链接到不同的c库(例如,我的应用程序链接到静态库,dll链接到msvcrt.dll),一切正常。

-如果用户的库只调用了纯win32 api(通过<windows.h>和相关的头文件),则构建出的dll将在任何上下文中工作。但是,如果这个库调用了标准的C API,事情会变得更加复杂。

在win32系统中只有一个win32库。  这个库中的每个函数都驻留在一个单独的dll模块中,可以安全地从任何地方调用。  而C库有多个版本,每个版本都有自己独立的内部状态。  调用标准c函数的 独立可执行文件和用户dll 都会链接到c运行库(crt),无论它是静态的还是共享的(dll)。    当一个可执行文件(不需要是独立的)和一个dll链接到不同的crt,两者都在同一进程中运行时,就会发生混合。

只要这些CRT的内部状态保持不变,就可以将其混合。  Microsoft知识库文章KB94248“如何:使用C运行时”和KB140584“如何:链接到正确的C运行库(CRT)”提到了混合带来的潜在问题。

如果混合后能正常运行,那是因为您的应用程序和dll们避免了这些CRT的内部状态被破坏,可能是程序经过了精心设计,也可能是运气好。

还要注意,将zlib1.dll链接到非Microsoft CRT(如Borland提供的CRT)也会引发类似的问题。

3. dll有问题,lib也有问题

动态库dll是可执行的,自己可以独立运行,自已会调用系统运行库,它呈现给exe项目的,就是它的接口函数,而不需要exe项目提供什么支持,一般不会干扰到exe项目。(除了建议运行库版本一致)

静态库lib是要整合到exe项目的,它需要exe项目的支持!比如lib要调用什么系统运行库,exe项目在链接时要提供给它。  这就有问题了,如果不同的lib需要的支持不一样,那就会出问题! 所以没这么简单!如下述。

五、更深入的问题:静态库lib的“默认库冲突”的问题

静态库lib项目,会指定未来链接的时候,是默认采用静态链接还是动态链接!!

静态库lib项目,在编译时,会把运行库“名称”先放入.obj文件中的!!--注意,只是名称

项目中,配置属性\C/C++\代码生成\运行库,有/MT,/MD的选项!!

/MT是到时链接时默认采用静态链接

/MD是到时链接时默认采用动态链接

《VS2003_CPP_zh-cn.pdf》说:

/MT、/MD 是编译器cl.exe的命令选项!(不是链接器link.exe的!)  它是在编译时就要我们去控制的。

/MT  (multithread) 从标准头(.h)文件中选择运行库例程的多线程特定版本。此选项还让编译器将库名称LIBCMT.lib放入.obj文件中,这样链接器到时会使用LIBCMT.lib来解析外部符号。

multithread-specific versions of the run-time routines are selected from the standard header (.h) files.   This option also causes the compiler to place the library name LIBCMT.lib into the .obj file so that the linker will use LIBCMT.lib to resolve external symbols.

/MD  (multithread & DLL)  从标准头 .h 文件中选择运行库例程的多线程和DLL特定版本。此选项还让编译器将库名称MSVCRT.lib  放入 .obj  文件中。用此选项编译的应用程序静态链接到 MSVCRT.lib。该库提供的代码允许链接器解析外部引用。实际工作代码包含在MSVCR71.DLL中,MSVCR71.DLL必须在应用程序运行时可用。

both multithread- and DLL-specific versions of the run-time routines are selected from the standard .h files.   This option also causes the compiler to place the library name MSVCRT.lib into the .obj file.   Applications compiled with this option are statically linked to MSVCRT.lib.    This library provides a layer of code that allows the linker to resolve external references.    The actual working code is contained in MSVCR71.DLL, which must be available at run time to applications linked with MSVCRT.lib.

我发现,按/MT和/MD分别生成的.lib文件,大小不一样!是不同的文件。

/MT 生成的.lib文件,文件更大,内部有出现“/DEFAULTLIB:"LIBCMT"”字串,没有出现“MSVCRT”字串!

/MD 生成的.lib文件,文件更小,内部有出现“/DEFAULTLIB:"MSVCRT"”字串,没有出现“LIBCMT”字串!

也就是说,我做我的lib时,如果用/MT,则会把“/DEFAULTLIB:"LIBCMT"” 放入我的lib中。    如果用/MD,则会把“/DEFAULTLIB:"MSVCRT"”放入我的lib中。   然后我后续exe链接时,会采用相应的运行库lib对我的lib进行链接。

exe项目

lib项目

结果

/MT

/MT

静态链接

/MD

/MT

这样会出问题了?

/MT

/MD

/MD

/MD

动态链接

有人说,“传递给链接器的 给定调用的所有模块 都必须使用相同的运行库编译器选项(/MD、/MT、/LD)进行编译。”

lib库编译时采用/mt ,则exe对自己c文件进行编译也要采用/mt,大家都用/mt。

lib库编译时采用/md ,则exe对自己c文件进行编译也要采用/md,大家都用/md。

实际发现,一个podofo库项目中,zlib.lib是/MD(/DEFAULTLIB:"MSVCRT"),freetype.lib是/MT(/DEFAULTLIB:"LIBCMT"),podofo.lib是/MD或/MT,做exe时均没有问题,正常。

后来发现,链接时有警告“warning LNK4098: 默认库“library”与其他库的使用冲突;请使用 /NODEFAULTLIB:library”。   也就是说,/MD、/MT开关不一样时,在最终exe链接时,可能只是警告一下,仍然会成功。但也可能会出错不成功。

如果我lib库中没有用到运行库,则无所谓了。

六、实例尝试lib“默认库冲突”的问题

我新建一个lib库项目,只有这个语句:

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

则生成的.lib文件里,没有“MSVCRT”字串、没有“LIBCMT”字串、没有“/FAILIFMISMATCH:"RuntimeLibrary=”的字串。

用语句:

#include <iostream>  及std::cout << "hello";

则也是没有“MSVCRT”、“LIBCMT”字串。但是,有“/FAILIFMISMATCH:"RuntimeLibrary=”的字串! 这样的话,未来exe项目也只能用同样的/MD或/MT开关。

在建立LIB库时,无论选不选“预编译头”,都是这样。然后,选打勾MFC,则生成的.lib里有“msvcrt.lib”字串。

汇总如下:

debug,/MD

release,/MD

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

有“MSVCRT”字串

都没有

有“预编译头”

有“MSVCRT”字串

都没有

勾选MFC

有“MSVCRT”字串

有“msvcrt.lib”字串

加上#include <iostream>  及std::cout << "hello";

有字串“/FAILIFMISMATCH:"_MSC_VER=1700"”、

“/FAILIFMISMATCH:

"RuntimeLibrary=MDd_DynamicDebug"”,

有“MSVCRT”字串

有字串“/FAILIFMISMATCH:"_MSC_VER=1700"”“/FAILIFMISMATCH:

"RuntimeLibrary=MD_DynamicRelease"

试了一下更改“C/C++的代码生成”各个属性值,都无法消掉“/FAILIFMISMATCH:"RuntimeLibrary=”这个字串。

看来没办法了。

<string.h> 的函数比如strcpy()不会触发“/FAILIFMISMATCH:"RuntimeLibrary=”字串。

所以,就是看我们程序中调用的运行库函数 有没有 触发“/FAILIFMISMATCH:"RuntimeLibrary=”字串!!

没有触发时,最多就是出现“warning LNK4098: /NODEFAULTLIB:library ”,链接会成功。

有触发时,链接器就会强制检查匹配,就必须要保证 “带有这个字串的obj模块”都是采用相同的/MD或/MT开关来生成的!(而且可能还需要VC版本一致)

不匹配时,就出现“error LNK2038: 检测到“RuntimeLibrary”的不匹配项”,链接失败。

这也是为什么从网上下载的库lib,有的随便用都没问题,有的却要重新用相同开关来编译才能用的原因。

使用的查找命令:

findstr /s /m "RuntimeLibrary" *.obj

findstr /s /m "MSVCRT" *.obj

findstr /s /m "LIBCMT" *.obj

findstr /s /m "msvcrt.lib" *.obj

七、DLL项目没有“默认库冲突”的问题

dll项目,会生成lib和dll文件。lib文件中,找不到“MSVCRT”字串、“LIBCMT”字串、“/FAILIFMISMATCH:"RuntimeLibrary=”的字串。就是说,dll是自己独立运行的,不会有“默认库冲突”的问题!

dll文件内部有运行库的版本号,所以就是会存在运行库版本不一致的问题(除了不需要运行库的dll)。

所以,如果没有源程序的话,dll比lib好用些,lib可能直接就不能用,dll好歹还能用。

当然,如果有源程序的话,就全部自己编译了,随便怎么用都行,运行库的版本号也都是一致的。一般可以子库全部做成静态库lib,全部用/MD,省得再生成dll文件,并且/MD可节省最终程序大小。最终整合的项目采用dll或exe,也用/MD开关

  • 3
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值