API对话框操作后OpenFile函数出现内存访问违规的原因
问题原因及解决方案
核心问题:直接二进制读写std::string引发内存访问违规
你遇到的0xC0000005访问违例,根源是直接对包含std::string的结构体执行二进制读写操作。std::string作为C++容器类,内部存储的是指向堆内存的指针(附带长度、容量等元数据),而非字符串的实际内容:
- 调用
fwrite写入结构体时,文件里只保存了string内部的指针值,不是字符串的真实字符。 - 读取时
fread把旧的指针值恢复到新的CCharacter_Script对象中,此时指针指向的内存要么已经被释放,要么属于无效区域。后续调用character.str.c_str()访问该内存,或者character对象析构时尝试释放这块非法内存,都会触发访问违例。
代码中的具体问题
- Save函数错误:直接写入整个结构体,仅持久化了
std::string的内部指针,而非字符串内容。 - OpenFile函数错误:读取旧指针值到新
std::string对象,导致该对象持有无效内存地址,后续访问或析构时崩溃。
修复方案
需要手动序列化/反序列化std::string——单独写入字符串的长度和内容,读取时先读长度再分配内存读取内容。
修复后的Save函数
void Save(int nameID, int emotationID, char script[]) { CCharacter_Script character; character.ID_Name = nameID; character.ID_Emotiation = emotationID; character.str = script; FILE* fp = fopen("Save_Script.bin", "rb"); if (fp == NULL) { fp = fopen("Save_Script.bin", "wb"); } else { fclose(fp); fp = fopen("Save_Script.bin", "ab"); } // 先写入int类型成员 fwrite(&character.ID_Name, sizeof(int), 1, fp); fwrite(&character.ID_Emotiation, sizeof(int), 1, fp); // 写入字符串长度,再写入实际内容 size_t str_len = character.str.size(); fwrite(&str_len, sizeof(size_t), 1, fp); fwrite(character.str.c_str(), sizeof(char), str_len, fp); fclose(fp); }
修复后的OpenFile函数
void OpenFile() { CCharacter_Script character; FILE* fp = fopen("Save_Script.bin", "rb"); if (fp == NULL) { OutputDebugString("Error :: None File\n"); return; } OutputDebugString("Reading :: Script\n"); // 逐个读取成员,手动反序列化string while (true) { // 读取int成员 int read_count = fread(&character.ID_Name, sizeof(int), 1, fp); if (read_count <= 0) break; read_count = fread(&character.ID_Emotiation, sizeof(int), 1, fp); if (read_count <= 0) break; // 读取字符串长度,再分配内存读取内容 size_t str_len; read_count = fread(&str_len, sizeof(size_t), 1, fp); if (read_count <= 0) break; character.str.resize(str_len); read_count = fread(&character.str[0], sizeof(char), str_len, fp); if (read_count <= 0) break; myDebugMsg("Name: %d Emotation: %d\n '%s'\n", character.ID_Name, character.ID_Emotiation, character.str.c_str()); } fclose(fp); }
额外注意事项
- 不要对包含非POD(Plain Old Data)类型的结构体做直接二进制读写,
std::string、std::vector等容器都属于非POD类型。 - 若需要更健壮的序列化方案,可考虑JSON、XML等文本格式,或Protobuf这类专业序列化库,避免手动处理的繁琐。
内容的提问来源于stack exchange,提问作者dkskfktldi
相关产品推荐
相关产品推荐

