C言語:GetMemory動的申請メモリ問題

19700 ワード

文章はC言語が関数を使ってメモリを申請するいくつかの情況について解析して、テーマに入る前に壁が裂けてCプログラムのメモリの分布を理解することをお勧めします:
①パラメータとしてポインタを使用
void GetMemory(char *p)
{
	p = (char *)malloc(100);
}
void Test(void) 
{
	char *str = NULL;
	GetMemory(str);   
	strcpy(str, "hello world");
	printf(str);
}

解析:エラー.呼び出し時に渡されるパラメータはstrのバックアップです.バックアップである以上、関数内部がどのように操作されても、そのバックアップを操作するだけで、元のstr値とは関係ありません.したがって、TestのstrはGetMemoryを経ても元の定義時のNULLであり、strcpy文字列をNULLに使用すると当然セグメントエラーが発生する.また、7行目がNULLに初期化されていなければコンパイル時にエラーは報告されませんが、それが野ポインタ野ポインタになっているのでしょうか、野ポインタを操作するのは危険です.
②ポインタのポインタをパラメータとして使用
void GetMemory(char **p)
{
	*p = (char *)malloc(100);
}
void Test(void)
{
	char *str = NULL;
	GetMemory(&str);
	strcpy(str, "hello"); 
	printf(str);
}

解析:一般的にメモリに正常に申請でき、helloも正常に印刷できるが、十分ではない.原因は以下の通りである:1、申請メモリは成功したかどうかを判断することに注意しなければならない.申請メモリが失敗した場合は少ないが、if(*p==NULL){}でエラー処理を行うべきである.2、申請「ヒープ(heap)」の中のメモリは手動で解放することを覚えておいてください.ここで解放しないとメモリが漏れることが発生し、メモリも使えば使うほど少なくなります.①と②:値を変えるためのポインタ、ポインタを変えるためのポインタ.
③スタックメモリアドレスを返す
char *GetMemory(void)
{  
	char p[] = "hello world";
	return p;
}
void Test(void)
{
	char *str = NULL;
	str = GetMemory();
	printf(str);
}

解析:エラー、char p[]は関数内部のローカル自動変数であり、「スタックメモリ(stack)」に存在し、GetMemory関数の実行が終了するとpのメモリが解放され、呼び出し元に戻って操作すると自然にエラーが発生します.この場合、通常戻り値のある関数が競合しているかどうかを考えることができます.たとえば、次のコードがあります.
#include 

int myfun(void)
{
	int x = 100;
	return x;
}

int main()
{
	int y = myfun();
	y = 200;
	printf("y = %d
"
, y); return 0; }

これは競合しません.関数が返す値は同じ「バックアップ」ですが、GetMemoryとmyfunが返す値のタイプは異なります.前者は配列(のヘッダアドレス)のバックアップを返し、後者は変数値のバックアップを返します.(配列を知る原理があれば分かるが、配列名は配列ヘッダアドレスを指すポインタ定数に類比できるが、sizeofを使用する場合は類比できない.)したがって、関数が返されると「スタックメモリ」のアドレスが得られます.前述したように、関数の実行が完了すると、局所的な自動変数が自動的に解放され、pが指すアドレスのこのメモリはプログラマーに使用されなくなり、なぜエラーが発生したのか理解しやすくなります.
④静的メモリアドレスを返す
char *GetMemory(void)
{  
	static char p[] = "hello world";
	return p;
}

void Test(void)
{
	char *str = NULL;
	str = GetMemory();
	printf(str);
}

解析:正しくは、解放されたスタックメモリアドレスを返すとエラーが発生します.静的メモリアドレスを返す(プログラムの実行中ずっと存在する)ことは問題ありません.③と④:関数を組み合わせて、静的と定義しない限り、局所変数アドレスを返すことはできません.
⑤文字列定数アドレスを返す
char *GetMemory(void)
{  
	char *p = "hello world";
	return p;
}
void Test(void)
{
	char *str = NULL;
	str = GetMemory();
	printf(str);
}

解析:出力は正常です.ただし、返されるこのアドレスは定数領域にあり、読む方法でしか操作できず、書くことができません.そうしないとエラーになります.
⑥文字列を返す
char *GetMemory(void)
{  
	return "hello world";
}
void Test(void)
{
	char *str = NULL;
	str = GetMemory();
	printf(str);
}

解析:前者と同様.
⑦メモリを直接申請してリリースする
void Test(void)
{
	char *str = (char *) malloc(100);
	strcpy(str, "hello");
	free(str); 
	if(str != NULL) 
	{
		strcpy(str, "world"); 
		printf(str);
	}
}

解析:実行は成功したが、この操作は論理的ではなく、「world」が正常に出力される可能性があり、文字化けして出力される可能性もある.まず注意しなければならないのは、1、申請メモリは申請が成功したかどうかを判断することに注意しなければならない.2、free解放メモリとstr==NULLを理解することは実は2つのことである.freeメモリは「スタックメモリ」というリリースされますが、アドレスは変更されていません.つまりNULLではありません.このアドレスは、引き続き使用しないことをお勧めします.「野ポインタ」のようにメモリに割り当てられていないので、strを手動で空のポインタ(str=NULL)に変更する必要があります.後で誤操作を避けることができます(NULLアドレスを操作するとコンパイラがエラーを報告するためです).総じて、freeの後はそのメモリの使用を推奨しないことを表し、空のポインタになるには手動でNULLに設定する必要がある.プログラムに戻って、freeがメモリを解放した後、strがNULLであるかどうかを判断し、明らかにNULLではないが、すでに解放されているので、操作するのは使用できないメモリであり、操作できるが提案しない.このような不正な操作は結果として確定できません.
⑧freeリリースアドレスが一致しない
void Test(void)
{
	char *str = (char *) malloc(100);
	strcpy(str, "hello");
	printf(str);
	str++;
	free(str); 
	str = NULL;
}

解析:エラー、mallocはfreeとペアで現れるだけでなく、同じアドレスで処理されます.
⑨正しいやり方(一つ)
char *GetMemory(void)
{  
	char *p = (char *)malloc(100);
	return p;
}
void Test(void)
{
	char *str = NULL;
	str = GetMemory();
	strcpy(str, "hello");
	printf(str);
	free(str);
	str = NULL;
}

1、関数内部の自動変数はスタックメモリ(stack)に格納され、スタック内に関数の実行が終了するとシステムによって自動的に解放される.2、malloc関数の動的申請を呼び出すメモリはスタック(heap)にあるで、ヒープメモリはプログラムの中で手動freeを必要として、さもなくばずっとプログラムの運行が終わるまで占有します;3、文字列の定数は定数区に記憶して、このメモリは読み書きすることしかできません;4、グローバル変数と静的変数はすべて静的データ区に記憶して、このメモリはプログラムの運行が始まるまで運行が終わるまでずっと占有しています.5、freeはメモリと空を解放しますポインタNULLは2つの異なるものです.