如果类在堆上,函数会存储在堆还是栈上?

6
class Foo{

    public:
        void foo(){
            int x, y; // go to the HEAP or the stack?
        }
};

int main(){
    Foo *f = new Foo();
    f -> foo();

    delete f;
}

如果我在堆上创建一个类实例并激活一个“类方法”,函数局部变量和“元数据”存储在哪里?它们存储在堆上,因为类在堆上或者栈上都可以。

1
据我所知,大多数编译器将函数与类实例分开存储,因此它们不关心您的对象位于何处。 - UnholySheep
3
非正式评论:栈上的情况。把 void Foo::foo() 看作是几乎等同于 void ::foo(Foo* obj) - Bathsheba
这是一个相对复杂的问题。如果您打开优化,xy就不会存在了,因为编译器很可能会优化它们的存在。通常情况下,它可能会将它们存储在_寄存器_或堆栈中。 - Daniel Langr
顺便问一下,“metadata”函数是什么? - Daniel Langr
@DanielLangr 元数据不是函数,它是函数的数据,例如返回值和所有其他幕后的东西。 - dsal3389
3个回答

8
如果类在堆上,函数是存储在堆还是栈上?
C++语言没有"堆"或者"栈"这样的东西。
自动变量具有自动存储。动态对象具有动态存储。静态变量具有静态存储。线程本地变量具有线程本地存储。
函数根本没有存储。只有对象(有时候是引用)才有。
关于使用von Neumann架构的C++语言的典型实现:程序指令和堆栈分开存储。所以,对于这个问题的答案是:都不是。
函数内部的局部变量存储在哪里?
自动变量,即那些在函数块内部局部且不为静态变量的变量,例如fxy,存储在自动存储中。
在典型的实现中,自动存储通过将对象存储在执行堆栈上、CPU寄存器中或者不存储(如果它们的存储不可观测)来实现。

1
但我知道如果使用new关键字,数据将存储在堆中,那么“C++语言没有堆或栈”是什么意思? - dsal3389
@dsal3389 new 创建具有动态存储期的对象。虽然关于它的部分标题为“自由存储区”,但C++标准并未描述动态对象的内存来自何处,这是同堆栈相似的。一些人不建议将“堆”用作此未使用内存的名称,因为与数据结构称为堆的名称冲突,而该数据结构与之无关。 - eerorika

1

你的标题有些误导,因为"函数"与它们的局部变量是不同的概念。

对于你的问题来说,技术上的正确答案是"这要看情况",你可以查看eerorika的回答以获取标准规范的细节。问题在于,有许多特定的场景,堆内存和栈内存的概念并不容易定义,例如当你在堆中分配内存并将其赋值为特定线程的栈区域时。

如果你在电脑上运行你的示例程序,则Foo实例(由f指向)分配在堆上(还有任何成员变量,在你的示例中没有)。fxy局部变量位于栈中。

如果你正在使用Linux平台,我建议你使用类似GDB的调试器运行你的程序,并评估所有这些变量的地址。

p &f
p f
p &x
p &y

然后通过查看/proc/1234/maps文件,其中1234是您程序的进程ID,寻找它们所属的内存区域。您也可以向gdb询问。您会注意到,在这些区域中,有一些包含可执行文件和几个共享库文件的区域。那将是程序指令所在的区域,也就是函数实际位于的内存(既不是堆栈的一部分,也不是堆的一部分)。

0
机器指令和数据被分开放置(例如查看维基百科上的代码段,或搜索应用程序内存布局)。尽管您可以使用C++混合代码和数据,但编译器稍后会自动分离它们(在C中,您无法混合数据和代码)。
调用非内联函数时,将创建堆栈帧并将其推送到堆栈上(LIFO!),然后处理器跳转到函数的代码部分。但是,如果堆栈太大,则会出现堆栈溢出异常(就像此网站的名称一样:D),因为在C++中,堆栈是有限的。在函数结束后,堆栈指针被移动,并且旧帧(在当前帧之前推送的)处于活动状态(当前位于顶部)。您可以这样想象它,它是为了以高效安全的方式模拟进入子程序(通常是执行另一个子程序)(除非堆栈没有超过:D)。
因此,函数/类方法的所有输入参数和所有本地变量都放置在堆栈上。堆栈帧的大小可以在编译时计算(在启动程序之前,根据变量及其大小计算)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接