(2)b_functionでは関数内で宣言したint型(4byte)分のメモリは確保しても関数終了とともに開放される....と認識していますが
関数内で宣言した上記の変数 int c の参照自体は存在して返却できるのではないでしょうか?
返却できたとして何が起こるか、試すためにmain()を書き換えました。
C
1int main (int c, char* param[])
2{
3 int *aptr, *bptr;
4
5 bptr = b_function(3, 4);
6 printf("bptr = %p, *bptr = %d\n", bptr, *bptr);
7 aptr = a_function(1, 2);
8 printf("aptr = %p, *aptr = %d\n", aptr, *aptr);
9 printf("bptr = %p, *bptr = %d\n", bptr, *bptr);
10 return 0;
11}
私の手元に、これを試せるコンパイラがありました。Windows上の、やや古いコンパイラです。コンパイルして動作させた事例をお見せします。
C:\Users\rubato\Documents> cl t87156.c
Microsoft(R) 32-bit C/C++ Standard Compiler Version 13.00.9466 for 80x86
Copyright (C) Microsoft Corporation 1984-2001. All rights reserved.
t87156.c
t87156.c(15) : warning C4172: ローカル変数またはテンポラリのアドレスを返します。
Microsoft (R) Incremental Linker Version 7.00.9466
Copyright (C) Microsoft Corporation. All rights reserved.
/out:t87156.exe
t87156.obj
C:\Users\rubato\Documents> t87156
bptr = 0018FE88, *bptr = 7
aptr = 003B0F10, *aptr = 3
bptr = 0018FE88, *bptr = 2130567168
ご覧の通り、警告は出たものの、コンパイルは通り動作もした、しかし最初 "7" が正しく(?)表示されたけど、その後 2130567168 という値が表示されてしまいました。
int c として割り当てられたメモリ(この場合、0x18FE88番地)の値が上書きされてしまったのです(2130567168 == 0x7EFDE000 は何かのアドレスでしょう)。
ローカル変数のアドレスを返してはいけないのは、こうした現象が起こるからです。
int c に割り当てられたメモリは「解放」された後も存在し続けます。霧のように消えてなくなるわけでも、メモリがアクセスできなくなるわけでもなく、どこか別な場所に移動することももありません。従って、変数 c のアドレスもそのまま、仰る通り「参照自体は存在」し続けると言えなくありません。なので「喉に骨がひっかかったような感覚w」が残るのも、理解できなくありませんが、、、
メモリは、何らかの目的に合わせて・何らかの値を格納するもの・値を変化できるもの・です(ね?)。
では、メモリを**「解放する」とは**、どういうことでしょうか。ざっくり言えば、他のコードが、他の目的に使って構わなくなるという事ではないでしょうか。
ある時点で、もう値を残しておく必要がなくなった・・・ので、他の関数が・別の目的で使っても良いよ、という変数もあります。malloc() で取得したメモリをfree()で返却することが、まさにそうです。
関数内のローカル変数は、関数からリターンしたら、もう他の目的に使える、という前提でメモリを割り当てます。それがC言語の基本設計なのです。この、関数からリターンしたら解放する、ということを実現しているのがスタックです。関数のローカル変数はスタック領域(のメモリ)に割り当てられます。
オライリー詳説Cポインタ
関数内のスタックフレームは関数終了時にスタックからポップされるため
関数内で宣言したアドレスが有効なものではなくなってしまう
繰り返しますが、アドレスが有効ではなくなるとは、int c に割当られたメモリが消えてなくなる…とかではなく、他のコード(上の事例では、おそらくa_function()関数)が、全く異なる目的に使うようになった、もはや b_function() の int c ではなくなった…だから上書きされたのです。
※念の為:"7"と表示したprintf()関数も同じスタック・同じメモリを使って動作するので、その時点で int c を上書きしてしまい、最初から7と表示されない可能性もあります。ただ、上の事例では「たまたま・幸運にも」7という値が表示された、にすぎません。
b_functionは関数内で静的に宣言されているint型変数の参照は返却してくれません
ここの「静的に宣言されている」という言い回しは誤りです。なぜなら、C言語には static というキーワードがあるから。例えば、次のように宣言した変数 c であれば「静的に宣言されている」と言えます。
C
1int *b_function(int a, int b)
2{
3 static int c;
4 c = a + b;
5 return &c;
6}
このように宣言された変数 c は、スタック領域ではないメモリ領域に割り当てられますので、関数からリターンした後も解放されることはありません。この使い方は定石とも言える正当なものであり、当然コンパイルも実行もできます。
(返却以前にコンパイルエラーですが)
お手元のコンパイラはエラーにしたわけですが、コンパイラによって扱いが異なるのですね。
実は私は最初、メモリが書き換えられる実行例をGCCでお見せできると思いました。試してみると、コンパイル時に警告は出たものの、コンパイルは通ったのですが、実行したら、何も表示せずセグメンテーション例外(!)で終了してしまいました。なんと、b_function()はNULLポインタを返していた、即ち0番地を返すコードが生成されていたのです。そこでWindows上のコンパイラを試した、という次第です。
エラーにするコンパイラ、コンパイルは通すけど実行時にエラーにするコンパイラ・・・NULLかどうか、チェックしてからアクセスしろってことでしょうが・・・それくらい、関数内部のローカル変数のアドレスを返すことは、C言語にとって尋常でない事だと言えます。