You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

重新赋值std::unique_ptr<QAudioBuffer>成员引发堆损坏问题

问题现象

构建std::unique_ptr<QAudioBuffer>类型的类成员时触发运行时异常:

  • 首次在按钮点击槽函数中为该智能指针赋值时程序运行正常
  • 当AudioConfig参数变更导致sizeInBytes取值变化、需要重新赋值时,程序触发崩溃,Visual Studio弹出HEAP_CORRUPTION_DETECTED(检测到堆损坏)提示
  • 崩溃点定位在const auto data = std::make_unique<char[]>(sizeInBytes)对应的数组智能指针析构阶段,堆栈显示堆内存损坏错误

槽函数代码如下:

void
MainWindow::on_start_clicked() {
    // 第二次进入该函数时在createAudioBuffer内部触发堆损坏
    mpAudioBuffer = createAudioBuffer(*mpAudioConfig);
    ...
}

createAudioBuffer函数原实现:

std::unique_ptr<QAudioBuffer>
MainWindow::createAudioBuffer(const AudioConfig& rConfig) const
{
    const auto bytesPerSample =
        (rConfig.sampleFormat == SampleFormat::Uint8) ? 1 :
        (rConfig.sampleFormat == SampleFormat::Int32) ? 4 :
        (rConfig.sampleFormat == SampleFormat::Int16) ? 2 : -1;
    const auto sizeInBytes = bytesPerSample * rConfig.channelCount * rConfig.frameCount;
    // 为传入QByteArray构造函数分配原始数据内存
    const auto data = std::make_unique<char[]>(sizeInBytes);
    // TODO 初始化data内容
    return std::make_unique<QAudioBuffer>(QByteArray(
        data.get(), sizeInBytes), [&rConfig]()-> QAudioFormat {
            // 用lambda直接构造QAudioFormat,避免单独定义变量
            QAudioFormat format;
            format.setSampleRate(static_cast<int>(rConfig.sampleRate));
            format.setChannelCount(static_cast<int>(rConfig.channelCount));
            format.setSampleFormat(QAudioFormat::Int32);
            return format;
        }());
}

崩溃堆栈:

ucrtbased.dll!free_dbg_nolock(void * const block, const int block_use) Line 952 C++
ucrtbased.dll!_free_dbg(void * block, int block_use) Line 1030  C++
afdx.exe!operator delete(void * block) Line 38  C++
afdx.exe!operator delete[](void * block) Line 32    C++
afdx.exe!std::default_delete<char [0]>::operator()<char,0>(char * _Ptr) Line 3156   C++
afdx.exe!std::unique_ptr<char [0],std::default_delete<char [0]>>::~unique_ptr<char [0],std::default_delete<char [0]>>() Line 3390   C++
afdx.exe!MainWindow::createAudioBuffer(const AudioConfig & rConfig) Line 544    C++
根本原因

崩溃的核心原因是内存分配大小与QAudioBuffer实际访问的内存大小不匹配,导致越界写破坏堆元数据:

  1. 代码中计算bytesPerSample时根据rConfig.sampleFormat动态返回1/2/4,但构造QAudioFormat时硬编码为QAudioFormat::Int32(对应4字节每样本),两者逻辑不一致。
  2. 首次运行时配置的样本格式刚好是Int32,分配的内存大小和QAudioBuffer按格式访问的内存范围一致,不会触发越界;当配置切换为Uint8/Int16时,计算得到的sizeInBytes变小,但QAudioBuffer仍按4字节每样本的规则读写内存,越界写入破坏了堆块的控制信息。
  3. 当函数内局部智能指针data析构释放内存时,堆管理器检测到元数据被篡改,直接抛出堆损坏错误。
  4. 额外隐患:bytesPerSample的兜底值为-1,一旦传入不支持的样本格式,负数转换为无符号类型的size_t时会得到一个极大的正整数,导致内存分配大小完全不符合预期,进一步加剧内存访问错误。
修复方案
  1. 统一格式逻辑:根据配置的样本格式同步计算单样本字节数、设置QAudioFormat的样本类型,保证内存分配大小和QAudioBuffer的访问范围完全匹配。
  2. 增加参数校验:移除不合理的-1兜底分支,遇到不支持的样本格式直接抛出异常或返回错误,避免非法值导致的内存大小计算错误。
  3. 简化冗余逻辑:去掉立即执行的lambda构造QAudioFormat的写法,直接在函数内构造format对象,降低代码复杂度;构造QByteArray时明确将数据指针转为const char*,确保调用深拷贝版本的构造函数,避免意外触发内存接管逻辑。

修复后的代码实现:

std::unique_ptr<QAudioBuffer>
MainWindow::createAudioBuffer(const AudioConfig& rConfig) const
{
    int bytesPerSample = 0;
    QAudioFormat::SampleFormat qtSampleFormat;
    // 统一映射样本格式、单样本字节数,避免不匹配
    switch(rConfig.sampleFormat) {
        case SampleFormat::Uint8:
            bytesPerSample = 1;
            qtSampleFormat = QAudioFormat::UInt8;
            break;
        case SampleFormat::Int16:
            bytesPerSample = 2;
            qtSampleFormat = QAudioFormat::Int16;
            break;
        case SampleFormat::Int32:
            bytesPerSample = 4;
            qtSampleFormat = QAudioFormat::Int32;
            break;
        default:
            throw std::invalid_argument("不支持的音频样本格式");
    }
    // 显式做类型转换,避免整数溢出、类型不匹配问题
    const qsizetype sizeInBytes = static_cast<qsizetype>(bytesPerSample) 
        * static_cast<qsizetype>(rConfig.channelCount) 
        * static_cast<qsizetype>(rConfig.frameCount);
    
    auto data = std::make_unique<char[]>(sizeInBytes);
    // TODO 初始化音频数据内容

    QAudioFormat format;
    format.setSampleRate(static_cast<int>(rConfig.sampleRate));
    format.setChannelCount(static_cast<int>(rConfig.channelCount));
    format.setSampleFormat(qtSampleFormat);

    // 显式转const char*,确保QByteArray执行深拷贝
    QByteArray audioData(static_cast<const char*>(data.get()), sizeInBytes);
    return std::make_unique<QAudioBuffer>(audioData, format);
}

内容的提问来源于stack exchange,提问作者johnco3

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 12:27:24