displayRecord函数执行后程序崩溃:读取访问违例问题求助
问题描述
displayRecord()函数能正常显示指定记录,但停顿一秒后程序在返回main()前意外崩溃,调试时触发错误:Exception thrown: read access violation. **_Val** was 0x19B6C345688。
已尝试在main()中打开文件并传入函数,也调用了clear()和seekg()重置文件位置,但问题依旧。
相关代码如下:
#include <iostream> #include <fstream> #include <string> #include <iomanip> using namespace std; struct Item { string description; int quantity; double wCost; double rCost; string dateAdded; }; int addRecord(); int displayRecord(); int changeRecord(); int main() { cout << "Inventory Program" << endl; cout << "-------------------------------" << endl << endl; char again = 'y'; int selection = 0; do { cout << "MENU" << endl; cout << "----" << endl; cout << "1. Add a record" << endl; cout << "2. Display a record" << endl; cout << "3. Change a record" << endl; cout << "4. Exit Program" << endl << endl; do { cout << "Please enter a selection from the menu (1, 2, 3, or 4): "; cin >> selection; if (selection != 1 && selection != 2 && selection != 3 && selection != 4) { cout << "\nYou must enter a valid selection of either 1, 2, 3, or 4. Please try again.\n\n"; } } while (selection != 1 && selection != 2 && selection != 3 && selection != 4); if (selection == 4) { cout << "Exiting Program..." << endl; cin.clear(); return 0; } if (selection == 1) { if (addRecord() == 1) { return 1; } } else if (selection == 2) { if (displayRecord() == 1) { return 1; } } else { if (changeRecord() == 1) { return 1; } } cout << "Return to menu? (Type Y for yes): "; cin >> again; } while (again == 'y' || again == 'Y'); cout << "-------------------------------" << endl << endl; system("pause"); return 0; } int displayRecord() { fstream inventoryFile("inventory.dat", ios::in | ios::binary); if (!inventoryFile) { cout << "ERROR: Cannot open file. Aborting Program." << endl; return 1; } Item item; long recNum = 0; cout << "\nEnter the record number you wish to display: "; cin >> recNum; inventoryFile.seekg((recNum - 1) * sizeof(item), ios::beg); inventoryFile.read(reinterpret_cast<char*>(&item), sizeof(item)); if (!inventoryFile) { cout << "\nERROR: Record does not exist." << endl << endl; inventoryFile.close(); return 0; } else { cout << endl; cout << showpoint << setprecision(2) << fixed << endl; cout << "Item Description: " << item.description << endl; cout << "Quantity : " << item.quantity << endl; cout << "Wholesale Cost : " << item.wCost << endl; cout << "Retail Cost : " << item.rCost << endl; cout << "Date Added: : " << item.dateAdded << endl << endl; inventoryFile.clear(); } inventoryFile.close(); return 0; }
问题根源
直接对包含std::string的结构体进行二进制读写是错误操作。std::string内部存储的是指向堆内存的指针、字符串长度等元数据,而非实际的字符串内容。写入文件时保存的只是这些指针值;读出时,这些指针指向的内存已无效(要么是之前分配的内存已释放,要么是无关地址),访问此类内存就会触发读访问违规。
修复方案
有两种可行的修复方式:
方式一:使用固定长度字符数组替代std::string
将结构体中的std::string替换为固定大小的char数组,确保结构体内存布局固定,二进制读写安全。
修改后的结构体和displayRecord示例:
// 修改后的结构体 struct Item { char description[50]; // 固定长度,可按需调整 int quantity; double wCost; double rCost; char dateAdded[20]; // 固定长度,可按需调整 }; // 修改后的displayRecord(addRecord也需要对应修改写入逻辑) int displayRecord() { fstream inventoryFile("inventory.dat", ios::in | ios::binary); if (!inventoryFile) { cout << "ERROR: Cannot open file. Aborting Program." << endl; return 1; } Item item; long recNum = 0; cout << "\nEnter the record number you wish to display: "; cin >> recNum; inventoryFile.seekg((recNum - 1) * sizeof(item), ios::beg); inventoryFile.read(reinterpret_cast<char*>(&item), sizeof(item)); if (!inventoryFile) { cout << "\nERROR: Record does not exist." << endl << endl; inventoryFile.close(); return 0; } else { cout << endl; cout << showpoint << setprecision(2) << fixed << endl; cout << "Item Description: " << item.description << endl; cout << "Quantity : " << item.quantity << endl; cout << "Wholesale Cost : " << item.wCost << endl; cout << "Retail Cost : " << item.rCost << endl; cout << "Date Added : " << item.dateAdded << endl << endl; } inventoryFile.close(); return 0; }
方式二:手动序列化/反序列化每个字段
不直接读写整个结构体,而是逐个读写每个字段。对于std::string,先写入/读出字符串长度,再写入/读出实际字符内容。
示例(仅展示displayRecord的读取逻辑,addRecord需对应写入逻辑):
int displayRecord() { fstream inventoryFile("inventory.dat", ios::in | ios::binary); if (!inventoryFile) { cout << "ERROR: Cannot open file. Aborting Program." << endl; return 1; } Item item; long recNum = 0; cout << "\nEnter the record number you wish to display: "; cin >> recNum; // 跳过前面recNum-1个记录的内容,需与addRecord的写入格式严格对应 // 读取description size_t descLen; inventoryFile.read(reinterpret_cast<char*>(&descLen), sizeof(descLen)); item.description.resize(descLen); inventoryFile.read(&item.description[0], descLen); // 读取quantity inventoryFile.read(reinterpret_cast<char*>(&item.quantity), sizeof(item.quantity)); // 读取wCost inventoryFile.read(reinterpret_cast<char*>(&item.wCost), sizeof(item.wCost)); // 读取rCost inventoryFile.read(reinterpret_cast<char*>(&item.rCost), sizeof(item.rCost)); // 读取dateAdded size_t dateLen; inventoryFile.read(reinterpret_cast<char*>(&dateLen), sizeof(dateLen)); item.dateAdded.resize(dateLen); inventoryFile.read(&item.dateAdded[0], dateLen); if (!inventoryFile) { cout << "\nERROR: Record does not exist." << endl << endl; inventoryFile.close(); return 0; } else { cout << endl; cout << showpoint << setprecision(2) << fixed << endl; cout << "Item Description: " << item.description << endl; cout << "Quantity : " << item.quantity << endl; cout << "Wholesale Cost : " << item.wCost << endl; cout << "Retail Cost : " << item.rCost << endl; cout << "Date Added : " << item.dateAdded << endl << endl; } inventoryFile.close(); return 0; }
注意:方式二需要addRecord函数也按照相同的顺序和格式写入字段,否则会出现读取错位问题。
内容的提问来源于stack exchange,提问作者CodeBrewSlam
相关产品推荐
相关产品推荐

