21

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

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

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

4

6 回答 6

13

不,一般不会。

你知道 gcc-fsplit-stack吗?

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

于 2012-12-05T09:43:41.340 回答
9

如果您正在研究将堆栈存储在比堆更大的地址上的体系结构,则可以将变量地址与堆栈底部进行比较。使用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...按预期打印。

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

于 2012-12-05T09:17:05.810 回答
3

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

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

#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我正在输入的机器上的输出。

于 2013-02-22T12:30:35.020 回答
2

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

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

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

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

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

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

于 2012-12-05T09:59:46.023 回答
1

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

于 2012-12-05T09:13:52.437 回答
0

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

于 2013-01-03T20:58:46.973 回答