C++中的指针存储在堆栈还是堆中?

35
我试图理解内存之间的区别,这个问题在SO上以及这个解释都很好地解释了基础知识。
然而,在第二个解释中,我遇到了一个例子,其中有一个具体问题,该例子如下: heap allocation example 解释说对象m是在上分配的,我只是想知道这是否就是全部。根据我的理解,对象本身确实是在上分配的,因为使用了new关键字进行其实例化。
然而,对象m的指针同时也在栈上分配,否则,位于中的对象本身将无法访问。我觉得出于完整性的考虑,在这个教程中应该提到这一点,忽略这一点会让我有些困惑,所以我希望有人能澄清这一点并告诉我,我的理解是正确的,即这个例子应该基本上有两个语句,分别是: 1.对象m的指针已经在栈中分配了 2.对象m本身(因此它所携带的数据以及访问其方法)已经在堆中分配了

3
是的,你的理解是正确的。指针在栈上分配,它所指向的对象在堆上分配。 - Mohit Jain
指针是一个对象。对象的位置通常取决于其生命周期;具有静态生命周期的对象位于全局内存中,具有动态生命周期的对象位于堆上,具有自动生命周期或临时生命周期的对象位于栈上,异常在其他一些特殊位置等等。像栈或堆这样的词只是约定俗成的方式,用于指定生命周期,并不是内存中的某个特定位置。 - James Kanze
4个回答

38
你的理解可能是正确的,但是陈述是错误的:

在堆栈上分配了一个指向对象m的指针。

m是指针,它在栈上。也许你指的是指向Member对象的指针。

对象m本身(它所携带的数据以及访问其方法的方式)已在堆上分配。

正确的说法应该是m指向的对象是在堆上创建的

通常,任何函数/方法本地对象和函数参数都是在堆栈上创建的。由于m是一个函数本地对象,因此它在堆栈上,但是被m指向的对象在堆上。


26
“堆”和“栈”是一般编程术语。特别地,没有存储需要通过堆或栈数据结构进行内部管理。
C ++具有以下存储类:
- 静态 - 自动 - 动态 - 线程
大致而言,“动态”对应于“堆”,而“自动”对应于“栈”。
针对您的问题:指针可以在这四个存储类中的任何一个中创建;被指向的对象也可以在这些存储类中的任何一个中。下面是一些例子:
void func()
{
    int *p = new int;            // automatic pointer to dynamic object
    int q;                       // automatic object
    int *r = &q;                 // automatic pointer to automatic object
    static int *s = p;           // static pointer to dynamic object
    static int *s = r;           // static pointer to automatic object (bad idea)
    thread_local int **t = &s;   // thread pointer to static object 
}

如果在函数内部,则未指定说明符声明的命名变量是 自动变量,否则为 静态变量


1
+1 对于“Moving onto your question”之前的所有内容,我认为这些额外的细节更重要,因为问题本身是一个真理。 - underscore_d
1
为什么“指向自动对象的静态指针”是不好的?是因为当函数结束时,自动对象将被释放,但静态指针将继续存在吗?此外,如果动态变量存储在堆上,自动变量存储在栈上,那么静态变量存储在哪里?这个答案真的帮了我很多,谢谢。 - Jay S.
2
@JayS。是的,这很糟糕,因为静态指针将指向一个在函数第一次返回后不再存在的对象。"事物存储的位置"是实现细节,超出了本答案的范围。 - M.M

5
当你在函数中声明一个变量时,它总是存储在堆栈上。所以你的变量Member* m被创建在堆栈上。请注意,仅凭自身,m只是一个指针;它不指向任何东西。您可以使用它指向堆栈或堆上的对象,或者什么都不指向。

在类或结构体中声明变量是不同的--那些会出现在类或结构体实例化的位置。

要在堆上创建东西,您使用newstd::malloc(或其变体)。在您的示例中,您使用new在堆上创建一个对象,并将其地址赋给m。堆上的对象需要释放以避免内存泄漏。如果使用new分配,则需要使用delete;如果使用std::malloc分配,则需要使用std::free。更好的方法通常是使用"智能指针",它是一个包含指针并具有释放它的析构函数的对象。


1
在函数中声明为静态的变量会被放置在堆栈上吗?考虑到它具有静态分配,这是不可能的,对吧? - Abdel Aleem
2
正确的、典型的基于堆栈的C++实现不会将静态变量存储在堆栈上。静态变量存储在内存的一个单独区域中,与全局变量一起。因此,大多数程序至少有三个数据内存区域:堆栈、堆和全局变量。实际上,通常还有更多的内存区域,但您暂时可以忽略它们。 - user3553031

2

是的,指针在堆栈上分配,但指针所指向的对象在堆上分配。你是正确的。

然而,难道指向对象 m 的指针不是同时在堆栈上分配的吗?

我想你是指 Member 对象。指针在堆栈上分配,并将在整个函数(或其作用域)的持续时间内保留在那里。之后,代码可能仍然有效:

#include <iostream>
using namespace std;

struct Object {
    int somedata;
};

Object** globalPtrToPtr; // This is into another area called 
                         // "data segment", could be heap or stack

void function() {
    Object* pointerOnTheStack = new Object;
    globalPtrToPtr = &pointerOnTheStack;
    cout << "*globalPtrToPtr = " << *globalPtrToPtr << endl;
} // pointerOnTheStack is NO LONGER valid after the function exits

int main() {
     // This can give an access violation,
     // a different value after the pointer destruction
     // or even the same value as before, randomly - Undefined Behavior
    cout << "*globalPtrToPtr = " << *globalPtrToPtr << endl;
    return 0;
}

http://ideone.com/BwUVgm

以上代码存储了一个指针的地址,该指针驻留在堆栈上(并且会泄露内存,因为它没有使用delete释放Object分配的内存)。

由于在退出函数后指针被“销毁”(即其内存可以用于程序需要的任何用途),您不能再安全地访问它

以上程序可能会正常运行、崩溃或给出不同的结果。访问已释放或取消分配的内存称为未定义行为


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