先週 C の勉強を始めたばかりの者です。
ローカル変数について学んでいる時に疑問を抱きました。
色んな入門書やネット上の記事では、「ローカル変数はそれが宣言された関数内の処理が全て終わった時点でメモリから消去される。」と説明されていますが、自分の環境でそれを実験してみると、変数とその中に代入した値が破棄されずに残るという現象が起こりました。
※ 試した実行環境は下記の二つです。
- Ubuntu 14.04.5 LTS (Cloud9) / gcc version 4.8.4
- Wandbox
参考にした入門書はこちらです。
苦しんで覚えるC言語 - 関数内で寿命が尽きる変数
###理解できない現象
C
1#include <stdio.h> 2 3int countfunc(void); 4 5int main(void) 6{ 7 countfunc(); 8 countfunc(); 9 countfunc(); 10 return 0; 11} 12 13int countfunc(void) { 14 int count; // 初期化していない 15 count++; 16 printf("%d\n", count); 17 return count; 18} 19 20// 実行結果: 21// 1 22// 2 23// 3
###疑問
- 上記プログラム実行時にはなぜ、 count をまるで静的ローカル変数として宣言したかのように、プログラム終了まで count の値が記憶されるのでしょうか。
- 上記プログラムの countfunc() 実行時の最初に、 まるでグローバル変数として宣言したかのように count が 0 に初期化されるのはなぜなのでしょうか。
他にもこの現象を理解する上で知っておくべき概念等がありましたらご教授頂けると幸いです。
よろしくお願い申し上げます。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答7件
0
こんにちは。
色んな入門書やネット上の記事では、「ローカル変数はそれが宣言された関数内の処理が全て終わった時点でメモリから消去される。」と説明されています
そもそも、ローカル変数をメモリから「消去」するってどのような意味でしょうか?
メモリ自体はハードウェアとして存在します。それをハード的に破壊するという意味でないのは自明と思います。
では、0やその他何か決まった値を書いて「消去」したことにするのでしょうか? その値はなんでしょう?
実のところ決めることはできません。C言語は全ての値をプログラマが使えるように設計されていますので、何か決まった値で埋めて「消去」するという概念がありません。
となるとできることは、あるメモリに割り当てていたローカル変数の割当を解除することだけです。
その後、再度同じ関数を呼び出した結果、前回割り当てられていたメモリが同じローカル変数に割り当てられることもあるかも知れません。(一般的な実装をするとそうなることも少なくないです。)
その結果、以前の値が保持され「消去」されていないかのように見えるかも知れません。
しかし、この動作はあくまでも「たまたま」です。一度割当を解除されたメモリは別の変数やスタックとして使われる可能性があります。(実際そうなることは多いです。)
この場合、再度同じ関数が呼ばれて以前のローカル変数が割り当てられていたメモリが今回も同じローカル変数に割り当てられたとしても既に値は以前の値ではありません。その時、何が入っているか予測することは事実上不可能ですので、そのようなメモリの使い方をしては行けません。
投稿2018/07/24 19:14
総合スコア23272
0
寿命が尽きた変数とは、管理を放棄した変数と言い換える事ができます
(C言語の場合のみ、C++の場合はデコンストラクタによって解放処理が行われる事もあります)
適当に変数を代入するだけのotherfunc
関数を追加して
c
1#include <stdio.h> 2 3int countfunc(void); 4void otherfunc(void){ 5 int hoge = 100; 6} 7int main(void) 8{ 9 countfunc(); 10 countfunc(); 11 otherfunc(); 12 countfunc(); 13 return 0; 14} 15 16int countfunc(void) { 17 int count; // 初期化していない 18 count++; 19 printf("%d\n", count); 20 return count; 21}
を手元の環境で実行してみたところ
1 2 101
と、他の関数を呼ぶことで変数の値が変化しました。
他の関数のまったく別の変数によって値が変化する以上信用できる値ではありません。
投稿2018/07/24 21:27
編集2018/07/25 13:50総合スコア15149
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/07/25 12:18
2018/07/25 13:49
2018/07/26 17:24
0
c
1 int count; // 初期化していない 2 count++;
この時点で未定義動作です。コンパイラは何をしても許されます。「基本的にはゴミが入る」とかそういうの以前の問題です。
投稿2018/07/24 18:36
編集2018/07/24 18:42総合スコア5852
0
仕様は理解された上で、実装についての質問だと思います。
終了した関数のautoローカル変数の値が参照できるのはたまたまメモリを誰も書き換えていないからです。
C
1int main(void) 2{ 3 countfunc(); 4 printf("Hello World!\n"); 5 countfunc(); 6 fclose(stdin); 7 countfunc(); 8 return 0; 9}
等、途中に関数呼び出しなどスタックを使う処理を挟むと結果がおそらく変わってくると思います。
実装依存のため、上記コードでどうなるかは分かりませんので、いろいろやってみてください。
もちろん、全ての処理系が「間に処理を挟まない限りローカルauto変数を保存する」訳でもないです。
初期値が0
であったというのは、
・たまたま
・その処理系が常にローカル変数も0
で初期化するようになっているとか
などが考えられます。
初回のcountfunc()
の前に他の関数呼び出し等を入れてみて、変わるようなら「たまたま」ですね。
投稿2018/07/25 00:22
総合スコア85766
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/07/26 17:52
0
面白いですね。この挙動。結論としてはC/C++では初期化しておいたほうが良く、今回の場合ですと初期化しないといけません。
たぶん未定義動作ってやつでないかなーと思います。
直近で残っていた内部的に参照していたエリアを見てしまうのだと思います。
この挙動に依存したプログラムはバグだと思います。なので、この場合ですと0で初期化すべきです。
上記プログラムの実行結果はなぜ、 変数 count を静的ローカル変数として宣言したかのようになるのでしょうか。
なっていません。ただのローカル変数です。
C言語の規約にはなかったはずなので、0が入るのはたまたまか、実装依存です(MSVCなのかgccなのかとか)。
WandBoxとかで試すとg++(C++)かな?
Javaとかはルールが決まっていて、0になったかと思います。
ローカル変数で明示的に初期値を代入しない場合、基本的にはゴミが入ると思ってください。
#include <stdio.h> int countfunc(void); int main(void) { int i = countfunc(); printf("i: %d\n", i); int j = countfunc(); printf("k: %d\n", j); int k = countfunc(); printf("k: %d\n", k); return 0; } int countfunc(void) { int count = 10; // 初期化 count++; printf("%d\n", count); return count; }
これだと意図通りですね。
投稿2018/07/24 18:18
編集2018/07/25 00:40総合スコア45
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/07/24 18:58 編集
2018/07/26 16:46
0
ベストアンサー
「ローカル変数はそれが宣言された関数内の処理が全て終わった時点でメモリから消去される。」と説明されていますが
すでに他の回答にもあるように、その説明は間違いです(そのような説明をしている人います?)。
C言語における変数定義とは「メモリのこの場所使うね」という、単なる場所取りです(具体的な場所はコンパイラーによって適切に決められます)。そして、スコープを抜ける際は「もういらないから好きにしていいよ」と言って、特に何もせずに明け渡すだけです。メモリに書いた内容は、他の誰かが同じ場所に書き込まない限り残り続けます。
これを踏まえて、
- 上記プログラム実行時にはなぜ、 count をまるで静的ローカル変数として宣言したかのように、プログラム終了まで count の値が記憶されるのでしょうか。
前述のように、スコープを抜けてもメモリに書いた値は他の誰かがいじらなければそのまま残っています。そのため、再度countfunc関数を呼び出したときに、たまたま同じ場所が割り当てられ、しかも他の誰もいじっていなければ前回と同じ値になるので、記憶されたかのように見えてしまうのです。
上記プログラムの countfunc() 実行時の最初に、 まるでグローバル変数として宣言したかのように count が 0 に初期化されるのはなぜなのでしょうか。
main関数に入る前に、たまたま同じ場所を使っていた誰かが、たまたま0を書き込んでいたのだと思います。
ただし、必ずしも上記のような仕組みとは限りません。例えば、最適化を有効にするとメモリを使わずに計算することもあるので、その場合はまったく異なる挙動を示すことになります。
さらに言うと、未初期化のローカル変数を使用した結果は未定義なので、どのような結果をもたらすかは想定できません。実際、Visual Studioではデバッグビルドで警告(ただしエラー扱い)が出ました(親切設計!)。警告を無視して無理矢理コンパイルを通して実行させたらアサーションが発生しました(親切設計!!)。
投稿2018/07/25 12:15
総合スコア5944
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/07/26 18:10
2018/07/30 09:59
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/07/26 17:12