リファレンスカウント拡張c言語によるメモリ自動管理の考え方--メモリ漏洩の原因
3067 ワード
最近object-cのものを見て、参照カウントを用いてメモリの自動管理を実現することに興味があるが、retain/releaseを保持してメモリを手作業で処理できるのは失敗だと思っている.思わず考えてみると、私がc言語を拡張したら、どうすればいいか.c言語はモバイル開発において、比類のない優位性を持っている:精細度の高いプログラム実現を実現し、設備の性能を十分に利用することができる.
なお、C言語を除いては、原則として他の言語プラットフォームとは何の関連もない.
まず簡単な関数からお話しします.
struct objectは特定の構造体を表すが、ここでは定義されていない.最初の行から、これらの変数が存在するメモリ領域をよく分析します.
aとbは入力変数であるため、aとbはともにスタック空間に存在し、bはアドレス変数であるstruct objectのオブジェクトまたはNULLを指し、struct objectが空でない場合、そのオブジェクトがスタックに存在する可能性もあるし、スタックに存在する可能性もある.
iは、スタックに格納int型の変数である.
jはintタイプを指すポインタ変数であり、jはスタックに格納、その指向変数は未定である.
Aはスタックに格納struct object型の変数である.広義にはstruct objectとintには何の違いもないが、唯一の違いはintが内蔵タイプであり、struct objectは開発者自身が組織しオペレーティングシステムに宣言するタイプである.私はここでintとstructを書いたが、目的はこれを指摘することだ.
Bはポインタタイプである、Bはスタックに格納され、Bが指す具体的なアドレスは未定である.
j = malloc(sizeof(int)); jのためにスタックにintの長さのメモリを申請するが、この場合、jの値は申請に成功したメモリのアドレスであり、*jはスタック内のメモリ自体を表す.理論的には、この文の前に*j=iを追加すると、jが指すアドレスが不確定であるため、不確定なアドレスを修正すると、予想できない結果を引き起こす.ただし、この文の後に*j=iが実行される場合.*jのメモリアドレスは開発者が自分で申請したので、全く問題ありません.関数が終わる前にfree(j)がないため、このため、この関数にはメモリリークがある.開発者がこの文の後にj=&iを追加すると、すると、関数の末尾にfree(j)を加えるも、jのアドレスが変化し、スタック空間を指すため、一連のエラーが発生する.
B = &A; この行は、AのアドレスをB変数に付与ものである.この場合、*BはAと等価であり、*Bを修正することは、Aを修正することである.
簡単な例をもう一つ書きます.
明らかに、結果はa=1 b=6であるべきである.でも、why?多くの人は、2番目のパラメータがbのアドレスを伝達するため、実際にはアドレスを伝達し、bが存在するスタックのアドレス値を変数dにコピーするだけで、aの値を変数cにコピーすることと何の違いもないと言う.ただし*dはbにアクセスする方法を提供する.プログラムがfunction(a,&(&b))になると、過去や合法などをコンパイルできるかどうかはともかく、&bという変数は、記憶するスペースがないので、&bに対して&bを再取得するのは、不法であるべきである.ただしint*xに変更すると;x=&b; function1(a,&x); それは論理的な問題はないはずだ.xはスタックに存在し、アドレスを取るのは完全に合法的であるからだ.もちろん、function 1の2番目のパラメータはintポインタ変数である、ポインタ値が入力されるため、タイプチェック時にエラーが発生する.
上記の例では、以下の点を説明できます.
1.メモリスタックには専用のメンテナンスがあるが、スタックで申請する変数であれば、メモリが解放する必要がなく、メモリ漏洩の問題も存在しない.メモリが足りないという問題があるだけです.
2、ポインタは基本タイプにすぎず、その使用は、他の基本タイプやカスタムタイプとは何の関係もない.ポインタの指向も、スタックやスタックとは何の関係もない.
3,関数のパラメータは,何が伝達するかにかかわらず,フルコピーで伝達される.ただし、ポインタ変数をコピーすると、メモリの共通アクセスを間接的に実現することができる.
4、ポインタタイプ変数がスタック内の新規申請のメモリを指す場合、ポインタ変数の値を変更すると、申請のメモリ領域が漏洩する可能性が高いため、ポインタタイプ変数がスタック内の新規申請のメモリを指す場合、その指向を変更することは一般的にできない.
5.関数が自分の必要な空間を正確に予知ことができれば、スタックを使用することなく、変数と空間をスタックに完全に割り当てることができ、すなわちプログラムにメモリ漏洩のリスクがない.結果として、仕事中のメモリサイズが予知できないことが多い.
6.すべての変数がスタックに割り当てるも、現在の親スタックの変数を直接修正することは、親スタックの変数のアドレスを知ることができる限り、完全に可能である.
しかし悲劇的には、プログラムがどれだけのメモリを必要とするかは、実行状況の変化に伴って変化するため、常に動的な申請とメモリの解放が必要である.動的にメモリを申請する、解放するのを忘れたり、メモリを指すポインタを油断して修正したり、ポインタが何度も戻ってきたりして、メモリ領域の変化を特定できる人もいないし、解放する勇気もないので、メモリが漏れることになります.また、実際に使用するメモリは解放するが、指向するポインタを依然として使用することも、プログラムに誤りを生じさせることが多い問題である.これもC言語の中でメモリの“誰が申請して、誰が釈放します”の原則の由来です.
メモリ漏洩の原因が分かった以上、C拡張自動メモリ管理にも根拠がある.
AppleはXCode 4からARCを提供しているので、後の文章は書かなかった.この部分はARCがどのように働くかを理解しましょう.
なお、C言語を除いては、原則として他の言語プラットフォームとは何の関連もない.
まず簡単な関数からお話しします.
void function(int a, struct object* b)
{
int i;
int *j;
struct object A;
struct object *B;
j = malloc(sizeof(int));
B = &A;
}
struct objectは特定の構造体を表すが、ここでは定義されていない.最初の行から、これらの変数が存在するメモリ領域をよく分析します.
aとbは入力変数であるため、aとbはともにスタック空間に存在し、bはアドレス変数であるstruct objectのオブジェクトまたはNULLを指し、struct objectが空でない場合、そのオブジェクトがスタックに存在する可能性もあるし、スタックに存在する可能性もある.
iは、スタックに格納int型の変数である.
jはintタイプを指すポインタ変数であり、jはスタックに格納、その指向変数は未定である.
Aはスタックに格納struct object型の変数である.広義にはstruct objectとintには何の違いもないが、唯一の違いはintが内蔵タイプであり、struct objectは開発者自身が組織しオペレーティングシステムに宣言するタイプである.私はここでintとstructを書いたが、目的はこれを指摘することだ.
Bはポインタタイプである、Bはスタックに格納され、Bが指す具体的なアドレスは未定である.
j = malloc(sizeof(int)); jのためにスタックにintの長さのメモリを申請するが、この場合、jの値は申請に成功したメモリのアドレスであり、*jはスタック内のメモリ自体を表す.理論的には、この文の前に*j=iを追加すると、jが指すアドレスが不確定であるため、不確定なアドレスを修正すると、予想できない結果を引き起こす.ただし、この文の後に*j=iが実行される場合.*jのメモリアドレスは開発者が自分で申請したので、全く問題ありません.関数が終わる前にfree(j)がないため、このため、この関数にはメモリリークがある.開発者がこの文の後にj=&iを追加すると、すると、関数の末尾にfree(j)を加えるも、jのアドレスが変化し、スタック空間を指すため、一連のエラーが発生する.
B = &A; この行は、AのアドレスをB変数に付与ものである.この場合、*BはAと等価であり、*Bを修正することは、Aを修正することである.
簡単な例をもう一つ書きます.
void function1(int c, int* d)
{
c = 5;
*d = 6;
}
void main()
{
int a = 1;
int b = 2;
function1(a,&b);
printf("a=%d b=%d",a,b);
}
明らかに、結果はa=1 b=6であるべきである.でも、why?多くの人は、2番目のパラメータがbのアドレスを伝達するため、実際にはアドレスを伝達し、bが存在するスタックのアドレス値を変数dにコピーするだけで、aの値を変数cにコピーすることと何の違いもないと言う.ただし*dはbにアクセスする方法を提供する.プログラムがfunction(a,&(&b))になると、過去や合法などをコンパイルできるかどうかはともかく、&bという変数は、記憶するスペースがないので、&bに対して&bを再取得するのは、不法であるべきである.ただしint*xに変更すると;x=&b; function1(a,&x); それは論理的な問題はないはずだ.xはスタックに存在し、アドレスを取るのは完全に合法的であるからだ.もちろん、function 1の2番目のパラメータはintポインタ変数である、ポインタ値が入力されるため、タイプチェック時にエラーが発生する.
上記の例では、以下の点を説明できます.
1.メモリスタックには専用のメンテナンスがあるが、スタックで申請する変数であれば、メモリが解放する必要がなく、メモリ漏洩の問題も存在しない.メモリが足りないという問題があるだけです.
2、ポインタは基本タイプにすぎず、その使用は、他の基本タイプやカスタムタイプとは何の関係もない.ポインタの指向も、スタックやスタックとは何の関係もない.
3,関数のパラメータは,何が伝達するかにかかわらず,フルコピーで伝達される.ただし、ポインタ変数をコピーすると、メモリの共通アクセスを間接的に実現することができる.
4、ポインタタイプ変数がスタック内の新規申請のメモリを指す場合、ポインタ変数の値を変更すると、申請のメモリ領域が漏洩する可能性が高いため、ポインタタイプ変数がスタック内の新規申請のメモリを指す場合、その指向を変更することは一般的にできない.
5.関数が自分の必要な空間を正確に予知ことができれば、スタックを使用することなく、変数と空間をスタックに完全に割り当てることができ、すなわちプログラムにメモリ漏洩のリスクがない.結果として、仕事中のメモリサイズが予知できないことが多い.
6.すべての変数がスタックに割り当てるも、現在の親スタックの変数を直接修正することは、親スタックの変数のアドレスを知ることができる限り、完全に可能である.
しかし悲劇的には、プログラムがどれだけのメモリを必要とするかは、実行状況の変化に伴って変化するため、常に動的な申請とメモリの解放が必要である.動的にメモリを申請する、解放するのを忘れたり、メモリを指すポインタを油断して修正したり、ポインタが何度も戻ってきたりして、メモリ領域の変化を特定できる人もいないし、解放する勇気もないので、メモリが漏れることになります.また、実際に使用するメモリは解放するが、指向するポインタを依然として使用することも、プログラムに誤りを生じさせることが多い問題である.これもC言語の中でメモリの“誰が申請して、誰が釈放します”の原則の由来です.
メモリ漏洩の原因が分かった以上、C拡張自動メモリ管理にも根拠がある.
AppleはXCode 4からARCを提供しているので、後の文章は書かなかった.この部分はARCがどのように働くかを理解しましょう.