c++ - 对于具有相同调用堆栈的同一对象,从同一线程调用两次函数

标签 c++ visual-studio debugging

背景

我正在调试一个开源项目,我愿意加入并学习 C++。然后我尝试扩展那里的一个问题所需的一些功能,我偶然发现了有趣的案例(我一生中从未见过这样的事情)。

源代码

QuestSet Player::GetQuestForEvent(uint16 eventId) const
{
    QuestSet eventQuests; // QuestSet is typedef for std::set<uint32>

    for (uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i)
    {
        uint32 questId = GetQuestSlotQuestId(i);
        if (questId == 0)
            continue;

        QuestStatusMap::const_iterator qs_itr = m_QuestStatus.find(questId);
        if (qs_itr == m_QuestStatus.end())
            continue;

        QuestStatusData const& qs = qs_itr->second;

        Quest const* qinfo = sObjectMgr->GetQuestTemplate(questId);
        if (!qinfo)
            continue;

        if (qinfo->GetEventIdForQuest() == eventId)
            eventQuests.insert(questId);
    }

    return eventQuests; <--- breakpoint here
}

我注意到我的服务器崩溃了

Unhandled exception at 0x00007FFE1BE6A388 (KernelBase.dll) in worldserver.exe: 0xC0000005: Access violation.

后面是我服务器上的日志

C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.20.27508\include\xtree(240) : Assertion failed: map/set iterators in range are from different containers

所以我在上述位置放置了断点,我注意到该函数被调用了两次。我知道上传图片可能不是最好的选择,但在我看来,这是在这种情况下最好的解释,所以请看一下。

屏幕截图

  • 第一个断点命中 first breakpoint hit

  • 第二个断点命中(诊断工具窗口中的通知事件窗口) second breakpoint hit

这是项目中调用此函数的唯一位置。

void OnLogin(Player* player, bool /*firstLogin*/) override
{
    QuestSet eventQuests = player->GetQuestForEvent(1);
    /*std::for_each(eventQuests.begin(), eventQuests.end(), [&player](uint32 questId)
    {
        player->AbandonQuest(questId);
    });*/
}

我知道这听起来很愚蠢,但我没有建议,您能否指出我正确的方向,理解为什么这个断点被击中两次很可能也能修复异常。

疑难解答:

  • 项目是在调试配置中构建的
  • 两次命中的调用堆栈、线程和 this 对象都是相同的。

最佳答案

很可能该函数实际上并未被调用两次。您只是因为两次击中同一个断点而得出了错误的结论。

Visual Studio 允许您检查断点附近的汇编代码,默认为 Ctrl+Alt+D我相信。在那里,您将看到汇编指令(您的 CPU 实际执行的操作)和“以下指令源自哪一行代码”的调试信息。

我希望您会看到 return eventQuests; line 与两个汇编指令 block 相关联,其间还有另一个指令 block 。您将首先命中第一组指令中的断点,然后执行第二组指令(例如与循环或右大括号关联),然后由于第二组指令再次命中断点。是的,这听起来很愚蠢,但是 MSVC 输出的调试信息并不是我见过的最好的。

另一种简单的检查方法是在返回之前插入一条打印语句(如 std::cout << "Hi" << std::endl ),并检查是否得到一个或两个打印输出。除非你得到两个打印输出,否则你多次实际输入该函数的结论是错误的。

关于c++ - 对于具有相同调用堆栈的同一对象,从同一线程调用两次函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56043266/

相关文章:

android - 超时后CPP调用函数

c++ - 使用堆栈跟踪检测功能

php - 尽管付款转帐成功,Paypal 仍显示错误消息

c++ - 使用 std::map::at(const char*) 超出范围,使用/02 编译时除外

c++ - 我们如何并行运行算法的 n 个实例并以有效的方式计算结果函数的平均值?

c++ - 由于某些 python 错误,arm-none-eabi-gdb 无法启动

c# - Git和TFS版本控制团队合作

c# - 如何在 C# 中使用本地数据库?

c# - 正则表达式嵌套括号在一种情况下不起作用

ios - 安装发布和调试 iOS