間違いに、突っ込みがなかった。
フラグメンテーション あるはずなのにないより:
ガーベッジコレクタを持っている処理系なら、どれも返してもらえるけど、持っていない処理系では、1のみ。
ガーベッジコレクタの役割は、使われていないメモリを明らかにして、再利用可能にすることです。「明らかにすること」だけかもしれない。
引用元のところで、ガーベッジコレクタがすると言っているのは、正しくはデフラグメント処理で、これはガーベッジコレクタの役割ではありません。
でも、.NET Framewrok では、ガーベッジコレクションのタイミングでデフラグメントも実行されます。参照されているメモリがどこかに集められ、参照されていないメモリがまとめられます。
このことより、.NET Framework では、ヒープに取られたメモリ領域が固定している前提で作ってはいけません。というか、そんなこと、意識しなくてもいいわけだけど。しかし、アンマネージドな世界では、固定している前提で作られていたりします。アンマネージドな世界と行き来する場合は、デフラグメントの時に動かされないようにしなければならないことがあります。そこで、「こいつは動かさないでね」と、お願いすることもできます。あとは囚人さんのセッションを見てちょうだい。→THIS IS CLR
int* ptr;
void huge()
{
int* hoge = (int) malloc(sizeof(int) * 100);
ptr = &hoge[50];
}
void fuga()
{
if (*ptr > *(ptr + 1)) {
int b = *ptr;
*ptr = *(ptr + 1);
*(ptr + 1) = b;
}
}
例示コードに意味を求めてはいけません。確保した領域を指す大域変数を作り、設定するところと使われるところが遠く離れている、という例です。hoge が動かないなら、ptr は hoge[50] のアドレスを指すことが保証されます。しかし、C 言語であっても、realloc によって動く可能性があります。hoge の場所が動けば、ptr は不正なアドレスを指すようになります。このような場合、ptr がどれくらい動いているかを計算しておき、realloc 後に hoge のアドレスが動いたら、ptr のアドレスも計算し直さなければなりません。
同じような間違いで、よく見かけるのが次のような例。
void main()
{
char* fn = getFileName(); // [1]
FILE* fp = fopen(fn, "r"); // [2]
...
fclose(fp);
}
char* getFileName()
{
char path[PATH_MAX];
... path にファイル名を入れる処理...
return &path[0];
}
path はスタックに積まれるメモリです。getFileName() 関数のローカル変数なので、関数を抜けると他の何かによって上書きされます。しかし、処理系によっては、意図通りに動作しているように見えてしまいす。それは、使用されなくなったメモリ(この場合はスタック メモリ)は初期化されないからです。そのため、解放されて他者が使用可能になっても、そこに割り当てた内容はそのまま残っています。getFileName() 関数から抜けても、まだ初期化されずに残っているので、path として使えるように見えます。しかし、[1] と [2] の間に他の宣言を入れてみたり、他の関数を呼び出してみたりすると、あるいは意図通りに動作しなくなります。
処理系によっては、コンパイラが警告を出したり、エラーにしたりします。
8/27 関数名と、関数内の変数名がかぶっていたので修正。
投稿日時 : 2007年8月26日 22:13