【问题标题】:Access violation in std::string destructor only in debugstd::string 析构函数中的访问冲突仅在调试中
【发布时间】:2013-09-01 17:44:16
【问题描述】:

我这几天一直在尝试解决这个问题,但无法让它发挥作用。我在从编译器生成的 POD 结构调用的 std::string 析构函数中的方法“orphan_all”中遇到访问冲突,该结构包含一些 std::string。

struct SaveData
{
    SaveData()
    {
        MusicStage = GameState::MusicStage;
        MusicSubStage= GameState::MusicSubStage;

        PlotStage = GameState::PlotStage;
        PlotSubStage = GameState::PlotSubStage;

        GameStage = GameState::GameStage;
        GameSubStage = GameState::GameSubStage;

        PlayerLife = 100.0f;
        PlayerSuitEnergy = 100.0f;

        CurrentPower = 0;
        PlayerPos = XMFLOAT3(0,0,0);
        CurrentGun = 0;
        Guns = 0;
        ModsL1 = 0;
        ModsL2 = 0;
        ModsL3 = 0;
        ModsL4 = 0;
        CurrentBulletMod = (uint)BulletMod::NoMod;
        ElectricModMult = 1.0;
        ExplosiveModMult = 1.0;
        CorrosiveModMult = 1.0;
    }

    string MusicStage;
    string MusicSubStage;
    string PlotStage;
    string PlotSubStage;
    string GameStage;
    string GameSubStage;

    float PlayerLife;
    float PlayerSuitEnergy;
    uint CurrentPower;
    XMFLOAT3 PlayerPos;

    uint CurrentGun;
    uint CurrentBulletMod;

    float ElectricModMult;
    float ExplosiveModMult;
    float CorrosiveModMult;

    uint Guns;
    uint ModsL1;
    uint ModsL2;
    uint ModsL3;
    uint ModsL4;
};

struct FileData
{
    uint64 Hash;
    uint Version;
    SaveData Data;
};

这就是结构。当调用该对象的析构函数时,这里:

HRESULT SavesIO::LoadGameFile(const std::string& FileName,SaveData& Data)
{
    ifstream file;
    file.open(FileName,ios::binary);
    if(file.is_open())
    {
        FileData fdata;
        file.read((char*)&fdata,sizeof(FileData));
        if(fdata.Hash != GameHash)
        {
            cout << "Corrupt Savegame : " << FileName << endl;
            return CheckHR(HR_Fail);
        }
        if(fdata.Version > CurrentVersion)
        {
            cout << "Savegame version is greater than game version : " << FileName << endl;
            return CheckHR(HR_Fail);
        }
        Data = fdata.Data;
        return HR_Correct;
    }

    cout << "Savegame : " << FileName << "not found" << endl;
    return CheckHR(HR_Invalid_Arg);
}

在“orphan_all”中发生了访问冲突,它是从“fdata”中“Data”中的字符串的析构函数调用的,它表示诸如“0xdddddddd”或“0xFEEEFEEE”之类的位置,因此由于某种原因它似乎调用一些已删除的数据。 我使用 HeapValidate() 和 _CrtCheckMemory() 检查了堆损坏,一切似乎都很好。如果我在发行版中编译,问题就会消失。 有人有什么想法吗? 我的系统是 Windows 8 Pro x64,使用 Visual Studio Express 2012,使用 v110 工具集编译。

编辑:我正在写这样的数据:

void SavesIO::SaveGameFile(SaveData Data,const std::string& FileName)
{
    ofstream file;
    file.open(FileName,ios::binary);

    FileData fdata;
    fdata.Hash = GameHash;
    fdata.Version = CurrentVersion;
    fdata.Data = Data;
    file.write((char*)&fdata,sizeof(FileData));

    file.close();
}

【问题讨论】:

  • 您正在(反)序列化复杂结构,就好像它们是 POD 一样。 file.read((char*)&amp;fdata,...) 这条线会给你带来麻烦。您实际上是在回读指针(例如包含在您的 std::strings 中),这些指针在您将它们写入文件时恰好是有效的。
  • 那么序列化/反序列化该结构的好主意是什么?这可能是后来崩溃的原因吗?
  • 未定义行为的原因,崩溃是一个显着的可观察结果。以文件格式存储字符串通常归结为写入长度后跟单个字符。写作和阅读都需要代码。使用 C++ 流和operator&gt;&gt; 可能是一个解决方案,但如果您要在不同位(32 和 64)或具有不同字节序(小字节序和大字节序)的机器之间共享安全游戏,这不是您的最佳选择。

标签: c++ windows winapi access-violation


【解决方案1】:

似乎_ITERATOR_DEBUG_LEVEL 应该归咎于调试模式只会崩溃。

我并不反对您的解决方案。相反,这是最好的做法。 但是,以下是GoodToKnow:

要么 #define _ITERATOR_DEBUG_LEVEL 0 或(甚至更好) 在项目中定义的预编译器中设置它。

这将停止 STL 以触发异常... 参考:https://msdn.microsoft.com/en-us/library/hh697468.aspx

确保所有嵌套/依赖项目都使用相同的选项编译,或者:_iterator_debug_level value '0' doesn't match value '2'

此定义的默认值为 2。

PS 看起来 MS 仍在编译他们内部开发的 STL,即使在工具集 v120 中也是如此

【讨论】:

    【解决方案2】:

    这是 IInspectable 所说的,但我不能将其标记为答案,因为它是评论。我的 write 和 read 函数现在看起来像这样:

    void SavesIO::SaveGameFile(SaveData Data,const std::string& FileName)
    {
        ofstream file;
        file.open(FileName,ios::binary);
    
        FileData fdata;
        fdata.Hash = GameHash;
        fdata.Version = CurrentVersion;
    
        fdata.PlayerLife = Data.PlayerLife;
        fdata.PlayerSuitEnergy = Data.PlayerSuitEnergy;
        fdata.CurrentPower = Data.CurrentPower;
        fdata.PlayerPos = Data.PlayerPos;
    
        fdata.CurrentGun = Data.CurrentGun;
        fdata.CurrentBulletMod = Data.CurrentBulletMod;
    
        fdata.ElectricModMult = Data.ElectricModMult;
        fdata.ExplosiveModMult = Data.ExplosiveModMult;
        fdata.CorrosiveModMult = Data.CorrosiveModMult;
    
        fdata.Guns = Data.Guns;
        fdata.ModsL1 = Data.ModsL1;
        fdata.ModsL2 = Data.ModsL2;
        fdata.ModsL3 = Data.ModsL3;
        fdata.ModsL4 = Data.ModsL4;
    
        file.write((char*)&fdata,sizeof(FileData));
    
        int size = Data.MusicStage.length();
        file.write((char*)&size,sizeof(int));
        file.write(Data.MusicStage.c_str(),Data.MusicStage.length()+1);
    
        size = Data.MusicSubStage.length();
        file.write((char*)&size,sizeof(int));
        file.write(Data.MusicSubStage.c_str(),Data.MusicSubStage.length()+1);
    
        size = Data.PlotStage.length();
        file.write((char*)&size,sizeof(int));
        file.write(Data.PlotStage.c_str(),Data.PlotStage.length()+1);
    
        size = Data.PlotSubStage.length();
        file.write((char*)&size,sizeof(int));
        file.write(Data.PlotSubStage.c_str(),Data.PlotSubStage.length()+1);
    
        size = Data.GameStage.length();
        file.write((char*)&size,sizeof(int));
        file.write(Data.GameStage.c_str(),Data.GameStage.length()+1);
    
        size = Data.GameSubStage.length();
        file.write((char*)&size,sizeof(int));
        file.write(Data.GameSubStage.c_str(),Data.GameSubStage.length()+1);
    
        file.close();
    }
    

    我的阅读:

    HRESULT SavesIO::LoadGameFile(const std::string& FileName,SaveData& Data)
    {
        ifstream file;
        file.open(FileName,ios::binary);
        if(file.is_open())
        {
            FileData fdata;
            file.read((char*)&fdata,sizeof(FileData));
            if(fdata.Hash != GameHash)
            {
                cout << "Corrupt Savegame : " << FileName << endl;
                return CheckHR(HR_Fail);
            }
            if(fdata.Version > CurrentVersion)
            {
                cout << "Savegame version is greater than game version : " << FileName << endl;
                return CheckHR(HR_Fail);
            }
    
            Data.PlayerLife = fdata.PlayerLife;
            Data.PlayerSuitEnergy = fdata.PlayerSuitEnergy;
            Data.CurrentPower = fdata.CurrentPower;
            Data.PlayerPos = fdata.PlayerPos;
    
            Data.CurrentGun = fdata.CurrentGun;
            Data.CurrentBulletMod = fdata.CurrentBulletMod;
    
            Data.ElectricModMult = fdata.ElectricModMult;
            Data.ExplosiveModMult = fdata.ExplosiveModMult;
            Data.CorrosiveModMult = fdata.CorrosiveModMult;
    
            Data.Guns = fdata.Guns;
            Data.ModsL1 = fdata.ModsL1;
            Data.ModsL2 = fdata.ModsL2;
            Data.ModsL3 = fdata.ModsL3;
            Data.ModsL4 = fdata.ModsL4;
    
            int size = 0;
            file.read((char*)&size,sizeof(int));
            char* tmp = new char[size+1];
            file.read(tmp,size+1);
            Data.MusicStage = tmp;
            delete tmp;
    
            size = 0;
            file.read((char*)&size,sizeof(int));
            tmp = new char[size+1];
            file.read(tmp,size+1);
            Data.MusicSubStage = tmp;
            delete tmp;
    
            size = 0;
            file.read((char*)&size,sizeof(int));
            tmp = new char[size+1];
            file.read(tmp,size+1);
            Data.PlotStage = tmp;
            delete tmp;
    
            size = 0;
            file.read((char*)&size,sizeof(int));
            tmp = new char[size+1];
            file.read(tmp,size+1);
            Data.PlotSubStage = tmp;
            delete tmp;
            return HR_Correct;
    
            size = 0;
            file.read((char*)&size,sizeof(int));
            tmp = new char[size+1];
            file.read(tmp,size+1);
            Data.GameStage = tmp;
            delete tmp;
            return HR_Correct;
    
    
            size = 0;
            file.read((char*)&size,sizeof(int));
            tmp = new char[size+1];
            file.read(tmp,size+1);
            Data.PlotSubStage = tmp;
            delete tmp;
            return HR_Correct;
    
            size = 0;
            file.read((char*)&size,sizeof(int));
            tmp = new char[size+1];
            file.read(tmp,size+1);
            Data.GameSubStage = tmp;
            delete tmp;
            return HR_Correct;
        }
    
        cout << "Savegame : " << FileName << "not found" << endl;
        return CheckHR(HR_Invalid_Arg);
    }
    

    不过,我还有最后一个问题。如果用户试图将他的存档从一台 32 位计算机转移到 64 位计算机,我会遇到什么问题? Endianess 可能不是问题,因为该游戏仅适用于 Windows。 无论如何,谢谢,你为我节省了很多时间。

    【讨论】:

    • 处理不同位数时应准备的问题:如果将数据转储为二进制结构,请确保结构具有相同的内存布局。一般来说,确保二进制布局和支持的范围是相同的;例如DWORD_PTR 会有不同的大小。作为替代方案,您可以考虑使用与位数无关的文件格式(如 XML)。
    • 谢谢,我会考虑的。我试图让它保持二进制,但如果有必要,可能会将它放在 XML 中
    • XML 不一定更好,并且也有一些陷阱。写入字符串数据需要转义特殊字符(如&gt;)并可能将 UTF-16 重新编码为 UTF-8,流式浮点数要么涉及舍入(有舍入错误)或转换为可序列化的形式(十六进制字符串二进制数据,例如)。 XML 在调试期间将是一个有价值的工具。您可以使用任何文本编辑器阅读和修改它。这也使作弊变得容易,因此必须实施额外的安全措施。
    猜你喜欢
    • 2021-11-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多