调试和发布配置 (c++) 中内置的共享库之间的用法有何区别?
What's the difference in usage between shared libraries built in debug and release configuration (c++)?
我在使用共享库(Win 10中的.dll)时遇到了问题。
我在两种不同的配置中构建了一个名为xlib的lib,并试图通过CMake在名为xlibtest的项目中测试使用它们。
工作流程是:
步骤A.构建xlib=>xlib.dll+xlib.lib(仅符号)+xlib.pdb[可选]
步骤B.构建xlibtest并链接xlib.lib=>xlibtest.exe
如果步骤A和步骤B中的构建配置不同,那么当我运行xlibtest.exe时,就会出现问题。例如,我有一个功能
//xlib.h
void foo(std::string input);
在xlib中,当我调用这个函数时,它会崩溃。
//xlibtest.cpp
int main() {
xlib::foo("test"); // in debug mode I found the string pass to foo is wrong
}
我搜索了一下,找到了关于这个问题的一些解释。这可能是因为调试和发布配置的内存模式不同。建议不要混合这两种配置,即在调试配置中使用调试库,在发布配置中使用发布库。
那么问题来了。我可以建立两个版本的我自己的图书馆。但是,我能为那些外部库做些什么呢?
我在项目中使用了intel MKL库,并在调试和发布配置中链接了相同的库。看起来效果不错。
但当我使用Boost库时,调试版本和发布版本之间可能存在差异,因为我们可以在安装Boost库的时候决定构建的版本。
第页。S.我之所以不链接静态库,是因为存在编译时的错误,并且需要相同的配置(即调试/发布)。
-更新--
我正在使用:
CMake 3.5.2;
编译器:适用于英特尔64 VS2015环境的英特尔icl 16.0更新3;
IDE:VS 2015社区;
操作系统:Windows 10,64位。
CPU:Intel i7 4930K
很抱歉没有提供有关工具链的这些信息。我曾经认为这是一个与我使用的工具链无关的一般问题@Hans Passant
我想@John D的回答可能会结束我的困惑。在我了解到混合调试和发布配置存在问题后,我不明白为什么使用"英特尔MKL库"没有区别,这就是我提出这个问题的原因。"英特尔MKL库"是一个没有std::字符串的C库,这是原因吗?
在调试/发布中编译-对代码的影响
从技术上讲,库的"调试"answers"发布"构建是使用不同的编译器开关和预处理器宏构建的。它与编译具有可选功能的库没有本质区别。
- 编译器开关通常不会影响代码在高级下的工作方式。在低级别上,相关规范没有指定行为(例如,按照独立操作的顺序、内存映射、变量的物理位置、编译器生成的指令插入代码),但如果你依赖这些,那你就错了
- 另一方面,预处理器宏可以以任何方式更改代码行为通常,
DEBUG
宏启用具有各种运行时检查和诊断功能的代码块。后者可能包括向数据类型添加成员
因此,您构建的C++代码的调试和发布版本在一般情况下是二进制不兼容的
这代表C++标准库的代码尽管它通常不是每次都从stratch编译的,但插入了为数不多的预编译版本之一(包括委托给DLL的"thunk"版本)。
真正的部分:陷阱和解决方案
如果您有两个模块,问题将发生
- 使用不同版本或类型的二进制表示和
- 该类型的交换对象
这个问题主要发生在动态链接中(但如果预先构建的静态库与其标头不对应,则也可能发生在静态链接中)。
如果模块已针对定义该类型的库的二进制不兼容版本进行编译(使用有问题的类型)和链接(使用处理有问题类型的库代码),则会发生这种情况。
因此,特别是对于C++标准库,为了避免和/或修复这些问题,您需要
-
请确保所有交换C++类型的模块都已编译并链接到C++运行时的二进制兼容版本(对于正在交换的类型)
- 这相对容易检查是否所有此类模块都动态链接到它,但如果有些模块静态编译,则不太容易
- 基本上,在理想情况下,所有模块甚至应该使用运行时的同一实例(在Linux中,它包括
libgcc
),例如,可能需要在它们之间传递异常。如果您的环境是如此异构以至于无法实现,那么明智的做法可能是完全放弃C++类型作为交换格式
在链接时区分不兼容的库版本
(指定正确的模块并命名自己的模块,以便区分它们)
在链接时(静态或动态),不存在"构建配置"这样的东西。这里有许多模块。链接器看到的只是对其他模块的引用(仅限动态链接)和导出/导入的条目。它所做的就是找到引用的模块(同样,仅限动态;在静态中,模块通常是显式指定的),然后将导出和导入的条目匹配在一起。C++导出/导入条目名称包含编码("损坏")形式的条目的整个签名(特别是,不兼容的编译器必须使用不同的损坏方案,这样它们生成的条目就不会冲突。
链接器区分库的两个版本的唯一方法是:
- 提供不同的搜索路径/明确指定不同的模块
- 使用不同的模块名称进行链接
- 使用不同的条目名称(意味着不同的类型名称/函数签名)
3)通常不实用(需要大量样板代码),并且在动态链接中,如果没有2),则无法使用,因为链接器首先匹配模块名称。1) (搜索路径风格)无法缩放。
总结:
- 2)是系统范围动态链接的标准做法(
_d
后缀,版本后缀) - 1) 是构建环境(静态和动态)的标准做法
- 1) (搜索路径风格)用于链接到图书馆的私人副本(正如所说,这不可扩展)
- C++中std::resize(n)和std::shrink_to_fit之间的区别
- int(c) 和 c-'0' 之间的区别。C++
- 在cuda线程之间共享大量常量数据
- 在c代码之间共享数据的最佳方式
- Mix_Init和Mix_OpenAudio SDL之间的区别是什么
- C++ 使用 assign 函数的字符串与直接使用 '=' 更改值的字符串之间的区别
- VSOMEIP-2个设备之间的通信(TCP/UDP)不工作
- std::atomic和std::condition_variable wait,notify_*方法之间的区别
- 这个指针在c++中的用法
- 大小相等但成员数量不同的结构之间的性能差异
- 类与私有变量的其他类之间的线程安全性
- 如何在cpp文件之间切换窗口?在Qt中
- 线程之间的布尔停止信号
- 复制和交换习惯用法与移动操作之间的交互
- C++ 中函数中 Const 用法之间的差异
- 调试和发布配置 (c++) 中内置的共享库之间的用法有何区别?
- 视图和控制器之间接口的习惯用法
- reinterpret_cast用法之间的差异
- C++ get 和类型转换之间的用法有什么区别?我应该使用哪一个?
- IDXGI 和 ID3D11Device 和 ID3D11DeviceContext 之间的用法差异