C++将vector保存到二进制文件读取时触发Segmentation fault段错误
问题描述
编写程序将列表数据保存为二进制文件,预期实现两个效果:一是避免数据被随意篡改,二是获得比文本文件更小的存储体积。采用三层嵌套vector<vector<vector<string>>>结构存储数据,但读取二进制文件时触发Segmentation fault(段错误)。
错误写入代码
#include <iostream> #include <fstream> #include <vector> using namespace std; vector<vector<vector<string>>> test; int main() { test.push_back({{"Hello"}}); cout << test[0][0][0]; ofstream otst("test.bin",ios::binary); otst.write((char*)&test,sizeof(test)); return 0; }
错误读取代码
#include <iostream> #include <fstream> #include <vector> using namespace std; vector<vector<vector<string>>> test; int main() { ifstream tst("test.bin",ios::binary); tst.read((char*)&test,sizeof(test)); return 0; }
错误根因
直接将vector对象本身的内存块写入文件的写法完全错误:
vector、string都属于动态管理堆内存的容器,对象本身只存储三类元信息:指向堆上真实数据的指针、当前元素数量、已分配容量,真实的字符串内容、各层vector存储的元素都存在对象之外的堆内存上。- 上述写法只把容器对象的元信息(包括失效的堆指针)写入了文件,根本没有保存任何实际业务数据。
- 读取时直接把文件里存的旧进程的指针值覆盖到新的容器对象上,这些指针地址不属于当前运行进程的合法内存空间,一旦访问就会触发段错误。就算不崩溃,读到的也都是无效内存数据。
修复方案
二进制存储动态容器必须做结构化序列化:按固定顺序逐层写入容器的长度、每个元素的实际内容,读取时按完全相同的顺序,先读长度再逐个还原元素,不能直接拷贝容器对象的内存布局。
修复后的写入代码
#include <iostream> #include <fstream> #include <vector> #include <string> using namespace std; int main() { vector<vector<vector<string>>> test; test.push_back({{"Hello"}}); cout << test[0][0][0] << endl; ofstream otst("test.bin", ios::binary); // 逐层写入尺寸,再写实际内容 size_t l1_size = test.size(); otst.write((char*)&l1_size, sizeof(l1_size)); for (auto& l2 : test) { size_t l2_size = l2.size(); otst.write((char*)&l2_size, sizeof(l2_size)); for (auto& l3 : l2) { size_t l3_size = l3.size(); otst.write((char*)&l3_size, sizeof(l3_size)); for (auto& s : l3) { size_t str_len = s.size(); otst.write((char*)&str_len, sizeof(str_len)); otst.write(s.data(), str_len); } } } otst.close(); return 0; }
修复后的读取代码
#include <iostream> #include <fstream> #include <vector> #include <string> using namespace std; int main() { vector<vector<vector<string>>> test; ifstream tst("test.bin", ios::binary); // 按写入顺序逐层读尺寸,再读内容还原结构 size_t l1_size; tst.read((char*)&l1_size, sizeof(l1_size)); test.resize(l1_size); for (size_t i = 0; i < l1_size; i++) { size_t l2_size; tst.read((char*)&l2_size, sizeof(l2_size)); test[i].resize(l2_size); for (size_t j = 0; j < l2_size; j++) { size_t l3_size; tst.read((char*)&l3_size, sizeof(l3_size)); test[i][j].resize(l3_size); for (size_t k = 0; k < l3_size; k++) { size_t str_len; tst.read((char*)&str_len, sizeof(str_len)); string s(str_len, '\0'); tst.read(s.data(), str_len); test[i][j][k] = move(s); } } } tst.close(); // 验证读取结果 cout << test[0][0][0] << endl; return 0; }
注意:如果需要跨平台兼容,还要额外处理整型的大小端、不同平台
size_t长度不一致的问题,同平台下上述代码可以正常运行。
内容的提问来源于stack exchange,提问作者Star Dev
相关产品推荐
相关产品推荐

