高品質C++プログラミング_第7章_メモリ管理(1)

3884 ワード

7.1メモリ割り当て方式
1つのC、C++プログラムコンパイル時のメモリは5つの大きな記憶領域に分けられる:スタック領域、スタック領域、グローバル領域、文字定数領域、プログラムコード領域.
(1)静的記憶領域への割当て
コントローラコントローラ:コンパイラ
≪割当て時間|Assign Time|oem_src≫:プログラムのコンパイル時にメモリを割当てます.
リリース時間:プログラムの全実行期間に存在し、プログラム終了後OSによりリリースされる
内容:グローバル変数、static変数
特徴:
0、スピードが速くて、間違いにくい.
1、初期化されたグローバル変数と静的変数は一つの領域にあり、初期化されていないグローバル変数と静的変数は別の領域にある.
2、定義後、変数の値は変更可能
(2)スタックに作成
コントローラコントローラ:コンパイラによって自動的に割り当てられて解放されます
≪割当て時間|Assign Time|oem_src≫:プログラムが実行されている場合(関数の実行時)にメモリを割当てます.
≪解放時間|Release Time|emdw≫:関数の実行が終了すると、解放メモリは自動的に解放されます.
例:ローカル変数、関数パラメータ
特徴:
1、スタックメモリ割り当て演算はプロセッサの命令セットに内蔵されており、割り当て効率は高いが、割り当てられたメモリ容量は限られている.
2、定義後、変数の値は変更可能
(3)スタックからの割当て
制御者:プログラマは一般的にプログラマによって割り当てられ、解放されます.
≪割当て時間|Assign Time|emdw≫:プログラムが実行された(newまたはmallocに遭遇した)ときにメモリを割当てます.
リリース時間:プログラマー自身が、プログラマーがリリースしない場合、プログラム終了時にOSで回収される可能性がありますが、プログラム実行中にリリースされないメモリはメモリ漏洩に属します.
例:newとmalloc申請を使用するスペース
特徴:
0.異なるサイズのヒープスペースを頻繁に割り当てて解放すると、ヒープ内のクラックが発生する
1、プログラマーはmallocまたはnewを使用して任意のメモリを申請し、freeまたはdeleteでメモリをいつ解放するかを自分で担当します.そうしないと、メモリが漏洩します.
2、定義後、変数の値は変更可能
(4)文字定数領域
制御しています:コンパイラ
≪割当て時間|Assign Time|oem_src≫:プログラムのコンパイル時にメモリを割当てます.
リリース時間:プログラム終了後にシステムからリリース
例:定数文字列
特徴:定義後、変数の値は変更できません.読み取り専用です.
(5)プログラムコード領域
内容:関数体のバイナリコードを格納する
例:
1、保管場所
int a = 0;          
char *p1;            
main() 
{ 
	int b;   
	char s[] = "abc";   
	char *p2;   
	char *p3 = "123456"; p3    , 123456\0    , **              ** 
	static int c =0;   (  )    
	p1 = (char *)malloc(10);       10 20         。
	strcpy(p1, "123456"); 123456\0     ,         p3    "123456"       。 
} 

2、内容が変わるかどうか
main() 
{ 
	static char b = 'a'; //       
	b = 'c'; //   ,     
	static char a[] = "hello"; //        
	a[0] = 'X'; //   ,     
	-------
	char *p = "world"; //   p        ,      
	p[0] = 'X';        //  ,            ,          ,
	strcpy(p,"123");   //  ,          ,             
} 

注意:ポインタを定義して初期化する値は定数領域に存在し、ポインタが指す値とポインタの指向は変更できません.
7.2よくあるメモリエラーとその対策
(1)メモリ割り当てに失敗したのに使用
解決策:メモリを使用する前に、ポインタがNULLであるかどうかを確認します.
たとえば、ポインタpが関数のパラメータである場合、関数の入り口でassert(p!=NULL)でチェックします.
mallocまたはnewでメモリを申請する場合はif(p=NULL)またはif(p!=NULL)でエラー防止処理を行うべきです.
(2)メモリ割り当ては成功したが,初期化されていないので参照する.
解決策:配列を作成する方法にかかわらず、デフォルト値を使用しないで初期値を割り当てることを忘れないでください.ゼロ値をつけても省略できないので、面倒を嫌がらないでください.
(3)メモリ割り当ては成功し初期化されたが,操作はメモリの境界を越えた.
(4)メモリの解放を忘れたため、メモリが漏れる
解決方法:ダイナミックメモリの申請と解放はペアリングしなければならない.プログラム中のmallocとfreeの使用回数は必ず同じでなければならない.そうしないと間違いがあるに違いない(new/delete同理).
あるいは、2つの変数を用いて、申請空間の数と解放空間の数をそれぞれ記録する.両方が等しい場合は、メモリ漏洩がないことを示します.
(5)メモリを解放しても使用し続ける
3つのケースがあります.
(1)プログラム中のオブジェクト呼び出し関係が複雑すぎて,あるオブジェクトがメモリを解放したか否かを明らかにすることが困難である.
解決方法:この場合、データ構造を再設計し、オブジェクト管理の混乱を根本的に解決しなければならない.
(2)関数のreturn文が間違っています.「スタックメモリ」への「ポインタ」または「参照」(3)に戻ってfreeまたはdeleteを使用してメモリを解放した後、ポインタをNULLに設定しないでください.「野ポインタ」が発生します.
解決方法:メモリを解放した後、積極的にポインタをNULLに割り当てる
7.7「野指針」の根絶
「野指針」の成因は主に2つあります.
(1)ポインタ変数は初期化されていない.
注意:任意のポインタ変数は作成されたばかりのときに自動的にNULLポインタにはなりません.デフォルト値はランダムで、むやみに指を指します.
解決方法:ポインタ変数は作成と同時に初期化されるべきで、ポインタをNULLに設定するか、合法的なメモリを指し示すかのいずれかです.
char *p = NULL;
char *str = (char *) malloc(100);

(2)ポインタpがfreeまたはdeleteされた後,NULLとして置かれず,pが合法的なポインタと勘違いされる.
解決方法:ポインタを解放した後に直接NULLを割り当てる
(3)ポインタ操作は変数の作用範囲を超えている.このような状況は防ぎようがない.
class A
{
public:
	void Func(void){ cout << “Func of class A” << endl; }
};
void Test(void)
{
	A *p;
	{
		A a;
		p = &a; //    a     
	}
	p->Func(); // p  “   ”
}

関数Testは文p->Func()を実行するとオブジェクトaが消え,pはaを指すのでpは「野ポインタ」となる.しかし、不思議なことに、私がこのプログラムを実行したときにエラーがなかったのは、コンパイラと関係があるかもしれません.