【问题标题】:How to find if a variable is allocated in stack or heap?如何查找变量是在堆栈还是堆中分配?
【发布时间】:2012-11-23 01:29:39
【问题描述】:

在某处偶然发现了这个面试问题,

在 C 中, 给定一个变量x,如何知道该变量的空间是在栈上还是堆上分配的?

(有没有什么方法可以通过编程方式找到它而不必通过符号表等?找到空间是在堆栈还是堆中分配是否有任何实际意义?)

【问题讨论】:

  • 查看汇编代码..你可以在那里得到它
  • 他还提过什么吗?比如架构、编译器、操作系统?否则我会拒绝。
  • 不,这是唯一给出的信息。
  • 我认为没有便携式解决方案。两种语言都没有堆栈或堆之类的东西,所以问题更多的是关于语言的给定实现。例如,如果您正在查看 GCC,它有大量与 malloc 相关的实用程序可能会有所帮助。
  • 他们通常不是在“正确答案”之后——他们通常是在展示堆、堆栈、调用约定、局部变量所在的位置以及编译器优化等知识之后,方向堆栈增长,不同架构如何管理堆和堆栈等。这是一个相当开放的问题,可以让面试官很好地了解某人知道什么。

标签: c++ c heap-memory stack-memory


【解决方案1】:

不,不是一般的。

你知道 gcc -fsplit-stack 吗?

由实现决定是分配一个连续的堆栈还是分配一个堆栈,其中块与内存中的堆块交错。祝你好运,当后者被拆分时,弄清楚是为堆分配了块还是为堆栈分配了块。

【讨论】:

  • 我想对于-fsplit-stack,您可以合理地回答“两者”作为自动变量。在栈上,栈在堆上。
  • @SteveJessop:啊!这应该让面试官思考:)
  • 一点用处都没有。我有一个函数destroy_something(something**s)。知道 *s 是否在堆或堆栈上以了解是否在 *s 上调用 free 将非常有帮助。
  • @GerardoZinno:这里更简单的解决方案是将销毁和释放分开;如果变量在栈上,释放是自动的,否则所有者必须释放它。
  • @MatthieuM 是的。我只是说,正如您在编辑之前所说的那样,拥有它并不是一个完全没用的功能:)
【解决方案2】:

任何标准都不能保证这一点,但是

在大多数平台上,堆栈从可用的最高地址向下增长,如果地址的最高有效字节位于平台可用内存空间的上半部分,并且您尚未分配,则堆从底部增长千兆字节的内存,这是一个很好的赌注,它在堆栈上。

#include <iostream>
#include <stdlib.h>
int main()
{
int x = 0;
int* y = new int;

unsigned int a1 = (int) &x;
unsigned int a2 = (int) y;

std::cout<<std::hex<<a1<<"  "<<a2<<std::endl;
}

在我正在输入的机器上输出ffbff474 21600

【讨论】:

  • 注意:Prolly 根本不适用于多线程应用程序。
【解决方案3】:

不,不可能通过内存位置来确定,编译器必须通过 isstack() 支持它才能移植。

【讨论】:

    【解决方案4】:

    这可能是一个棘手的问题。变量具有自动或静态存储持续时间[*]。您可以相当安全地说自动分配是“在堆栈上”,至少假设它们没有优化到寄存器中。存在“堆栈”不是标准的要求,但符合标准的 C 实现必须维护调用堆栈并将自动变量与调用堆栈的级别相关联。所以不管它实际做什么的细节,你几乎可以称之为“堆栈”。

    具有静态存储持续时间的变量通常位于一个或多个数据段中。从操作系统的 POV 来看,数据段可能在程序启动之前从堆中分配,但从程序的 POV 来看,它们与“free store”无关。

    您可以通过检查源中变量的定义来判断变量的存储持续时间——如果它在函数范围内,那么它是自动的,除非标记为static。如果它不在函数范围内,那么无论它是否标记为static,它都具有静态持续时间(因为static 关键字在那里的含义有所不同)。

    没有可移植的方法可以根据变量的地址来判断变量的存储持续时间,但特定的实现可能会提供执行此操作的方法,或者您可以使用的工具以或多或少的可靠性进行猜测。

    对象也可以具有动态存储持续时间(这通常是“在堆上分配”的意思),但这样的对象不是变量,所以如果有一个。

    [*] 或 C11 和 C++11 中的线程本地。

    【讨论】:

      【解决方案5】:

      如果您正在研究将堆栈存储在比堆更大的地址上的体系结构,则可以将变量地址与堆栈底部进行比较。使用pthread 线程API,这个比较看起来像这样:

      #include <stdio.h>
      #include <stdlib.h>
      #include <pthread.h>
      #include <inttypes.h>
      
      int is_stack(void *ptr)
      {
        pthread_t self = pthread_self();
        pthread_attr_t attr;
        void *stack;
        size_t stacksize;
        pthread_getattr_np(self, &attr);
        pthread_attr_getstack(&attr, &stack, &stacksize);
        return ((uintptr_t) ptr >= (uintptr_t) stack
                && (uintptr_t) ptr < (uintptr_t) stack + stacksize);
      }
      

      测试:

      int main()
      {
        int x;
        int *p1 = malloc(sizeof(int));
        int *p2 = &x;
      
        printf("%d %d\n", is_stack(p1), is_stack(p2));
        return 0;
      }
      

      ...按预期打印0 1

      上面的代码不会从other线程中的堆栈中检测存储。为此,代码需要跟踪所有创建的线程。

      【讨论】:

        【解决方案6】:

        我认为它没有解决方案。代码可以通过堆栈(堆)地址范围调整 var 的地址,但这不是一个确切的方式。代码最多只能在某些特定平台上运行。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-01-10
          • 1970-01-01
          • 2010-10-26
          • 2012-07-29
          • 2021-10-13
          • 2016-02-21
          • 2019-10-25
          相关资源
          最近更新 更多