进入函数时堆栈溢出

stack overflow on stepping into a function

当我使用 VS 2015 在调试模式下进入函数时遇到堆栈溢出错误。这是准确的消息,以防万一:

Unhandled exception at 0x0000000140D9F018 in TestProgram.exe: 0xC00000FD: Stack overflow (parameters: 0x0000000000000001, 0x0000000000213000).

我输入的函数如下所示:

void CGUITaskRequest::DecodeAndDeserializeSettings(const std::string& sEncodedSettingsString)
{
    auto sDecoded = string_functions::base64_decode(sEncodedSettingsString);
    std::stringstream ss(sDecoded);

    if (m_eType == ETaskTypes::FILE && m_eSubtype == ETaskSubtypes::OPEN) {
        auto pSettings = std::make_shared<CModSettingsFileImport>();
        cereal::XMLInputArchive arSettingsObject(ss, pSettings->XML_TAG);   
        pSettings->load(arSettingsObject);
        m_ptrSettings = pSettings;
    }
    else if (m_eType == ETaskTypes::META && m_eSubtype == ETaskSubtypes::STATUS) {
        auto pSettings = std::make_shared<SMetaStatusSettings>();                   
        cereal::XMLInputArchive arSettingsObject(ss, pSettings->XML_TAG); // <- line I comment out to run successfully  
        pSettings->load(arSettingsObject);
        m_ptrSettings = pSettings;
    }
}

这是让我困惑的地方:

  1. 单步执行函数,所以我认为不会有任何递归进行。当我收到堆栈溢出错误时,它位于函数的左括号中 - none 甚至调用了函数的行。
  2. 当我在 'else if' 部分注释掉这一行时: cereal::XMLInputArchive arSettingsObject(ss, pSettings->XML_TAG); 然后不仅堆栈溢出错误消失了,而且函数 运行s 也如预期的那样,成功执行了 `if' 块(所以它成功地创建了一个 'cereal::XMLInputArchive',从字符串流加载它,等等) .

在这两种情况下(当函数 运行s 时,以及当它导致堆栈溢出时),都会使用相同的输入参数调用它(大约 300 个 base64 编码的字符 xml)。

所以,不知何故,当我在所有代码未注释的情况下进行编译时,我导致函数的 execution/memory 分配出现问题,但我不明白是什么。

哦,是的,以防万一,当我收到堆栈溢出错误时,调用堆栈将其置于顶部:

TestProgram.exe!__chkstk()

除此之外,它看起来与函数 运行 成功时相同(这也让我认为没有递归)。

[编辑]

搜索 __chkstk() 后,我只是 found/read 这篇 SO 文章: What is the purpose of the _chkstk() function?

这让我觉得这不是传统的堆栈溢出错误,我要求的内存太多,而是函数中的某些内容试图引用内存中的非法位置,这导致 VS报告堆栈溢出。但是,我仍然不确定 why/how 如果该函数甚至没有执行是否会发生这种情况,因为该块不会 运行.

提前感谢您提供有关可能导致此类行为的原因的任何见解。

我有一种不好的预感,我错过了关于函数调用的一些基本知识。

结果是 _chkstk() throws a stack overflow when you have exceeded the declared maximum stack size declared in an .exe build. Your solution then? Increase it。尽管也考虑删除代码中的冗余位:

首先,考虑到在进入函数时,我们需要确保堆栈上有足够的space用于您的函数的所有局部变量。让我们看看它们是什么:

void CGUITaskRequest::DecodeAndDeserializeSettings(const std::string& sEncodedSettingsString)
{
  /* two variables here */
  auto sDecoded = string_functions::base64_decode(sEncodedSettingsString);
  std::stringstream ss(sDecoded);

  if (m_eType == ETaskTypes::FILE && m_eSubtype == ETaskSubtypes::OPEN) {
    /* two more variables here */
    auto pSettings = std::make_shared<CModSettingsFileImport>();
    cereal::XMLInputArchive arSettingsObject(ss, pSettings->XML_TAG);   
    pSettings->load(arSettingsObject);
    m_ptrSettings = pSettings;
  }
  else if (m_eType == ETaskTypes::META && m_eSubtype == ETaskSubtypes::STATUS) {
    /* and a final pair */
    auto pSettings = std::make_shared<SMetaStatusSettings>();                   
    cereal::XMLInputArchive arSettingsObject(ss, pSettings->XML_TAG); // <- line I comment out to run successfully  
    pSettings->load(arSettingsObject);
    m_ptrSettings = pSettings;
  }
}

现在假设您在调用堆栈上找到了 _chkstk() 变量。这意味着这个函数 allocates a lot of memory!注释掉单个声明可以解决问题,指向贪婪的内存罪魁祸首。但是等等,你有两个,你可以摆脱一个意味着合并你重复的声明可能会带来好处:

void CGUITaskRequest::DecodeAndDeserializeSettings(const std::string& sEncodedSettingsString)
{
  auto sDecoded = string_functions::base64_decode(sEncodedSettingsString);
  std::stringstream ss(sDecoded);
  /* single declaration*/
  cereal::XMLInputArchive arSettingsObject;
  if (m_eType == ETaskTypes::FILE && m_eSubtype == ETaskSubtypes::OPEN) {
    auto pSettings = std::make_shared<CModSettingsFileImport>();
    arSettingsObject = cereal::XMLInputArchive(ss, pSettings->XML_TAG);   
    pSettings->load(arSettingsObject);
    m_ptrSettings = pSettings;
  }
  else if (m_eType == ETaskTypes::META && m_eSubtype == ETaskSubtypes::STATUS) {
    auto pSettings = std::make_shared<SMetaStatusSettings>();                   
    arSettingsObject = cereal::XMLInputArchive(ss, pSettings->XML_TAG); // <- line I comment out to run successfully  
    pSettings->load(arSettingsObject);
    m_ptrSettings = pSettings;
  }
}

虽然这改变了 arSettingsObject 的作用域,但这不是问题,因为函数在 if/else 语句之后终止,并且声明它的所有 return 路径都需要它.