質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

3回答

7037閲覧

C++で、動的配列をdeleteしたのに、メモリが解放されていない

the_hoots_under

総合スコア33

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2019/05/02 16:17

以下のプログラムが正常に動きます。
動的配列arryをdeleteしたはずなのに、その後にそのアドレスを参照しても、メモリが残っています。
てっきり、すでにdeleteしているので、その後、別のポインタで参照したら、エラーになるものと思いました。

私の理解がどう間違っているかわかりません。

#include<iostream> using namespace std; int main(int argc, char const *argv[]) { double **start_x; int NumDomain=2; start_x = new double*[NumDomain]; double *arry; arry = new double[30]; for (size_t i = 0; i < 30; i++) { arry[i] = i; } start_x[0] =arry; delete[] arry; for (size_t i = 0; i < 30; i++) { cout<< *(start_x[0]+i)<<endl; } return 0; }

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

ベストアンサー

こんにちは。

deleteするとポイント先のメモリを使用する権利がなくなります。
権利がないだけでアクセスできないよう保護されるわけではなく、普通にアクセスできることが少なくないです。しかし、既に他のスレッドで確保されて使われているケースもあれば、OSへ返却されてアクセスできなくなる場合もあります。
つまり、アクセスしては行けないということです。

投稿2019/05/02 16:43

Chironian

総合スコア23272

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

the_hoots_under

2019/05/02 16:47

すいません。説明文が理解できません。 動的配列に対し,delete[]をしても、ヒープ領域のメモリが解放される訳ではないのでしょうか。(その場合、ヒープ領域のメモリはどのように解放すれば良いのでしょうか。)
Chironian

2019/05/02 16:59

deleteするとそのポインタが指していたメモリは解放されます。これは、そのメモリを他の関数等がnewなどで確保して使えるようにしたということです。これが「解放された」ということです。 物理的に存在するメモリを消し去ることはできませんから、解放することでメモリが消えてなくなるわけではないのです。
the_hoots_under

2019/05/02 17:27

なるほど。いい加減な理解のもと適当な質問をしてしまい申し訳ございません。 delete{]されたことで、arry用に保護されていたメモリ領域が、”誰でも”アクセスできる状態になったということですね。 てっきり、start_x用にメモリが残っているものだと思いました。 そうすると、例えば、また新しく動的配列を確保した際に、OS側が先のarry用だったメモリ領域を確保してしまうなどして、start_xではないところで予期せぬ書き換えが可能になってしまうので、解放したらアクセスしてはいけない。ということですね。 質問の内容が変わってしまうのですが、よろしいでしょうか。 プログラム内で一時的に確保したいくつかの動的一次元配列のデータをその後、 start_xというポインタ配列で、動的一次元配列を一括して管理したいのです。 そのための試作として上のようなプログラムを書いたのですが、何か良い方法ありますでしょうか。
Chironian

2019/05/02 17:44

>(前略)ということですね。 その通りです。 > そのための試作として上のようなプログラムを書いたのですが、何か良い方法ありますでしょうか。 イマイチ質問の意味が分かりませんが、delete[] arry;しなければ良いだけのような気がします。 arryが指すメモリとstart_x[0]の指すメモリは当然同じですね。 であればarrayというポインタが消えてもstart_x[0]が残っていれば、元々arrayが指していた先のメモリ領域をstart_x[0]にて継続して管理できます。 外していたらコメント欄でやり取りできる質問ではないと思いますので、別途質問を立てた方が良いと思います。
the_hoots_under

2019/05/02 17:51

ご返答ありがとうざいます。 いい加減な質問で申し訳ございませんでした。 もう少し考え直して、疑問が明確になったら、別途質問を立てたく思います。 お忙しい中丁寧にありがとうございました。
guest

0

もうベストアンサー出てますけど、なんかスッキリ了解とはなっていないようなので。

C/C++のランタイムメモリ管理は、ズボラな貸し会議室管理にでも例えてみればどうでしょう。

malloc/newすると、メモリ管理人は「この部屋(メモリ領域)がいま空いてるから使って」と部屋を割り当ててくれます。
そして、free/deleteは「使い終わりましたよ」という報告です。少なくとも管理人は、使用中とされている部屋を他に貸すことはないのですが...

部屋には鍵はありません。管理人は使用者(プログラマ)の申告だけを信用していて、使用者は自分に割り当てられている部屋だけちゃんと使うという了解のもとに成り立っているシステム。
使用者が勝手に(あるいは勘違いして)他の部屋まで進出したり、返却報告済の部屋にまた入って作業しちゃったり、実は好き放題出来てしまいます。それらの部屋が既に他に割り当てられていたら...
でも、なにが起こってもプログラマの責任だからヨロシクね、重い管理は言語側ではしないよ、その分高速低コストだから。というのがC/C++のポリシー。

たまにランタイムメモリ管理人の上位のOS管理人さんが、フロア全室空いたからとセキュリティシステムを起動しちゃって、入ろうとしたら警報がなって(アクセス違反)アプリケーションごと排除されることもありますけどね。

投稿2019/05/03 01:26

thkana

総合スコア7639

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

the_hoots_under

2019/05/03 17:50

なるほど。 C/C++ のヒープ領域のメモリの動的確保は、そこそこずさんに作られているということですね。 うろ覚えですが、C++のクラスで実装されたリストのコードを読んだ時、 新しくリストの要素を作成する関数が、 リスト要素クラスを動的にとって、両隣のリスト要素と繋いだ後に、そのクラスインスタンスのポインタ自体が関数内で消える というflowだと記憶していたので、 ついつい、動的に変数をとった後、そのポインタ自体が消えても、ヒープ領域のメモリへアクセスして良いものだと勘違いしておりました。
thkana

2019/05/03 22:59

「ずさん」というのとはちょっと違うんじゃないかと。性善説に立って、プログラマの能力を信頼した場合の必要最低限の仕組みだけを設けているということでしょう。C/C++の高速性や細かい操作が出来るという特性はその辺から生まれているわけです。反面、「ずさんなプログラマ」にとってはC/C++はメチャクチャな操作をそのまま実行してクラッシュする危険な言語ということになるわけですけれど。 ポインタは、使用者側で「この場所を確保してるぞ」と記録しているメモに過ぎません。「メモリ確保帳簿の原本」はOS側で保持していますから、アプリケーション側のポインタが消滅しても確保は継続します。だからメモリリークなんて話が出てくるわけで。(最近のC++規格では、領域を指しているポインタがなくなったら自動解放ができるような仕組み「スマートポインタ」も取り入れられました)
thkana

2019/05/03 23:01

(って、ズボラという言葉を使って説明してたのは私だったか...ごめんなさい>C++)
guest

0

メモリのOSからの割り当て・OSへの返却というのは、そこそこ時間の掛かる処理になります。
そこで、多くの処理系は返却されたメモリをOSに返さずに再利用します。

実験として
arry = new double[30];の要素数30を適当に増やしていくと
動作が代わりエラーになることがあります。
wandboxで実験した際は16382が閾値でした。

これは、小さいものだけC++ランタイムが確保しておいたメモリプールを利用・再利用を行い
大きい割り当ての際はOSから直接仕入れ・返却しているためと考えられます。

投稿2019/05/02 17:11

asm

総合スコア15147

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

the_hoots_under

2019/05/02 17:34

高度な返答ありがとうございます。 先の返答者様とのやり取りで、deleteしても、メモリアドレスをどこかに保持していれば、そのメモリへアクセスできるものと理解しました。 しかし、処理系が、”メモリをOSへ返してしまう”と、deleteされしまったヒープ領域へは、そのアドレスを知っていてもアクセスできなくなるということでしょうか。
the_hoots_under

2019/05/02 17:37

確認ですが、動作エラーが起きるというのは、 処理系がOSへ返してしまった領域に、start_xがアクセスしてしまったために、 ということでよろしいでしょうか。
asm

2019/05/02 23:20

> 処理系が、”メモリをOSへ返してしまう”と、deleteされしまったヒープ領域へは、そのアドレスを知っていてもアクセスできなくなる 環境に依存しますが、OSの下で複数のプロセスが動く普通の環境の場合はそうなるでしょう。 (シングルタスクなOSや組み込みみたいな奴まで考えだすと。。。) > 処理系がOSへ返してしまった領域に、start_xがアクセスしてしまったために、 ほぼ、そのとおりです。 OSがアドレスへのメモリの割り当てを解除(もしくは読み取り権限を剥奪)し start_x[0]が指す先が無効なアドレスになったためエラーが発生します。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問