IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 记 QT 应用开发中的一个二进制兼容性问题 -> 正文阅读

[C++知识库]记 QT 应用开发中的一个二进制兼容性问题

笔者在参与开发一个集成了 QT 的跨平台桌面应用程序,目标平台是 Windows 和 Mac。一段时间以来,运行 Windows 平台的应用程序时,不断地被类似于如下这样的崩溃问题所折磨。

C++ Runtime 库崩溃

这里提示说,在 C++ Runtime 库中发生了崩溃,C++ Runtime 库中的一个断言失败了,即断言 _ASSERTE(__acrt_first_block == header); 失败了,这也是崩溃直接发生的位置。断言失败的这行代码更详细的上下文如下:

崩溃直接位置上下文

从发送崩溃的这段代码中,基本上看不出任何线索。

通过 Visual Studio 查看这个崩溃发生的调用堆栈,找到这个堆栈中最靠近栈顶的我们自己编写的代码,可以看到这个崩溃发生在如下函数中:

void MainWindow::on_cameraComboBox_currentTextChanged(const QString& arg1) {
  auto camera_device =
      std::find_if(camera_info_.begin(), camera_info_.end(),
                   [&arg1](const CameraDeviceInfo& info) -> bool {
                     std::string device_name = info.name;
                     return device_name == arg1.toStdString();
                   });
  if (camera_device != std::end(camera_info_)) {
    MediaEngineImpl::GetInstance().SetCameraDevice(camera_device->id);
  }
}

这个 崩溃发生的具体位置如下:

微信图片_20220329155621.png

即在 lambda 表达式返回的第 200 行发生了崩溃。代码本身,实在是看不出来有任何问题。

笔者想知道,是不是这里的 lambda 真有什么问题。于是笔者做了一个实验,移除上面这个函数中的几乎所有代码,只保留如下这一行:

void MainWindow::on_cameraComboBox_currentTextChanged(const QString& arg1) {
  auto str = arg1.toStdString();
}

再次运行 QT 应用程序,执行到这个函数时,崩发溃依然生。这种崩溃发生之莫名其妙,简直是让人怀疑人生。这次崩溃的具体位置如下图中 Visual Studio 的箭头所指的位置:

崩溃 2

如图所示,崩溃发生在函数执行结束时的第 197 行。

这,其实是一个二进制兼容性问题。跨二进制编译目标传递对象时,有风险出现。比如一个动态链接库创建的对象,传给了另一个动态链接库来销毁。或者动态链接库创建了一个对象,但在应用程序中销毁了。

二进制兼容性问题的类型可能有很多。对象的创建和销毁在不同的二进制目标中导致的二进制兼容性问题可能也有不少。但就这个问题来说,出现二进制兼容性问题的原因在于 C++ MSVC 运行时库,即两个编译目标在编译链接时链接了不同的 C++ MSVC 运行时库。

C++ MSVC 运行时库会执行一些诸如内存分配与释放之类的操作。Windows 有动态 MSVC 运行时库和静态 MSVC 运行库时之分。Windows 平台的 C/C++ 程序,需要链接动态 MSVC 运行时库时,加 “/MDd” 或 “/MD” 编译标记(其中前者为 debug 版,后者为 release 版),需要链接静态 MSVC 运行时库时,加 “/MTd” 或 “/MT” 编译标记(其中前者为 debug 版,后者为 release 版)(webrtc\build\config\win\BUILD.gn)。

动态 MSVC 运行时库和静态 MSVC 运行时库在不同的堆中分配内存。大体可以理解为,链接静态 MSVC 运行时库时,每个编译目标都有一个堆,如每个动态链接库有自己的堆,可执行文件也有自己的堆;链接动态 MSVC 运行时库时,则是所有链接动态 MSVC 运行时库的各个编译目标共用同一个堆。

当编译应用程序和动态链接库时链接了不同的 MSVC 运行时库,而又在两者之间共同管理了对象的生命周期,则会出现我们这里遇到的崩溃。在这里,QT 链接了动态 MSVC 运行时库,我们的可执行程序链接了静态 MSVC 运行时库。这段代码中,QT 创建了一个 std::string 给到应用程序,其中引用了一块在 QT 内部,在堆上分配的内存,即通过动态 MSVC 运行时库分配的内存。std::string 是个模板类,应用程序中,函数返回时,释放由 QT 返回的 std::string 对象,释放对象时,将内存还回给堆,但这时会还回给静态 MSVC 运行时库,于是就出现了上面的问题。

如此说来,两个同时链接动态 MSVC 运行时库的编译目标可以共同管理对象的生命周期,因为它们共用同一堆。而链接了静态 MSVC 运行时库的编译目标,不能与其它编译目标共同管理对象的生命周期,无论另一个编译目标链接的是静态 MSVC 运行时库,还是动态 MSVC 运行时库。

如此,解决问题的办法也就明了了。使用了 QT 的程序,最好链接动态 MSVC 运行时库。我们为我们的应用程序添加链接动态 MSVC 运行时库的编译标记:

if(SSZRTC_SYSTEM_NAME STREQUAL "win")
  set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MD")
  set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MDd")
endif()

如果一个动态链接库,new 了一个非模板类对象,返回给应用程序,随后在应用程序中 delete,是不是也有很大的风险出现?这种情况相对安全一点,这是因为 delete 释放内存是一个间接操作。

对于上面的这种问题,还有一种解决的思路,即严格遵守谁创建的对象谁释放的原则。一个动态链接库开出了创建对象的接口,则同时也必须开出释放对象的接口。

在 Stackoverflow 上有一个问题在讨论这个,Debug Assertion Failed! Expression: __acrt_first_block == header ,有兴趣的也可以参考一下。

参考文档

Debug Assertion Failed! Expression: __acrt_first_block == header

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-03-31 23:45:54  更:2022-03-31 23:49:12 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 2:06:11-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码