这个话题要从哪里说起呢。博主小白一个,最近调试程序时(WIN-MSVC)连一些基本问题都没弄懂,本着遇见问题一定要解决的原则,下面来说下MTD/MT/MDD/MD以及LIB/DLL之间的一些联系和问题:
一 动态库DLL 静态库LIB
MSVC中工程只有三种类型,即LIB库,又叫做静态库;DLL库又叫做动态库;EXE,可执行程序;其中EXE调用LIB库或者DLL库,三者关系如上。
DLL库定义
动态链接库英文为DLL,是Dynamic Link Library 的缩写形式,DLL是一个包含可由多个程序同时使用的代码和数据的库,DLL不是可执行文件。动态链接提供了一种方法,使进程可以调用不属于其可执行代码的函数。函数的可执行代码位于一个 DLL 中,该 DLL 包含一个或多个已被编译、链接并与使用它们的进程分开存储的函数。DLL 还有助于共享数据和资源。多个应用程序可同时访问内存中单个DLL 副本的内容。
一般生成动态库后会产生两个文件即DLL文件和对应DLL文件的LIB文件,其中DLL文件存储了具体的程序,而LIB文件只是这些程序的一个目录或者说是索引。
当调用DLL文件时,需要在LINKER选项加载对应DLL的LIB文件。
LIB库定义
静态链接库就是.lib文件,库中的代码最后需要连接到可执行文件中去,所以静态连接的可执行文件一般比较大一些。
静态链接库不同于动态链接库(*.dll),在静态库情况下,函数和数据被编译进一个二进制文件(通常扩展名为*.LIB),Visual C++的编译器在链接过程中将从静态库中恢复这些函数和数据并把他们和应用程序中的其他模块组合在一起生成可执行文件。这个过程称为"静态链接",此时因为应用程序所需的全部内容都是从库中复制了出来,所以静态库本身并不需要与可执行文件一起发行。
因此这也导致了使用静态库程序偏大的原因:
程序编译一般需经预处理、编译、汇编和链接几个步骤。在我们的应用中,有一些公共代码是需要反复使用,就把这些代码编译为“库”文件;在链接步骤中,连接器将从库文件取得所需的代码,复制到生成的可执行文件中。这种库称为静态库,其特点是可执行文件中包含了库代码的一份完整拷贝;缺点就是被多次使用就会有多份冗余拷贝。
静态库和动态库是两种共享程序代码的方式,它们的区别是:静态库在程序的链接阶段被复制到了程序中,和程序运行的时候没有关系;动态库在链接阶段没有被复制到程序中,而是程序在运行时由系统动态加载到内存中供程序调用。使用动态库的优点是系统只需载入一次动态库,不同的程序可以得到内存中相同的动态库的复本,因此节省了很多内存。
这就是为什么当我们将工程调整为静态库LIB时,LINKER选项就消失了,因为静态库不需要额外的链接其他库,一次性全部编译到程序中了
二 MTD/MT 与 MDD/MD
静态链接多线程库(MT/MTD)
静态链接的多线程库的目标代码也最终被编译在应用程序的二进制文件中,但是它可以在多线程程序中使用。通过 /MT 编译选项可以设置 Visual C++ 使用静态链接的多线程库。
MTMultiThread(static link) MT 静态链接多线程库
定义 _MT,以便从标准头 (.h) 文件中选择运行时例程的多线程特定版本。
此选项还使编译器将库名 LIBCMT.lib 放入 .obj 文件中,以便链接器使用 LIBCMT.lib 解析
外部符号。
Debug multiThread(static link) MTd Debug版静态链接多线程库
定义 _DEBUG 和 _MT。定义 _MT 会导致从标准 .h 文件中选择运行时例程的多线程特定版本。此选项还使编译器将库名 LIBCMTD.lib 放入 .obj 文件中,以便链接器使
用 LIBCMTD.lib 解析外部符号。
动态链接多线程库(MD/MDD)
动态链接的运行时库将所有的 C 库函数保存在一个单独的动态链接库 MSVCRTxx.DLL 中, MSVCRTxx.DLL 处理了多线程问题。使用 /MD 编译选项可以设置 Visual C++ 使用动态
链接的运行时库。
MultiThread(dynamic link) MD
定义 _MT 和 _DLL 以便同时从标准 .h 文件中选择运行时例程的多线程特定版本和 DLL 特定版本。此选项还使编译器将库名 MSVCRT.lib 放入 .obj 文件中。
用此选项编译的应用程序静态链接到 MSVCRT.lib。该库提供允许链接器解析外部引用的代码层。实际工作代码包含在 MSVCR71.DLL 中,该库必须在运行时对于与 MSVCRT.lib 链接的应用程序可用。
Debug multiThread(dynamic link) MDD
定义 _DEBUG、_MT 和 _DLL,以便从标准 .h 文件中选择运行时例程的调试多线程特定版本和 DLL 特定版本。它还使编译器将库名 MSVCRTD.lib 放入 .obj 文件中。
为什么DLL会有其相对应的动态Lib?
将 /DLL 选项传递到链接器。链接器查找 DllMain 函数,但并不需要该函数。如果没有编写 DllMain 函数,链接器将插入返回 TRUE 的 DllMain 函数。链接 DLL 启动代码。
如果命令行上未指定导出 (.exp) 文件,则创建导入库 (.lib);将导入库链接到调用您的 DLL 的应用程序。
这里还要补充说明一下,有的DLL库时不会生成Lib文件的,因为DLL没有被“导出”,导出DLL库中的函数有两种方式:
一种是编写define文件(.def),如:
; DEF file for MS VC++LIBRARYEXPORTS XML_DefaultCurrent @1 XML_ErrorString @2 XML_ExpatVersion @3 XML_ExpatVersionInfo @4 XML_ExternalEntityParserCreate @5 XML_GetBase @6 XML_GetBuffer @7 XML_GetCurrentByteCount @8 XML_GetCurrentByteIndex @9 XML_GetCurrentColumnNumber @10 XML_GetCurrentLineNumber @11 XML_GetErrorCode @12 XML_GetIdAttributeIndex @13 XML_GetInputContext @14 XML_GetSpecifiedAttributeCount @15 XML_Parse @16 XML_ParseBuffer @17 XML_ParserCreate @18 XML_ParserCreateNS @19 XML_ParserCreate_MM @20 XML_ParserFree @21 XML_SetAttlistDeclHandler @22 XML_SetBase @23 XML_SetCdataSectionHandler @24 XML_SetCharacterDataHandler @25 XML_SetCommentHandler @26 XML_SetDefaultHandler @27 XML_SetDefaultHandlerExpand @28 XML_SetDoctypeDeclHandler @29 XML_SetElementDeclHandler @30 XML_SetElementHandler @31 XML_SetEncoding @32 XML_SetEndCdataSectionHandler @33 XML_SetEndDoctypeDeclHandler @34 XML_SetEndElementHandler @35 XML_SetEndNamespaceDeclHandler @36 XML_SetEntityDeclHandler @37 XML_SetExternalEntityRefHandler @38 XML_SetExternalEntityRefHandlerArg @39 XML_SetNamespaceDeclHandler @40 XML_SetNotStandaloneHandler @41 XML_SetNotationDeclHandler @42 XML_SetParamEntityParsing @43 XML_SetProcessingInstructionHandler @44 XML_SetReturnNSTriplet @45 XML_SetStartCdataSectionHandler @46 XML_SetStartDoctypeDeclHandler @47 XML_SetStartElementHandler @48 XML_SetStartNamespaceDeclHandler @49 XML_SetUnknownEncodingHandler @50 XML_SetUnparsedEntityDeclHandler @51 XML_SetUserData @52 XML_SetXmlDeclHandler @53 XML_UseParserAsHandlerArg @54; added with version 1.95.3 XML_ParserReset @55 XML_SetSkippedEntityHandler @56; added with version 1.95.5 XML_GetFeatureList @57 XML_UseForeignDTD @58; added with version 1.95.6 XML_FreeContentModel @59 XML_MemMalloc @60 XML_MemRealloc @61 XML_MemFree @62; added with version 1.95.8 XML_StopParser @63 XML_ResumeParser @64 XML_GetParsingStatus @65
另一种是在函数头加上 _declspec(dllexport)如:
#define DLL1_API _declspec(dllexport)DLL1_API int Add(int a,int b){ return a+b;}
如果你想查看你的DLL的导出情况可以这样做,在你的VC安装目录下的VC98\BIN目录下有一个dumpbin.exe文件,它就是用来查看DLL文件信息的,你可以在命令行下(CMD)用dumpbin -exports dllname 命令来查看DLL的导出函数列表。
PS:
DLL有其对应的LIB
静态LIB只有其自己
三 MD/MDD/MT/MTT 与DLL/LIB的关系
当使用动态库DLL时,在MSVC中的Code Generation选项选择MD/MDD(依Release或Debug而定),此时系统函数会调用MSVCRT.lib与MSVCR71.DLL
当使用静态库LIB时,在MSVC中的Code Genration选项选择MT/MTD(依Release或Debug而定),此时系统函数会调用LIBCMT.lib
四 警告
不要混合使用库的静态版本和动态版本。在一个进程中有多个库副本会导致问题,因为副本中的静态数据不与其他副本共享。链接器禁止在 .exe 文件内部既使用静态版本又使用动态版本链接,但您仍可以使用运行时库的两个(或更多)副本。例如,当与用动态 (DLL) 版本的运行时库链接的 .exe 文件一起使用时,用静态(非 DLL)版本的运行时库链接的动态链接库可能导致问题。(还应该避免在一个进程中混合使用这些库的调试版本和非调试版本)。
这句话的意思是exe工程尽量保持要么都引用动态库,要么都静态库,不要又引用动态又引用静态,会容易引起冲突
还有就是别动态库中引用了一个静态库,然后该动态库又被exe引用了,这些都容易造成冲突