LinuxプログラミングC++メモリ管理のポインタ伝達の詳細

4973 ワード

ポインタパラメータはどのようにメモリを渡しますか?
関数のパラメータがポインタである場合、そのポインタでダイナミックメモリを申請することを期待しないでください.例7-4-1では、Test関数の文GetMemory(str,200)はstrに所望のメモリを取得させなかったが、strはNULLのままであるのはなぜか.
void GetMemory(char *p, int num)
{
 p = (char *)malloc(sizeof(char) * num);
}
void Test(void)
{
 char *str = NULL;
 GetMemory(str, 100); // str     NULL
 strcpy(str, "hello"); //     
}

例ポインタパラメータでダイナミックメモリを申請しようとする
故障は関数GetMemoryにある.コンパイラは常に関数の各パラメータに一時的なコピーを作成し、ポインタパラメータpのコピーは_p,コンパイラ使_p = p.関数内のプログラムが変更されたら_pの内容は,パラメータpの内容を相応に修正する.これがポインタが出力パラメータとして使用できる理由です.この例では_pは新しいメモリを申請しましたが、ただ_pが指すメモリアドレスは変更されましたが、pは少しも変わりません.関数GetMemoryは何も出力できません実際、GetMemoryを実行するたびにメモリが漏洩します.freeでメモリを解放していないためです.
メモリをポインタパラメータで申請する必要がある場合は、例4.2を参照して、「ポインタを指すポインタ」に変更する必要があります.
void GetMemory2(char **p, int num)
{
 *p = (char *)malloc(sizeof(char) * num);
}
void Test2(void)
{
 char *str = NULL;
 GetMemory2(&str, 100); //       &str,   str
 strcpy(str, "hello");
 cout<< str << endl;
 free(str);
}

例ポインタを指すポインタでダイナミックメモリを申請する
「ポインタを指すポインタ」という概念は理解しにくいため、関数で値を返してダイナミックメモリを渡すことができます.この方法はもっと簡単だ.
char *GetMemory3(int num)
{
 char *p = (char *)malloc(sizeof(char) * num);
 return p;
}
void Test3(void)
{
 char *str = NULL;
 str = GetMemory3(100);
 strcpy(str, "hello");
 cout<< str << endl;
 free(str);
}

例:関数で値を返して動的メモリを渡す
関数で値を返してダイナミックメモリを渡す方法は使いやすいが、return文を間違って使う人が多い.ここでは、関数の終了時にメモリが自動的に消滅するため、return文で「スタックメモリ」へのポインタを返さないことを強調します.例を参照してください.
char *GetString(void)
{
 char p[] = "hello world";
 return p; //         
}
void Test4(void)
{
 char *str = NULL;
 str = GetString(); // str       
 cout<< str << endl;
}

例return文は、スタックメモリへのポインタを返します.
テスタでTest 4を逐次追跡したところ、str=GetString文を実行するとstrはNULLポインタではないが、strの内容は「hello world」ではなくゴミであることが分かった.
char *GetString2(void)
{
 char *p = "hello world";
 return p;
}
void Test5(void)
{
 char *str = NULL;
 str = GetString2();
 cout<< str << endl;
}

例return文は定数文字列を返します
関数Test 5の実行はエラーはありませんが、関数GetString 2の設計概念はエラーです.GetString 2内の「hello world」は定数文字列であり、静的記憶領域に位置し、プログラムのライフサイクル中は一定であるためです.GetString 2は、いつ呼び出されても、常に同じ読み取り専用メモリブロックを返します.
「野指針」を根絶する
「野ポインタ」はNULLポインタではなく、「ゴミ」メモリへのポインタです.一般的にNULLポインタを誤用することはありません.if文で判断しやすいからです.しかし、「野ポインタ」は危険で、if文は役に立たない.「野指針」の成因は主に2つあります.
(1)ポインタ変数は初期化されていません.作成されたばかりのときに自動的にNULLポインタになることはありません.デフォルト値はランダムで、むやみに指図されます.したがって、ポインタ変数は作成と同時に初期化されるべきです.ポインタをNULLに設定するか、正当なメモリに指し示すかのいずれかです.たとえば、
char *p = NULL;
char *str = (char *) malloc(100);

(2)ポインタpがfreeまたはdeleteされた後,NULLとして置かれず,pが合法的なポインタであると勘違いされる.
(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は「野ポインタ」となる.しかし、不思議なことに、私がこのプログラムを実行したときにエラーがなかったのは、コンパイラと関係があるかもしれません.
malloc/freeがあったらどうしてnew/deleteするの?
mallocとfreeはC++/C言語の標準ライブラリ関数であり、new/deleteはC++の演算子である.ダイナミックメモリの申請やメモリの解放に使用できます.
内部データ型以外のオブジェクトでは、maloc/freeだけではダイナミックオブジェクトの要件を満たすことはできません.オブジェクトは作成と同時にコンストラクション関数を自動的に実行し、オブジェクトは消滅する前にコンストラクション関数を自動的に実行します.malloc/freeは演算子ではなくライブラリ関数であるため、コンパイラ制御権限内ではなく、コンストラクション関数とコンストラクション関数を実行するタスクをmalloc/freeに押し付けることはできません.
したがって、C++言語には、動的メモリ割り当てと初期化作業を完了できる演算子newと、メモリのクリーンアップと解放作業を完了できる演算子deleteが必要です.注意new/deleteはライブラリ関数ではありません.まず、malloc/freeおよびnew/deleteがオブジェクトの動的メモリ管理をどのように実現するかを見てみましょう.例6を参照してください.
class Obj
{
 public :
  Obj(void){ cout << “Initialization” << endl; }
  ~Obj(void){ cout << “Destroy” << endl; }
  void Initialize(void){ cout << “Initialization” << endl; }
  void Destroy(void){ cout << “Destroy” << endl; }
};
void UseMallocFree(void)
{
 Obj *a = (obj *)malloc(sizeof(obj)); //       
 a->Initialize(); //    
 //…
 a->Destroy(); //     
 free(a); //     
}
void UseNewDelete(void)
{
 Obj *a = new Obj; //            
 //…
 delete a; //         
}

例malloc/freeとnew/deleteでオブジェクトのダイナミックメモリ管理をどのように実現するか
クラスObjの関数Initializeは構造関数の機能をシミュレートし,関数Destroyは構造関数の機能をシミュレートした.関数UseMallocFreeでは、malloc/freeではコンストラクション関数と解析関数を実行できないため、メンバー関数InitializeとDestroyを呼び出して初期化とパージを完了する必要があります.関数UseNewDeleteはずっと簡単です.
したがって、malloc/freeを使用してダイナミックオブジェクトのメモリ管理を完了しようとしないで、new/deleteを使用します.内部データ型の「オブジェクト」には、malloc/freeおよびnew/deleteは構造および解析のプロセスがないため、等価です.
new/deleteの機能がmalloc/freeを完全にカバーしている以上、なぜC++はmalloc/freeを淘汰しないのでしょうか.これは、C++プログラムがC関数を呼び出すことが多いためです.Cプログラムはmalloc/freeで動的メモリを管理するしかありません.
freeで「newで作成したダイナミックオブジェクト」を解放すると、そのオブジェクトは構造関数を実行できないため、プログラムエラーを引き起こす可能性があります.deleteで「malloc申請のダイナミックメモリ」を解放すれば、理論的にはプログラムは間違っていませんが、プログラムの可読性は悪いです.だからnew/deleteはペアで使用しなければなりません.malloc/freeも同じです.
(T114)