直接用 fread 或 std::ifstream::read 读二进制文件最简单,但结构体必须是标准布局(无虚函数、继承、非公有成员、用户定义构造/析构,不含 std::string 等非 POD 类型),且字段顺序、padding、字节序、sizeof 需跨平台一致;非 POD 成员须手动序列化长度与内容;推荐封装 serialize/deserialize 函数,并预留版本字段保障兼容性。

直接用 fread 读二进制文件最简单,但必须确保结构体是标准布局(standard-layout)
如果结构体里只有基本类型(int、double、char 数组等),没有虚函数、非公有非静态成员、继承或用户定义构造/析构函数,那它就是标准布局,内存布局和 C 兼容,fread 可以安全读入。
常见错误:结构体含 std::string 或 std::vector —— 这些对象内部指针指向堆内存,fread 只拷贝指针值,反序列化后访问必崩溃。
- 检查方式:编译期断言
static_assert(std::is_standard_layout_v<mystruct>);</mystruct> - 结构体字段顺序要和写入时完全一致(包括 padding)
- 跨平台时注意字节序和
sizeof差异(比如long在 Windows 和 Linux 下可能不同)
用 std::ifstream 配合 read() 更现代,但本质和 fread 一样
它不自动处理换行或格式,适合已知固定大小的二进制块。关键不是“用什么流”,而是“读什么内容”——只要数据是原样 write() 出去的,就能原样读回来。
示例:
立即学习“C++免费学习笔记(深入)”;
struct Point { int x; int y; };
std::ifstream fin("data.bin", std::ios::binary);
std::vector<Point> pts(100);
fin.read(reinterpret_cast<char*>(pts.data()), sizeof(Point) * pts.size());
- 务必检查
fin.gcount() == sizeof(Point) * pts.size(),否则读取不全 - 不能对
pts使用resize()后直接data()——resize()不保证内存连续初始化,应先reserve()再resize(),或用std::vector<point> pts(n);</point>构造 - 若文件大小未知,先
fin.seekg(0, std::ios::end); auto size = fin.tellg();算出元素个数
遇到 std::string 等非 POD 类型,必须手动序列化字段
不能跳过,也不能指望 memcpy。得把字符串长度、内容分开存,读的时候先读长度,再按长度分配并读内容。
例如:
struct Person {
int age;
std::string name;
};
// 写入:
out.write(reinterpret_cast<const char*>(&p.age), sizeof(p.age));
size_t len = p.name.size();
out.write(reinterpret_cast<const char*>(&len), sizeof(len));
out.write(p.name.c_str(), len);
// 读入:
Person p;
in.read(reinterpret_cast<char*>(&p.age), sizeof(p.age));
size_t len;
in.read(reinterpret_cast<char*>(&len), sizeof(len));
p.name.resize(len);
in.read(&p.name[0], len);
- 字符串中间含
\0没问题,因为按长度读,不是c_str()风格 - 如果结构体嵌套其他自定义类型,每个都得自己实现读/写逻辑,别偷懒用
reinterpret_cast - 建议封装成
serialize()/deserialize()成员函数,避免每次手写重复逻辑
别用 operator<< / operator>> 直接读结构体
这会触发格式化 I/O,把整数转成十进制字符串再写,再读回来还要解析——既慢又不可靠(空格、换行、精度丢失都可能破坏结构)。除非你明确需要人可读的文本格式,否则这是典型误用。
- 错误写法:
out << my_struct.age << " " << my_struct.name << "\n"; - 即使加了
std::noskipws,也解决不了字段边界模糊、字符串含空格就截断的问题 - 真要文本格式,用 JSON 或 CSV 库(如 nlohmann/json),别自己拼接
实际项目中,最容易被忽略的是版本兼容性:今天写的结构体,明天加了个字段,旧文件加载就会错位。哪怕只是预留一个 uint8_t version; 字段,也比硬编码字段偏移强得多。


















