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

如何在C++中序列化与反序列化变长结构体?程序崩溃求助

问题排查与优化方案

崩溃原因分析

你的代码访问bp_时崩溃,核心问题出在指针成员的序列化逻辑错误:

  • 序列化Dump时,你刻意跳过了bp_/ap_/bv_/av_四个指针的写入,只写了结构体固定头部和动态数组的数据。但反序列化Load时,直接把buff强制转换为DepthUpdate*,此时结构体里的bp_成员是buff中的垃圾值(从未写入过有效数据),后续用du->bp_ + du->length_计算其他指针地址时,使用的是无效指针,必然导致内存访问错误。
  • 额外问题:sizeof(DepthUpdate) - sizeof(bp_) * 2 - sizeof(bv_) * 2的计算未考虑结构体的内存对齐,实际写入的头部长度可能与预期不符,导致数据偏移错位。

修复后的基础方案

先修正现有逻辑,让指针指向反序列化后的内存块内部,同时补全内存释放逻辑避免泄漏:

#pragma once
#include <iostream>
#include <fstream>
#include <sys/time.h>
#include <utility>
#include <cstring>

class DepthUpdate {
public:
    size_t length_;
    timeval t_, ex_t_;
    char ticker_[16];
    char exchange_[16];
    double* bp_;
    double* ap_;
    size_t* bv_;
    size_t* av_;

    DepthUpdate(size_t len) : length_(len) {
        bp_ = new double[length_];
        ap_ = new double[length_];
        bv_ = new size_t[length_];
        av_ = new size_t[length_];
    }

    ~DepthUpdate() {
        delete[] bp_;
        delete[] ap_;
        delete[] bv_;
        delete[] av_;
    }

    void Dump(std::fstream* f) {
        // 逐个写入固定头部字段,避免对齐问题
        f->write(reinterpret_cast<char*>(&length_), sizeof(length_));
        f->write(reinterpret_cast<char*>(&t_), sizeof(t_));
        f->write(reinterpret_cast<char*>(&ex_t_), sizeof(ex_t_));
        f->write(ticker_, sizeof(ticker_));
        f->write(exchange_, sizeof(exchange_));
        // 写入动态数组数据
        f->write(reinterpret_cast<char*>(bp_), sizeof(double) * length_);
        f->write(reinterpret_cast<char*>(ap_), sizeof(double) * length_);
        f->write(reinterpret_cast<char*>(bv_), sizeof(size_t) * length_);
        f->write(reinterpret_cast<char*>(av_), sizeof(size_t) * length_);
    }

    static size_t calsize(size_t len) {
        return sizeof(length_) + sizeof(t_) + sizeof(ex_t_) + sizeof(ticker_) + sizeof(exchange_)
               + len * (sizeof(double)*2 + sizeof(size_t)*2);
    }

    static std::pair<DepthUpdate*, size_t> Load(void* ptr) {
        char* base = reinterpret_cast<char*>(ptr);
        DepthUpdate* du = new DepthUpdate(0);
        // 读取固定头部
        du->length_ = *reinterpret_cast<size_t*>(base);
        base += sizeof(size_t);
        du->t_ = *reinterpret_cast<timeval*>(base);
        base += sizeof(timeval);
        du->ex_t_ = *reinterpret_cast<timeval*>(base);
        base += sizeof(timeval);
        memcpy(du->ticker_, base, sizeof(ticker_));
        base += sizeof(ticker_);
        memcpy(du->exchange_, base, sizeof(exchange_));
        base += sizeof(exchange_);
        // 分配数组并读取数据
        du->bp_ = new double[du->length_];
        memcpy(du->bp_, base, sizeof(double)*du->length_);
        base += sizeof(double)*du->length_;
        du->ap_ = new double[du->length_];
        memcpy(du->ap_, base, sizeof(double)*du->length_);
        base += sizeof(double)*du->length_;
        du->bv_ = new size_t[du->length_];
        memcpy(du->bv_, base, sizeof(size_t)*du->length_);
        base += sizeof(size_t)*du->length_;
        du->av_ = new size_t[du->length_];
        memcpy(du->av_, base, sizeof(size_t)*du->length_);
        return std::make_pair(du, calsize(du->length_));
    }
};

修正后的使用示例:

// 序列化
std::fstream f("a.bin", std::ios::out | std::ios::binary);
DepthUpdate du(10);
// 填充测试数据
for(size_t i=0; i<10; i++){
    du.bp_[i] = 100.0 + i;
    du.ap_[i] = 101.0 + i;
    du.bv_[i] = 1000 + i;
    du.av_[i] = 2000 + i;
}
du.Dump(&f);
f.close();

// 反序列化
std::fstream f_in("a.bin", std::ios::in | std::ios::binary);
size_t total_size = DepthUpdate::calsize(10);
char* buff = new char[total_size];
f_in.read(buff, total_size);
auto [p, offset] = DepthUpdate::Load(buff);
std::cout << p->bp_[0] << std::endl; // 输出100.0
delete[] buff;
delete p;
f_in.close();

更优的变长结构体序列化方案

如果追求极致性能和内存连续性,推荐使用柔性数组(C99特性,C++兼容),让整个数据结构在连续内存中,避免多次动态分配:

#pragma once
#include <iostream>
#include <fstream>
#include <sys/time.h>
#include <cstring>

struct DepthUpdateHeader {
    size_t length_;
    timeval t_, ex_t_;
    char ticker_[16];
    char exchange_[16];
    // 柔性数组:按顺序存储bp, ap, bv, av数据
    char data_[0];
};

class DepthUpdate {
private:
    DepthUpdateHeader* header_;
    static size_t calcTotalSize(size_t len) {
        return sizeof(DepthUpdateHeader) + len * (sizeof(double)*2 + sizeof(size_t)*2);
    }
public:
    explicit DepthUpdate(size_t len) {
        header_ = reinterpret_cast<DepthUpdateHeader*>(new char[calcTotalSize(len)]);
        header_->length_ = len;
    }

    ~DepthUpdate() {
        delete[] reinterpret_cast<char*>(header_);
    }

    // 直接返回连续内存中的数组指针
    double* bp() { return reinterpret_cast<double*>(header_->data_); }
    double* ap() { return bp() + header_->length_; }
    size_t* bv() { return reinterpret_cast<size_t*>(ap() + header_->length_); }
    size_t* av() { return bv() + header_->length_; }

    // 序列化:直接写入整个连续内存块
    void Dump(std::fstream* f) {
        f->write(reinterpret_cast<char*>(header_), calcTotalSize(header_->length_));
    }

    // 从文件反序列化
    static DepthUpdate* Load(std::fstream* f) {
        // 先读取长度确定总大小
        size_t len;
        f->read(reinterpret_cast<char*>(&len), sizeof(len));
        f->seekg(0);
        DepthUpdate* du = new DepthUpdate(len);
        f->read(reinterpret_cast<char*>(du->header_), calcTotalSize(len));
        return du;
    }

    // 从内存块反序列化
    static DepthUpdate* LoadFromMemory(void* ptr) {
        DepthUpdateHeader* hdr = reinterpret_cast<DepthUpdateHeader*>(ptr);
        DepthUpdate* du = new DepthUpdate(hdr->length_);
        memcpy(du->header_, hdr, calcTotalSize(hdr->length_));
        return du;
    }
};

使用示例:

// 序列化
std::fstream f("a.bin", std::ios::out | std::ios::binary);
DepthUpdate du(10);
for(size_t i=0; i<10; i++){
    du.bp()[i] = 100.0 + i;
    du.ap()[i] = 101.0 + i;
    du.bv()[i] = 1000 + i;
    du.av()[i] = 2000 + i;
}
du.Dump(&f);
f.close();

// 反序列化
std::fstream f_in("a.bin", std::ios::in | std::ios::binary);
DepthUpdate* p = DepthUpdate::Load(&f_in);
std::cout << p->bp()[0] << std::endl; // 输出100.0
delete p;
f_in.close();

方案优势

  1. 内存连续:所有数据(头部+动态数组)在一块内存中,减少缓存失效,读写效率更高。
  2. 序列化简单:直接读写整个内存块,无需拆分指针和数据,代码更简洁。
  3. 无内存泄漏风险:统一管理内存,析构时一次释放即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 05:01:34