web-dev-qa-db-ja.com

C ++のポインターは、スタックまたはヒープのどこに格納されますか?

stackheapメモリの違いを理解しようとしています。そして この質問 on SOと同様に この説明 は基本を説明するのにかなり良い仕事をしました。

ただし、2番目の説明では、特定の質問がある例に出会いました。例は次のとおりです。

heap allocation example

オブジェクトmheapに割り当てられると説明されていますが、これが完全なストーリーかどうか疑問に思っています。私の理解によると、newキーワードがインスタンス化に使用されているため、オブジェクト自体はheapに実際に割り当てられます。

ただし、オブジェクトmへのポインタが、スタック上に同時に割り当てられているではありませんか?それ以外の場合、heapにあるオブジェクト自体はどのようにアクセスされますか。完全を期すために、これはこのチュートリアルで言及されるべきでしたが、それを残しておくと少し混乱を招きます。基本的に、次の2つのステートメントが必要です。

1。オブジェクトmへのポインターがスタックに割り当てられました

2。オブジェクトm自体(したがって、オブジェクトが運ぶデータ、およびそのメソッドへのアクセス)はヒープに割り当てられています

21
nburk

あなたの理解は正しいかもしれませんが、陳述は間違っています:

オブジェクトmへのポインターがスタックに割り当てられています。

mポインターです。スタック上にあります。おそらく、あなたはMemberオブジェクトへのポインタを意味していました。

オブジェクトm自体(オブジェクトが運ぶデータ、およびそのメソッドへのアクセス)は、ヒープに割り当てられています。

正しいのはmが指すオブジェクトがヒープ上に作成されると言うことです

一般に、関数/メソッドのローカルオブジェクトと関数パラメーターはスタック上に作成されます。 m関数ローカルオブジェクトであるため、スタック上にありますが、mが指すオブジェクトはヒープ上にあります。

19
Rakib

「スタック」と「ヒープ」は、一般的なプログラミング用語です。特に、スタックまたはヒープデータ構造を介して内部的に管理されるストレージは必要ありません。

C++には次のストレージクラスがあります

  • 静的
  • 自動
  • 動的
  • スレッド

おおよそ、動的は「ヒープ」に対応し、自動は「スタック」に対応します。

あなたの質問に移ります:これら4つのストレージクラスのいずれにもポインターを作成できます。また、ポイントされているオブジェクトは、これらのストレージクラスのいずれかに属します。いくつかの例:

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 
}

指定子なしで宣言された名前付き変数は、関数内の場合は自動、そうでない場合はstaticです。

14
M.M

関数で変数を宣言すると、常にスタックに配置されます。したがって、変数Member* mがスタック上に作成されます。 m自体は単なるポインターであることに注意してください。何も指していない。これを使用して、スタックまたはヒープ上のオブジェクトを指すか、まったく何も指すことができません。

クラスまたは構造体の変数の宣言は異なります。クラスまたは構造体がインスタンス化される場所に移動します。

ヒープ上に何かを作成するには、newまたはstd::malloc(またはそれらのバリアント)を使用します。この例では、newを使用してヒープ上にオブジェクトを作成し、そのアドレスをmに割り当てます。メモリリークを回避するには、ヒープ上のオブジェクトを解放する必要があります。 newを使用して割り当てられた場合、deleteを使用する必要があります。 std::mallocを使用して割り当てられた場合、std::freeを使用する必要があります。より良いアプローチは、通常、「スマートポインター」を使用することです。これは、ポインターを保持し、それを解放するデストラクタを持つオブジェクトです。

5
user3553031

はい、ポインターはスタックに割り当てられますが、ポインターが指すオブジェクトはヒープに割り当てられます。正解です。

しかし、オブジェクト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

上記のコードは、スタック上にあるポインターのアドレスを格納します(Objectdeleteで割り当てられたメモリを解放しないため、メモリもリークします)。

関数を終了した後、ポインターは「破棄」されるため(つまり、そのメモリはプログラムを満足させるために使用できます)、安全にアクセスできなくなります

上記のプログラムは、適切に実行するか、クラッシュするか、別の結果を与えることができます。解放または解放されたメモリへのアクセスは、undefined behaviorと呼ばれます。

2
Marco A.