【问题标题】:stack overflow on stepping into a function进入函数时堆栈溢出
【发布时间】:2019-01-23 22:23:02
【问题描述】:

当我使用 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-&gt;XML_TAG); 那么不仅堆栈溢出错误消失了,而且函数按预期运行,成功执行了 `if' 块(因此它成功创建了一个 'cereal::XMLInputArchive',从 stringstream 加载它等)。

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

所以,不知何故,当我编译所有未注释的代码时,我会导致函数的执行/内存分配出现问题,但我不明白是什么。

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

TestProgram.exe!__chkstk()

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

[编辑]

搜索 __chkstk() 后,我刚刚找到/阅读了这篇 SO 文章: What is the purpose of the _chkstk() function?

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

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

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

【问题讨论】:

  • 在将函数的参数压入堆栈时可能会发生堆栈溢出,但在这种情况下,传递的只是一个引用(指针)。
  • 遇到崩溃时调用堆栈有多高?
  • sizeof(cereal::XMLInputArchive) 是什么?如果我没记错的话,如果函数有超过 4K 的局部变量,__chkstk 可能会失败。
  • @Axalo - 调用堆栈中有 8 个项目 - 这个函数是第 8 个,__chkstk 是第 9 个。 Igor Tandetnik - sizeof(cereal::XMLInputArchive) 产生 66088 -
  • @Igor Tandetnik - sizeof(cereal::XMLInputArchive) 产生“66088”,我猜是字节。鉴于我的函数运行正常,我猜我的最大堆栈大小必须> 4k,但

标签: c++ function stack-overflow


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

【讨论】:

  • 非常感谢!我使用了您的解决方案 - 除了谷物::XMLInputArchive 没有默认构造函数。所以我用一个 std::unique_ptr 代替,然后在必要时创建指针。根据 Igor Tandetnik 的评论,我做了“sizeof”,这个变量一定是罪魁祸首。再次感谢!
  • 我在发表最后一条评论后意识到,我的解决方案不仅解决了@OrderNChaos 建议的问题(通过减少声明的数量),而且还将内存分配移动到堆中,因为我有使用指针。不管怎样,它解决了这个问题。
猜你喜欢
  • 1970-01-01
  • 2019-05-18
  • 2018-05-28
  • 2017-10-20
  • 2019-08-07
  • 2019-12-01
  • 1970-01-01
  • 2020-08-20
相关资源
最近更新 更多