分段错误求助:类读写特定结构二进制文件的内存分配问题
二进制文件读写后跨进程析构触发段错误排查
我编写了一个类用于读写包含特定数据结构的二进制文件。同一进程内保存后再加载一切正常,但先保存文件再重启程序加载时,程序能完成所有功能,但结束时触发分段错误,疑似内存分配问题。简化代码如下:
读写文件的类头文件
#ifndef __TESTS_MAP_HH__ #define __TESTS_MAP_HH__ #include <iostream> #include <fstream> #include <string> #include <vector> #include <algorithm> #include <sstream> typedef struct test_struct{ bool test_bool; double test_double; std::vector<double> test_vector; } test_struct_t; class map { private: std::string m_path, m_str; double m_double; test_struct m_struct; public: map(const std::string& a_id); void set_str(std::string a_str); void set_double(double a_double); void set_struct(test_struct a_struct); void load_file(); void save_file() const; void show_file() const; ~map(); }; #endif //__TESTS_MAP_HH__
读写二进制文件的类源文件
#include "map.hh" map::map(const std::string& a_id) { m_path = a_id + ".bin"; m_str = "none"; m_double = 0.0; m_struct = {false, 0.0}; } void map::set_str(std::string a_str){ m_str = a_str; } void map::set_double(double a_double){ m_double = a_double; } void map::set_struct(test_struct a_struct){ m_struct = a_struct; } void map::load_file(){ std::ifstream l_inF; l_inF.open(m_path.c_str(), std::ios::binary | std::ios::in); l_inF.read((char*)&m_double,sizeof(double)); l_inF.read((char*)&m_struct,sizeof(test_struct_t)); size_t str_size; l_inF.read((char*)&str_size, sizeof(str_size)); m_str.resize(str_size); l_inF.read((char*)&m_str[0], str_size); l_inF.close(); } void map::save_file() const{ std::ofstream l_outF; l_outF.open(m_path.c_str(), std::ios::binary | std::ios::out); l_outF.write((char*)&m_double,sizeof(double)); l_outF.write((char*)&m_struct,sizeof(test_struct_t)); size_t str_size = m_str.size(); l_outF.write((char*)&str_size, sizeof(str_size)); l_outF.write((char*)&m_str[0], str_size); l_outF.close(); } void map::show_file() const{ std::cout << ">>-----------------------------------------------" << std::endl; std::cout << ">> double : " << m_double << std::endl; std::cout << ">> double : " << m_double << std::endl; std::cout << ">> struct.bool : " << m_struct.test_bool << std::endl; std::cout << ">> struct.double : " << m_struct.test_double << std::endl; std::cout << ">> struct.vector : " << "size = " << m_struct.test_vector.size() << std::endl; std::cout << ">> string : " << m_str << std::endl; std::cout << ">>-----------------------------------------------" << std::endl; } map::~map(){}
主函数(case1运行正常,case2触发分段错误)
#include "map.hh" int main(int argc, char const *argv[]) { std::string id = "mapfile"; int action = 0; if(argc > 1) action = std::stoi(argv[1]); else { std::string input; std::cout << "Enter case (1 or 2): "; std::cin >> input; action = std::stoi(input); } switch (action) { case 1: { // This works 100% (no errors and it saves/reads class perfectly) std::vector<double> l_vect = {0.1, 0.0, 0.6}; test_struct save_struct = {true, 5.0, l_vect}; map test_save(id); test_save.show_file(); test_save.set_double(8.0); test_save.set_str("save this string"); test_save.set_struct(save_struct); test_save.show_file(); test_save.save_file(); map test_load(id); test_load.load_file(); test_load.show_file(); } break; case 2: { // gives segmentation error at the end of the program map test_load(id); test_load.load_file(); test_load.show_file(); } break; default: break; } return 0; }
问题根源
核心错误在于直接读写包含std::vector的结构体:std::vector内部存储的是指向堆内存的指针、元素数量、容量等元数据,直接将结构体写入文件时,保存的只是指针的内存地址而非实际容器数据。跨进程加载时,旧指针地址在新进程的地址空间中是无效的,当std::vector析构时尝试释放这块无效内存,就会触发分段错误。
同一进程内运行正常只是侥幸——此时保存的指针地址仍在当前进程的有效内存范围内,析构时能正常释放,但这种写法本质上违反了std::vector的内存管理逻辑,属于错误实现。
修复方案
需要手动序列化/反序列化std::vector,不能直接读写整个结构体。修改save_file和load_file函数如下:
修改后的save_file函数
void map::save_file() const{ std::ofstream l_outF; l_outF.open(m_path.c_str(), std::ios::binary | std::ios::out); l_outF.write((char*)&m_double,sizeof(double)); // 单独保存结构体中的非容器成员 l_outF.write((char*)&m_struct.test_bool, sizeof(bool)); l_outF.write((char*)&m_struct.test_double, sizeof(double)); // 序列化vector:先存大小,再存元素数据 size_t vec_size = m_struct.test_vector.size(); l_outF.write((char*)&vec_size, sizeof(vec_size)); if(vec_size > 0){ l_outF.write((char*)m_struct.test_vector.data(), vec_size * sizeof(double)); } // 保存字符串(当前写法正确,继续保留) size_t str_size = m_str.size(); l_outF.write((char*)&str_size, sizeof(str_size)); l_outF.write((char*)&m_str[0], str_size); l_outF.close(); }
修改后的load_file函数
void map::load_file(){ std::ifstream l_inF; l_inF.open(m_path.c_str(), std::ios::binary | std::ios::in); l_inF.read((char*)&m_double,sizeof(double)); // 加载结构体非容器成员 l_inF.read((char*)&m_struct.test_bool, sizeof(bool)); l_inF.read((char*)&m_struct.test_double, sizeof(double)); // 反序列化vector:先读大小,再分配空间读取元素 size_t vec_size; l_inF.read((char*)&vec_size, sizeof(vec_size)); m_struct.test_vector.resize(vec_size); if(vec_size > 0){ l_inF.read((char*)m_struct.test_vector.data(), vec_size * sizeof(double)); } // 加载字符串(当前写法正确,继续保留) size_t str_size; l_inF.read((char*)&str_size, sizeof(str_size)); m_str.resize(str_size); l_inF.read((char*)&m_str[0], str_size); l_inF.close(); }
额外注意事项
std::string的当前写法是正确的(先存长度再存数据),如果直接读写std::string对象也会出现和std::vector相同的问题,需保持现有逻辑。- 若需跨平台使用,要额外处理字节序问题(比如小端/大端转换),避免不同平台间数据解析异常。
内容的提问来源于stack exchange,提问作者Lente
相关产品推荐
相关产品推荐

