进入函数时堆栈溢出

时间:2018-08-16 21:55:53

标签: c++ function stack-overflow

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

  

TestProgram.exe中0x0000000140D9F018的未处理异常:0xC00000FD:堆栈溢出(参数: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. 我正在逐步进入该函数,因此我认为不会进行任何递归。当我收到堆栈溢出错误时,它在函数的左括号中-函数的所有行都没有被调用。
  2. 当我在“ else if”部分中将此行注释掉时: cereal::XMLInputArchive arSettingsObject(ss, pSettings->XML_TAG); 那么不仅堆栈溢出错误消失了,而且函数按预期运行,成功执行了“ if”块(因此它成功创建了“ cereal :: XMLInputArchive”,并从字符串流中加载了它,等等)。

在两种情况下(函数运行时,并导致堆栈溢出),都将使用相同的输入参数(约300个字符的base64编码xml)对其进行调用。

因此,以某种方式,当我编译所有未注释的代码时,导致函数的执行/内存分配出了点问题,但是我不明白是什么。

哦,是的,如果有帮助,当我收到堆栈溢出错误时,调用堆栈会放在顶部:

  

TestProgram.exe!__ chkstk()

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

[编辑]

在搜索__chkstk()之后,我才发现/阅读了这篇SO文章: What is the purpose of the _chkstk() function?

这让我认为这不是传统的堆栈溢出错误,我在这里请求过多的内存,而是函数中的某些内容试图引用内存中的非法位置,这导致VS报告一个错误。堆栈溢出。但是,我仍然不确定,如果该函数甚至没有执行,为什么/如何发生这种情况,因为该代码块不会运行。

预先感谢您对导致这种行为的原因有任何了解。

我很难过,我缺少关于函数调用的一些基本知识。

1 个答案:

答案 0 :(得分:1)

结果是_chkstk() throws a stack overflow when you have exceeded the declared maximum stack size declared in an .exe build。那你的解决方案呢? Increase it。尽管也可以考虑删除代码中的冗余点:

首先,考虑到输入函数,我们需要确保堆栈上有足够的空间容纳函数的所有局部变量。让我们看看它们是什么:

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语句之后终止,并且所有声明它的返回路径都需要它。