如何判断某个东西是堆分配的还是堆栈分配的?

Posted

技术标签:

【中文标题】如何判断某个东西是堆分配的还是堆栈分配的?【英文标题】:How to tell if something is heap or stack allocated? 【发布时间】:2015-07-21 08:08:53 【问题描述】:

我想知道是否有办法确定变量是堆栈分配的还是堆分配的。

考虑一下:

struct SomeStruct;

fn main() 
    let some_thing = Box::new(SomeStruct);
    println!(":p", some_thing);
    foo(&*some_thing);


fn foo (bar: &SomeStruct) 
    println!(":p", bar);

打印

0x1
0x1

然后

struct SomeStruct;

fn main() 
    let some_thing = &SomeStruct;
    println!(":p", some_thing);
    foo(some_thing);


fn foo (bar: &SomeStruct) 
    println!(":p", bar);

打印

0x10694dcc0
0x10694dcc0

我可以看到堆分配版本的内存地址要短得多,但我不知道这是否是区分差异的可靠方法。不知道有没有类似std::foo::is_heap_allocated()

【问题讨论】:

首先想到的问题是“为什么?”。有了这些知识,什么样的代码会有不同的工作方式? 我不需要这段代码来比更多地发现语言:) 0x1 是 Rust 分配器为零大小对象返回的虚拟地址,它不在堆上。见heap.rs#L90。 我也有同样的问题。我想知道,因为我感兴趣的是这件事是如何在幕后工作的。例如,这个简单的例子似乎显示了几乎连续地址 play.rust-lang.org/… 中的所有内容。我现在想知道是否所有对堆的引用都占用了堆栈空间,它给了我对堆的堆栈引用的地址。 @MrMesees 所有地址都是连续的,因为您只获取(和打印)堆栈地址:numdog 在堆栈上,zoo 是一个数组,因此它存在堆栈,虽然Vec 的“存储缓冲区”是堆分配的,但您只打印 Vec 结构本身的地址(指针、长度和容量的三元组),并且在堆栈上. 【参考方案1】:

如果您在某个 POSIX 系统上,您可能可以使用带有 0 参数的 sbrk() 系统调用来确定程序中断的当前位置,即堆的当前限制。如果给定值的地址小于此地址但大于堆的开头,则它在堆上。我不知道你会如何检查它是否在堆栈上,这不一定会自动选择不在堆上,因为它也可以是静态初始化或未初始化的数据,尽管这可能很明显您在检查代码时。您可能可以在 x86_64 架构上使用rbp 寄存器,它应该指向当前堆栈帧的开头。如果您想检查它是否在当前堆栈帧上,或者如果您想检查它是否在堆栈上的任何位置,您可以使用rsp

我认为您可以使用 end() 系统调用使用 end 参数来启动堆。所以堆的下限是end(end)的结果,上限是sbrk(0)

【讨论】:

大多数现代 POSIX 系统(/分配器)使用mmap 而不是(s)brk 来获取堆空间。 (s)brk 在 macOS 上被正式弃用(将触发警告),并且在大多数 BSD 上都被非正式地弃用(它们被称为“历史奇闻”)。我认为正确的方法是获取当前堆栈指针 stack base (pthread_attr_getstackaddr),并检查您的指针是否介于两者之间。

以上是关于如何判断某个东西是堆分配的还是堆栈分配的?的主要内容,如果未能解决你的问题,请参考以下文章

结构总是堆栈分配还是有时堆分配?

结构总是堆栈分配还是有时堆分配?

删除、释放还是解除分配?

分配的变量引用在哪里,在堆栈中还是在堆中?

是应该在堆栈还是堆上分配pthread函数参数?

如何判断一个 fortran 数组指针是直接分配的,还是与另一个对象相关联?