C言語でよく見られるメモリエラーと解決方法


よくあるエラー
メモリに関するいくつかの知識はメモリ割り当てに記載されており、一般的なメモリエラーと対策を記録し、共有します.
タイプ1:メモリの割り当てに成功しませんでしたが、使用しました.
正方形:使用する前にポインタがNULLであるかどうかを確認します.
1)ポインタpが関数のパラメータである場合,関数入口で文assert(p!=NULL)で断言検査を行う.
2)mallocまたはnewを使用してメモリを申請する場合はif(p!=NULL)でエラーチェックを行うべきである.
タイプ2:まだ初期化されていないポインタを参照
原因:メモリのデフォルト初期値は何なのか統一的な基準がなく、使用する前に初期化されます.
1)初期化の観念がない.
2)メモリのデフォルト値は未定義、すなわちゴミ値です.
タイプ3:境界を越えた操作メモリ
原因:メモリ割り当ては成功し、初期化されましたが、境界を越えた操作は許可されません.
たとえば、配列を使用する場合、特にforループ文の場合、下付きの「マルチ1」または「少ない1」が頻繁に発生します.
タイプ4:メモリを解放するのを忘れ、メモリが漏れる.
原因:このタイプのエラーを含む関数は、呼び出されるたびにメモリが失われます.メモリが十分な場合、このエラーの影響は表示されません.メモリが消費されると、「メモリが消費されました」というプロンプトが表示されます.したがって、ダイナミックメモリの申請と解放はペアリングされ、プログラム内のmallocとfreeの使用回数は同じである必要があります.
タイプ5:メモリを解放しても使用し続ける
原因:対応する場合は2種類あります
1)スタック内の変数が関数の終了後に自動的に破棄されるため、スタックメモリのポインタまたは参照が返されます.
2)あるメモリがfreeされた後、そのメモリへのポインタがNULLに設定されていないため、「野ポインタ」が発生する.
ルールの使用
コードの堅牢さと安全性を保証するために、次のルールを参照してください.
ルール1:malloc申請のメモリを使用する場合は、対応するポインタがNULLであるかどうかをすぐに確認する必要があります.
ルール2:配列とダイナミックメモリを初期化します.
ルール3:配列やポインタの下に境界を越えないようにします.
ルール4:ダイナミックメモリの申請と解放は、メモリの漏洩を防ぐためにペアを合わせなければならない.
ルール5:freeメモリを解放した後、ポインタをNULLに設定し、野ポインタの発生を防止します.
いくつかの重要な概念
1.野ポインタ
コンセプト:「野ポインタ」はNULLポインタではなく、「ゴミ」メモリを指すポインタです.すなわち、ポインタが指す内容は不確定である.
発生原因:1)ポインタ変数が初期化されていません.したがって、ポインタ変数を作成するときは、NULLまたは正当なメモリセルを指します.
2)ポインタpがfreeされた後,NULLとして置かれず,pを合法的なポインタと勘違いさせる.
3)ポインタが適法範囲を越えて動作する.スタックメモリへのポインタや参照は返さないでください.
例1-1:初期化されていないポインタを参照
char *p;
*p = 'A';//error,p     

例1-2:return文は「スタックメモリ」へのポインタを返す
char *GetString1(void)
{
	char p[] = "hello world!";
	//p   ,           

	return p;//error,        
}

例1-3:リリースされたメモリを使用
char *pstr = (char *)malloc(sizeof(char)*100);
free(pstr);	//pstr        
if (NULL !=pstr)//     
{
	strcpy(pstr,"string!");//error,           ,      
}

注意:free()はポインタが指すメモリを解放します!ポインタ変数ではありません!これはとても重要です!ポインタは変数であり、プログラムが終了したときにのみ破棄されます.メモリスペースを解放すると、このスペースを指すポインタが存在します!ただし、現在ポインタが指している内容のゴミは、定義されていないので、ゴミと言います.したがって、前述したように、メモリを解放した後にNULLにポインタを向け、ポインタが後でうっかり参照されないようにします.
次の例と比べて理解を深める
例1-4:関数戻り値伝達動的メモリ
char* GetMemory(int num)
{
	char *p = (char *)malloc(sizeof(char) * num);
	return p ;//ok,        
}

例1-5:
char *GetString(void)
{
	char *p = "hello world!";
	//    p   ,          

	return p;//ok,        
}

2.メモリリーク
コンセプト:ダイナミックメモリ割り当て関数でダイナミックに開いたスペースは、使用後に解放されず、プログラムが終了するまでメモリユニットを占有し続けます.
注意:メモリ漏洩とは、スタックメモリの漏洩です.その一般的な表現は、プログラムの実行時間が長ければ長いほど、メモリの消費量が多くなり、最終的にすべてのメモリを使い果たし、システム全体がクラッシュすることです.
例2-1:メモリリーク、合計9*100バイトリーク発生
void Test(void)
{
	char *p = NULL;
	for ( int i = 0; i<10; i++)
	{
		p = (char*)malloc(100);//           ,          
	}
	
	strncpy(p,"string!");
	free(p);
}

3.メモリオーバーフロー
コンセプト:システムが割り当てたメモリが不足してデータを置くことができず、メモリオーバーフローと呼ばれます.
例3-1:運転時プロンプトエラー
char str[10]={0};

strcpy(str,"hello world!");//error!