如何在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();
方案优势
- 内存连续:所有数据(头部+动态数组)在一块内存中,减少缓存失效,读写效率更高。
- 序列化简单:直接读写整个内存块,无需拆分指针和数据,代码更简洁。
- 无内存泄漏风险:统一管理内存,析构时一次释放即可。
内容的提问来源于stack exchange,提问作者nealh
相关产品推荐
相关产品推荐

