C语言 如何发现一个变量是在栈中还是堆中分配的?

tcbh2hod  于 2022-12-03  发布在  其他
关注(0)|答案(7)|浏览(272)

在某个地方偶然发现了这个面试问题,
在C语言中,给定一个变量x,你如何知道这个变量的空间是在栈还是堆上分配的?
(Is有什么方法可以通过编程找到它,而不必通过符号表等?以及找到空间是在堆栈还是堆中分配有任何实际意义吗?)

oaxa6hgo

oaxa6hgo1#

"不,不是一般的"
你知道gcc -fsplit-stack吗?
由实现来决定是分配连续堆栈还是块与内存中的堆块交叉的堆栈。当堆被拆分时,请好运地找出块是分配给堆还是分配给堆栈。

jaql4c8m

jaql4c8m2#

如果您正在使用的架构将堆栈存储在比堆更大的地址上,则可以将变量地址与堆栈底部进行比较。使用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 线程的堆栈中检测存储。要做到这一点,代码需要跟踪所有创建的线程。

cbeh67ev

cbeh67ev3#

任何标准都不能保证这一点,但
在大多数平台上,堆栈从可用的最高地址向下增长,堆从底部向上增长。如果地址的最高有效字节位于平台可用内存空间的上半部分,并且您尚未分配GB内存,则它很可能位于堆栈上。

#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

3zwtqj6y

3zwtqj6y4#

这可能是一个陷阱问题。变量有自动或静态存储持续时间[*]。你可以相当有把握地说自动变量是“在堆栈上”分配的,至少假设它们没有优化到寄存器中。标准并不要求有“堆栈”,但是一个符合规范的C实现必须维护一个调用栈并将自动变量与调用栈的层次相关联,你几乎可以称之为“堆栈”。
具有静态存储持续时间的变量通常驻留在一个或多个数据节中。从操作系统的视点来看,数据节可能在程序启动之前从堆中分配,但从程序的视点来看,它们与“自由存储”没有关系。
通过检查源代码中变量的定义,可以知道变量的存储持续时间--如果它在函数作用域中,那么它是自动的,除非标记为static;如果它不在函数作用域中,那么不管它是否标记为static,它都有静态的持续时间(因为static关键字在这里的含义不同)。
没有一种可移植的方法可以根据变量的地址来判断变量的存储持续时间,但特定的实现可能会提供这样做的方法,或者提供一些工具,让您可以以更高或更低的可靠性来猜测变量的存储持续时间。
对象也可以有动态存储持续时间(通常是“在堆上分配”的意思),但这样的对象 * 不是变量 *,所以如果有变量的话,这将是一个诀窍。
[*]或C11和C++11中的线程局部。

jbose2ul

jbose2ul5#

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

anauzrmj

anauzrmj6#

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

6ljaweal

6ljaweal7#

C++中跟踪内存分配SPECIFICALY ON THE HEAP的一种可能的方法是重载new运算符来跟踪它。这样你就知道如果内存不是在堆上分配的,那么它必须在堆栈上分配。

#include <iostream>
#include <string>

// Variable to track how many allocations has been done.
static uint32_t s_alloc_count = 0;

void* operator new(size_t size){
    s_alloc_count++;
    std::cout << "Allocating " << size << " bytes\n";
    return malloc(size);
}

跟踪变量s_alloc_count,你应该能够看到堆上已经完成了多少分配,并将这些分配的大小打印在控制台上。使用调试工具(如断点),“逐步”运行代码和控制台日志记录是跟踪这些分配的一种方法。这不是一种自动化的方法,但却是一种实现自动化的方法。

OBS:此提示应仅用于测试,避免在生产代码中使用此类型的代码。

相关问题