【问题标题】:Can I get the limits of the stack in C / C++?我可以在 C/C++ 中获得堆栈的限制吗?
【发布时间】:2015-04-26 19:24:54
【问题描述】:

我的问题非常简单明了:如果我有例如1MB RAM分配给程序的栈,我能得到开始和结束的地址,还是开始和长度的地址?

我正在使用 Visual Studio 2013。

【问题讨论】:

标签: c++ c visual-c++


【解决方案1】:

您应该质疑您对堆栈布局的假设。

也许the stack doesn't have just one top and bottom

也许it has no fixed bottom at all

显然没有可移植的方式来查询不可移植的概念。

不过,在 Visual C++ 中,您可以使用 Win32 API,具体取决于 Windows 版本。

在 Windows 8 上非常简单,只需拨打GetCurrentThreadStackLimits

早期版本需要使用VirtualQueryEx 并对结果进行一些处理。在堆栈中获取一个地址很容易,只需在局部变量上使用&。然后,您需要找到包含该地址的保留区域的限制。 Joe Duffy 写了a blog post showing the details of finding the bottom address of the stack

【讨论】:

  • 正如Thread Stack Size 所说,default 初始和保留大小可以从 PE 标头中获得(它没有指定 哪个模块的 标头) .
  • @ivan_pozdeev:这只是主线程的堆栈大小,虽然它也用作稍后创建的线程的默认值,但它们的大小是动态设置的。当然,它是可执行映像的 PE 头,与 GetModuleHandle(NULL) 匹配的那个。
  • 您认为 Visual C++ == Win32 不会正确的假设 much longer
  • @Damien_The_Unbeliever:该页面以“Visual C++”的名称快速播放。是的,Visual Studio 开发环境可能针对其他平台,甚至是其他平台上的 C++ 本机开发,但它是通过 Visual C++ 以外的编译器的工具链插件完成的,而不是通过将其他平台添加到 Visual C++ 工具链。使用 Windows Store API 会是一个更好的论据,它不是 Win32,而是使用 Visual C++ 工具链完成的。
  • @BenVoigt:PE 标头中指定的 默认 堆栈大小不会总是 应用于辅助线程。调用者决定在调用CreateThread()/_beginthread/ex()时。
【解决方案2】:

GetCurrentThreadStackLimits 似乎在做你正在寻找的事情,将堆栈的下/上边界放入指针地址:

ULONG_PTR lowLimit;
ULONG_PTR highLimit;
GetCurrentThreadStackLimits(&lowLimit, &highLimit);

看起来它只在 Windows 8 和 Server 2012 上可用。

查看MSDN

【讨论】:

    【解决方案3】:

    在 8 之前的 Windows 上,自己实现 GetCurrentThreadStackLimits():

    #include <windows.h>
    #if _WIN32_WINNT < 0x0602
    VOID WINAPI GetCurrentThreadStackLimits(LPVOID *StackLimit, LPVOID *StackBase)
    {
        NT_TIB *tib = (NT_TIB *) NtCurrentTeb();
        *StackLimit = tib->StackLimit;
        *StackBase = tib->StackBase;
    }
    #endif
    

    【讨论】:

    • 来自我在回答中链接的 Joe Duffy 文章:不幸的是,StackLimit 仅在您实际触摸堆栈上的页面时才会更新,因此 这不是一种可靠的方式找出还有多少未提交的堆栈。 CLR 使用 kernel32!VirtualAlloc 提交页面,而不是通过实际移动保护页面,因此 StackLimit 不会像您预期的那样更新。 但是如果您阅读下面的 cmets在回答之前的问题,您就已经知道 TIB 不包含所需的信息。
    • @BenVoigt TIB 确实包含该信息。 GetCurrentThreadStackLimits 返回的“低”值是未记录的 DeallocationStack 字段,位于 FS:[0xE0C] (x86) / GS:[0x1478] (x64)。至少从 Windows 2000 开始。因此,可以在 Win8 之前的操作系统上使用未记录的字段,并在 Win8+ 上使用 GetCurrentThreadStackLimits。
    • @Alex:也许,但这个答案引用了StackLimit 字段,它给出了错误的结果。
    • @BenVoigt 那么,应该有人编辑答案吗?这是一个有效的答案(考虑到“修复”)——当然比公认的答案建议的更好(通过VirtualQuery 调用内核)。我没有足够的 MS C++ 经验来正确编写它。
    • @Alex:我仍然坚持我的回答......它在所有平台上都能正常工作,而将未记录的字段从 TIB 中拉出需要每个平台的代码。
    猜你喜欢
    • 2019-03-14
    • 2012-06-27
    • 1970-01-01
    • 2012-11-18
    • 1970-01-01
    • 1970-01-01
    • 2021-06-25
    • 1970-01-01
    相关资源
    最近更新 更多