シンプルな質問です
C++でループ内で変数宣言を行った場合
1ループごとにその変数は解放されますか?
それとも完全にループが終了するまで新たに領域を確保し続けて、最終的に全てを解放しますか?
それとも領域は使い回しですか?
出来たらそれが明記してある文書などをご存知でしたら
それも教えていただけるとありがたいです
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答7件
0
変数が確保されるのは関数の先頭(実装の話)
変数を使えるのはscopeの中(規格)
変数のctor(コンストラクタ)/dtor(デストラクタ)の呼び出しはscopeで(規格)
投稿2018/07/26 05:20
総合スコア5852
0
C++でループ内で変数宣言を行った場合
1ループごとにその変数は解放されますか?
「解放」の意味にもよりますが、スマートポインタなどクラス型の変数をブロック内で宣言していた場合、デストラクタはスコープアウト時点で実行されます。
投稿2018/07/26 03:00
総合スコア145930
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
まあ、実装の問題もありますが、
関数内で宣言される変数は、関数の入り口でまとめて領域確保されます
ループ内で宣言されるとかの事情は関知されません
ですんで、
変数の開放は、関数の終了時にされます
領域は使いまわしです
投稿2018/07/26 02:53
編集2018/07/26 02:59総合スコア88024
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/07/26 03:44
2018/07/26 04:18
2018/07/26 04:33
0
ベストアンサー
本題に入る前にCについて述べます。CにはVLAがあるため、それぞれのループで必ずしも同じ領域が確保されるとは限りません。
C
1#include <stdio.h> 2 3int main(void) 4{ 5 for (int i = 0; i < 10; i++) { 6 int x; 7 int y[i + 1]; 8 int z[i + 2]; 9 printf("%d: %p, %p, %p\n", i, (void *)&x, (void *)y, (void *)z); 10 } 11}
Wandboxでの実行結果
※ GCCなどVLAが使用できるコンパイラで最適化無し(-O0
)で実行してください。
領域は一つのループに対して、開始する度に確保され、終了する度に開放されますが、解放された領域は再び使用可能であるため、通常は次のループでも同じ領域を確保します。しかし、VLAのような取得する大きさが毎回異なる場合は、その大きさに合わせて確保される領域もずれていくことになります。
さて、上はCの場合です。**C++にはVLAはありません。**そして、私が知る限り、VLAのように実行の度に確保する領域の大きさが異なる仕組みは存在しません。
※ ここで変数によって確保される領域はスタック領域であって、malloc
やnew
等で確保されるヒープ領域のことではないことに注意してください。
では、C++では確保も解放も何もされないのかというとそうではありません。なぜなら、コンストラクタとデストラクタの呼び出しは、毎回行われているからです。
C++
1#include <iostream> 2 3class A 4{ 5public: 6 int v; 7 A(int v) : v(v) 8 { 9 std::cout << "run construct: " << this->v << std::endl; 10 } 11 ~A() { std::cout << "run destruct: " << this->v << std::endl; } 12}; 13 14int main() 15{ 16 for (int i = 0; i < 10; ++i) { 17 A a(i); 18 std::cout << reinterpret_cast<void *>(&a) << std::endl; 19 } 20}
確保と解放がコンストラクタとデストラクタの呼び出しも意味するのであれば、ループ毎に毎回行われます。そして、スタック領域は再利用されるため、常に同じメモリ領域をしようしているように見えます。では、同じスタック領域を使用することが保証されていると言えるのでしょうか?
まず、GCCやClangではC++でもVLAが使用できるように独自拡張を行っています。VLAが使われている場合は、必ずしも同じとは限らない事は既に見たとおりです。でも、純粋なC++とは言えませんので、これを反例とするにはいささか弱いと思われます。
次に最適化によってそもそもスタックに入らずレジスタのみで完結している可能性です。ですが、それを検証する手段がありません。なぜなら、&
などのアドレス演算子を使ってしまうとレジスタのみで完結するコードが生成されないからです。
int
等の未初期化のまま使うと動作未定義になる変数を使って検証することには意味がありません。なぜなら、その時点で動作未定義であり、何が起きてもC++の仕様としては満たすことになるからです。
結局の所、たとえ同じ領域を使っていても、使っていなくても、動作未定義を含まない意味のあるコードを作ることは出来ないと考えています。つまり、保証されまいが、保証されていようが、それがコードに影響することはないという考えです。領域再利用を前提で、次のようなコードを考えるかも知れません。
C++
1#include <iostream> 2 3int main() 4{ 5 int *p = nullptr; 6 for (int i = 0; i < 10; ++i) { 7 if (p != nullptr) { 8 std::cout << p << ": " << *p << std::endl; 9 (*p)++; 10 } 11 int a; 12 if (p == nullptr) { 13 a = 0; 14 } else { 15 a = *p; 16 } 17 p = &a; 18 } 19}
このコードはうまく再利用しているように動作しますし、a
は必ず代入してから使用しているように見えます。しかし、このコードは動作未定義です。なぜなら、a
はループ毎に終了しており、p
に保存されたアドレスを用いてアクセスすることは未定義な動作になってしまうからです。さらに、デストラクタで解放処理が行われている場合は、動作を予想するのは困難です。
C++
1#include <iostream> 2#include <string> 3 4int main() 5{ 6 std::string *p = nullptr; 7 for (int i = 0; i < 10; ++i) { 8 if (p != nullptr) { 9 std::cout << p << ": " << *p << std::endl; 10 *p += "+"; 11 } 12 std::string a; 13 if (p == nullptr) { 14 a = "+"; 15 } else { 16 a = *p; 17 } 18 p = &a; 19 } 20}
上のコードを予測できる人は少ないと思います(少なくとも私の予測は外れていました)。
投稿2018/07/26 13:31
編集2018/07/26 13:33総合スコア21737
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/07/27 02:22
2018/07/27 11:52
0
イメージとしては、変数を定義した時点で変数の「格納場所を確保」し、スコープを抜けるところで「解放」します。
ただし、それをどのように実装するかはC/C++では規定していません。コンパイラー任せです。コンパイラーは、なるべくメモリを無駄遣いしないようによしなに取り計らってくれます。ですので、少なくともループが終了するまで領域を確保し続けるなどということはせず、領域は使い回しです。
実際のところ、C/C++においては変数の領域がどれだけ必要かはコンパイル時に判るので、関数の開始時点で必要な分の領域をまとめて確保し、それを使い回して関数を抜ける際にまとめて解放しています。
さらには、最適化により変数を省いたりレジスターを使ったりして「メモリ領域を消費しない」ということもあります。
投稿2018/07/26 12:58
総合スコア5944
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/07/27 01:34
0
C++ コンパイル出力は詳しく見たこと無いですが、、C の上方互換とすると、
変数が使えるのが、スコープの中という理解は良いと思います。ただ、デバッグ時は要注意。 例えば、以下の場合、
int a, b;
a = 0;
...(a を使ったなんかの処理) ...
...(これ以降は、aは使われない)
b = 2;
...(b を使ったなんかの処理) ...
デバッガ等で、bを使った処理の中で、止めた場合、 aの値は正しく取得できるとは限りません。 なぜならば、 a の値は、b = 2; 以降には使われないので、同じ領域を bが使う事があるからです。 (コンパイラによる最適化)
特に、割り当てられているのがレジスタだったりすると、アウトの可能性が高いです。
ご注意を
投稿2018/07/26 10:44
総合スコア6385
0
こんにちは。
C++でループ内で変数宣言を行った場合
1ループごとにその変数は解放されますか?
1ループ毎にブロック・スコープを抜けますので、1ループ毎に開放される建前です。
標準規格書では、「3.3 Scope」のあたりに書いて有りそうです。(C++11でよく使われるドラフト文書N3337の場合)
これを江添氏が解説されています。(規格書に忠実に解説されているようです。)
規格書は難解ですので、この解説の該当箇所を見たほうが分かりやすいと思います。
なお、実装的にはスタックにメモリが確保されますが、1ループ毎にスタック・ポインタを増減しているかというと、やってない筈です。
投稿2018/07/26 05:13
総合スコア23272
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/07/27 02:35
2018/07/27 11:58