跳到主要内容

动态链接和动态链接

CRT 是什么?

CRT 构建是指在编译和链接 C 或 C++ 程序时涉及到的 C 运行时库(C Runtime Library,CRT)的配置方式。C 运行时库提供了基本的程序执行支持,包括输入输出操作、内存管理、字符串操作等基本功能。在开发 C 或 C++ 应用程序时,正确配置 CRT 是非常重要的,因为它影响到程序的运行时依赖、分发大小以及兼容性。

CRT 可以以两种主要方式构建和链接到你的应用程序:

动态链接(DLL)

  • 使用动态链接时,你的应用程序在运行时依赖外部的 CRT DLL。这意味着CRT作为动态链接库(如 msvcr120.dllucrtbase.dll)安装在用户系统上。
  • 动态链接的优点是减小了应用程序本身的大小,因为它共享了系统级的运行时库。此外,更新或修补 CRT DLL 可以不需要重新编译应用程序。
  • 动态链接的标志通常是 /MD(对于 Release 构建)和 /MDd(对于 Debug 构建)。

静态链接

  • 静态链接将 CRT 直接嵌入到应用程序的二进制中。这样做的话,你的程序就不再依赖于系统上的 CRT DLL。
  • 静态链接的优点是使应用程序成为自包含的,不依赖于系统上的运行时库,这在分发应用程序时简化了过程,因为用户不需要安装额外的运行时。
  • 静态链接的标志通常是 /MT(对于 Release 构建)和 /MTd(对于 Debug 构建)。

选择动态链接还是静态链接

选择动态链接还是静态链接主要基于应用程序的特定需求和分发策略。动态链接可能更适合频繁更新或修补的应用程序,以及那些对二进制大小敏感的应用程序。静态链接则适合那些需要简化部署的应用程序,或者当你想要避免潜在的运行时库版本冲突时。

在前面提到的 CMake 脚本中,通过替换 /MD/MDd/MT/MTd,实际上是从动态链接 CRT 切换到了静态链接 CRT 的构建配置,这有助于生成不依赖于外部 CRT DLL 的自包含程序。

静态链接的脚本

创建一个 CRTLinkage.cmake 文件, 内容如下:

这段代码是一段用于配置 CMake 项目的CMake脚本,主要用于Windows平台。

if (WIN32)
foreach(flag_var
CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE
CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO
CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
if(${flag_var} MATCHES "/MD")
string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}")
endif()
if(${flag_var} MATCHES "/MDd")
string(REGEX REPLACE "/MDd" "/MTd" ${flag_var} "${${flag_var}}")
endif()
endforeach(flag_var)

set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /NODEFAULTLIB:atlthunk.lib")
set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} /NODEFAULTLIB:libcmt.lib /NODEFAULTLIB:libcpmt.lib /NODEFAULTLIB:msvcrt.lib")
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /NODEFAULTLIB:libcmtd.lib /NODEFAULTLIB:libcpmtd.lib /NODEFAULTLIB:msvcrtd.lib")
message(STATUS "OCR_BUILD_CRT True")
endif ()

然后在 CMakeLists.txt 中引入这个文件:

# Windows Link CRT
if (BUILD_CRT STREQUAL "True")
include(${CMAKE_CURRENT_SOURCE_DIR}/CRTLinkage.cmake)
endif ()

上面那个脚本它执行了以下几个操作:

1、检查编译器标志

  • 首先,它检查项目是否在Windows (WIN32) 上编译。如果是,它将对一系列的编译器标志进行迭代,这包括C和C++的标志,以及它们在不同构建类型(如Debug、Release等)下的变体。
  • 对于每个标志变量,它检查是否包含/MD/MDd/MD/MDd是Microsoft Visual Studio编译器的标志,用于指定与多线程动态链接库(DLL)的C运行时库(CRT)一起使用。/MD 用于 Release 构建,而 /MDd 用于Debug构建。

2、替换编译器标志

  • 如果找到/MD,则将其替换为/MT。同样,如果找到/MDd,则将其替换为/MTd/MT/MTd标志指定编译器应该将CRT作为静态库(而不是DLL)链接。/MT用于Release构建,/MTd用于Debug构建。这种替换的目的是改变CRT的链接方式,可能是因为项目的特定依赖性或分发考虑。

3、设置链接器标志

  • 接着,脚本为可执行文件的链接器设置额外的标志。它添加/NODEFAULTLIB:atlthunk.lib到所有构建类型的链接器标志中,这告诉链接器忽略默认的atlthunk.lib库。这可能是为了解决特定的链接问题或冲突。
  • 对于Debug和Release构建,它还分别添加了一系列/NODEFAULTLIB选项,以忽略特定的运行时库。这是为了防止在使用静态CRT时出现的默认库与指定库之间的冲突。