C++遍历文件中结构体时出现退出码-107371819读访问错误
退出码exited with code -107371819对应Windows平台内存访问违例错误(错误码0xC0000005),你标记的问题行判断完全准确,崩溃核心原因是对包含std::string类型的结构体直接做整块二进制读写。
std::string不属于可直接二进制拷贝的纯值类型(POD类型):它内部仅持有指向堆内存的指针、字符串长度、容量三个元数据,实际的字符内容存储在堆上,并不在结构体自身的内存布局范围内。当你调用file.read((char*)&temp, sizeof(Tool))时,会把文件里存储的旧string内部指针值直接覆盖到当前temp对象的name成员上——这个指针是之前写入文件时程序运行的旧地址,对当前进程来说是完全无效的野指针。
你观察到程序能正常打印内容、能正常关闭文件,是因为这些操作还没触发string对这块野指针的内存操作;等listTools()函数运行到末尾,局部变量temp出作用域析构时,string的析构函数会尝试释放自己管理的内存,访问到这个野指针就会立刻触发访问错误。
至于你提到的「曾经正常运行过一次、之后没改代码也持续报错」纯是巧合:那次运行时程序内存布局恰好让string分配到了和写入文件时完全一致的地址,野指针刚好指向了合法可访问的内存,所以没触发崩溃;但内存地址分配受系统环境、程序加载顺序影响极大,没有任何可复现性,本质上代码从一开始就存在内存错误。
另外你的循环写法也有逻辑缺陷:while (!file.eof())的判断会在最后一次读取失败时多进入一次循环,输出无效的临时变量内容,正确的判断应该把读操作本身作为循环条件。
根据业务需求二选一即可:
- 方案1:将结构体中的
std::string替换为固定长度字符数组,让整个结构体成为连续存储的纯值类型,再使用二进制块读写
参考实现代码:// 结构体修改 struct Tool { int tid; char name[32]; // 固定长度,根据业务需要调整大小,确保能存下最长的工具名 int quantity; double cost; // 原有get方法适配修改 const char* getName() const { return name; } int getTid() const { return tid; } int getQuantity() const { return quantity; } double getCost() const { return cost; } }; void listTools() { fstream file; file.open("hardware.txt", ios::binary | ios::in); if (!file.is_open()) { cout << "Failed to open file" << endl; return; } Tool temp; // 直接把read操作作为循环条件,避免eof判断导致的多读问题 while (file.read((char*)&temp, sizeof(Tool))) { cout << temp.getTid() << temp.getName() << temp.getQuantity() << temp.getCost() << endl; } // 读到文件尾是正常情况,非eof触发的读取失败才是真错误 if (!file.eof()) { cout << "File read error occurred before reaching end of file" << endl; } file.close(); cout << "File closed successfully." << endl; } - 方案2:保留
std::string,放弃整块结构体读写,改为逐个字段做序列化/反序列化
写入时按固定顺序写:tid值、name的长度、name的实际字符内容、quantity值、cost值;读取时也按相同顺序逐个字段读取,读name时先读长度,再给string分配对应长度的空间后读取字符内容,绝对不要直接覆盖string对象的原始内存。
所有涉及整块二进制读写结构体的场景,必须先确认结构体所有成员都是POD类型(基础类型、固定长度数组、无自定义构造/析构且所有成员都是POD的结构体),只要包含std::string、std::vector这类带动态内存管理的类型,直接二进制读写一定会出现野指针问题。
内容的提问来源于stack exchange,提问作者BobdaFett

