C/C++メモリ割り当て方式

7742 ワード

会社の筆記試験の問題を作ったばかりで、メモリが強化されなければならないことを示しています.
回転メモリ割り当てのアラーム.
メモリわりあてほうしき
1.メモリの割り当て方法は3種類あります.
[1]静的記憶領域からの割当て.
メモリはプログラムのコンパイル時にすでに割り当てられており、この内にプログラムが存在する全実行期間が存在します.たとえばグローバル変数、static変数です.
[2]スタックに作成します.
関数を実行すると、関数内のローカル変数のメモリセルがスタック上に作成され、関数の実行が終了すると、これらのメモリセルが自動的に解放されます.スタックメモリ割り当て演算はプロセッサの命令セットに組み込まれており,効率は高いが,割り当てられたメモリ容量は限られている.
[3]スタックから割り当てられ、動的メモリ割り当てとも呼ばれる.プログラムは実行時にmallocまたはnewで任意のメモリ数を申請し、プログラマー自身がfreeまたはdeleteでメモリをいつ解放するかを担当します.動的メモリの生存期間はプログラマーによって決定され、非常に柔軟に使用されますが、スタックにスペースが割り当てられている場合は、それを回収する責任があります.そうしないと、実行されるプログラムにメモリ漏れが発生し、異なるサイズのスタックスペースを頻繁に割り当て、解放すると、スタック内のクラックが発生します.
2.プログラムのメモリ容量
次の図に示すように、1つのプログラムがオペレーティングシステムに割り当てられたメモリブロックを4つの領域に分けます.
コード領域(codearea)プログラムメモリ領域
グローバルデータ領域(dataarea)
ヒープゾーン
スタックエリア
C/C++コンパイルされたプログラムによって消費されるメモリは以下の部分に分けられます.
1、スタック領域(stack)はコンパイラによって自動的に割り当てられて解放され、関数を実行するために割り当てられた局所変数、関数パラメータ、戻りデータ、戻りアドレスなどが格納される.その動作は、データ構造内のスタックに似ています.
2、ヒープエリア(heap)は一般的にプログラマーによって割り当てられて解放され、プログラマーが解放されなければ、プログラム終了時にOSによって回収される可能性がある.割り当て方法はチェーンテーブルに似ています.
3、グローバル領域(静的領域)(static)はグローバル変数、静的データ、定数を格納する.プログラム終了後にシステム解放
4、文字定数領域定数文字列はここに置いてあります.プログラムが終了するとシステムから解放されます.
5、プログラムコード領域は関数体(クラスメンバー関数とグローバル関数)のバイナリコードを保存する.
次に例のプログラムを示します.


  int a = 0;//        
  char *p1;//         

  int main(){ 
    int b; //   
    char s[] =\"abc\"; //   
    char *p2;//   
    char *p3 =\"123456\"; //123456\\0     , p3    。 

    static intc =0;//   (  )     
    
    p1 = newchar[10]; 
    p2 = newchar[20];

    //                。 
    strcpy(p1,\"123456\"); //123456\\0      ,          p3      \"123456\"        。

  } 

3.スタックとスタックの比較
1申請方式
stack:システムによって自動的に割り当てられます.例えば、関数内の局所変数int bを宣言する.システムは自動的にスタックの中でbのために空間を開く.
heap:プログラマーが自分で申請し、サイズを指定する必要があります.Cではmalloc関数、C++ではnew演算子です.

    p1 =(char *)malloc(10); p1 = new char[10]; 
    p2 =(char *)malloc(10); p2 = new char[20]; 

ただし、p 1、p 2自体はスタック内にあることに注意してください.
2申請後のシステムの応答
スタック:スタックの残りの領域が申請された領域より大きい場合、システムはプログラムにメモリを提供します.そうしないと、例外プロンプトスタックがオーバーフローします.
≪ヒープ|Heap|emdw≫:まず、オペレーティング・システムに空きメモリ・アドレスを記録するチェーン・テーブルがあることを知っておく必要があります.システムがプログラムの申請を受信すると、チェーン・テーブルを巡回し、申請した空間よりも大きな最初の空間のヒープ・ノードを探し、その後、そのノードを空きノード・チェーン・テーブルから削除し、そのノードの空間をプログラムに割り当てます.
ほとんどのシステムでは、このメモリ領域のヘッダアドレスに今回の割り当てのサイズが記録されます.これにより、コードのdelete文がこのメモリ領域を正しく解放できます.
見つかったスタックポイントのサイズが申請のサイズに等しいとは限らないため、余分な部分を空きチェーンテーブルに自動的に再配置します.
3応募サイズの制限
スタック:Windowsの下で、スタックは低アドレスに拡張されたデータ構造で、連続したメモリの領域です.スタックトップのアドレスとスタックの最大容量はシステムで予め定められており、WINDOWSではスタックの大きさは2 M(1 Mということもあり、つまり1つのコンパイル時に決定される定数)であり、申請された空間がスタックの残りの空間を超えるとoverflowが提示される.したがって、スタックから得られる空間は小さい.
ヒープ:ヒープは高アドレスに拡張されたデータ構造で、不連続なメモリ領域です.これは,システムがチェーンテーブルで格納する空きメモリアドレスであるため,当然不連続であり,チェーンテーブルの遍歴方向は低アドレスから高アドレスである.スタックのサイズは、コンピュータシステムで有効な仮想メモリに制限されます.このことから,スタックが得られる空間は比較的柔軟であり,比較的大きいことが分かる.
4出願効率の比較
スタックはシステムによって自動的に割り当てられ、速度が速い.しかし、プログラマーはコントロールできません.
スタックはnewで割り当てられたメモリで、一般的に速度が遅く、メモリの破片が発生しやすいが、最も使いやすい.
また、WINDOWSでは、VirtualAllocでメモリを割り当てるのが一番です.彼はスタックでもスタックでもないので、プロセスのアドレス空間に直接高速メモリを残しておくのが一番不便ですが.しかし、スピードが速く、最も柔軟です.
5スタックおよびスタック内のストレージ
スタック:関数呼び出し時に、最初にスタックに入ったのは主関数の次の命令(関数呼び出し文の次の実行可能文)のアドレスで、それから関数の各パラメータで、ほとんどのCコンパイラでは、パラメータは右から左へスタックに入って、それから関数数の中の局所変数です.静的変数はスタックに入らないことに注意してください.
今回の関数呼び出しが終了すると、ローカル変数は先にスタックを出て、それからパラメータで、最後にスタックトップポインタは最も最初に保存されたアドレス、つまりメイン関数の次の命令を指し、プログラムはこの点から実行を継続します.
ヒープ:通常、ヒープのヘッダにヒープのサイズを1バイトで格納します.スタックの具体的な内容はプログラマーが手配します.
6アクセス効率の比較

  char s1[] =\"a\"; 
  char *s2 =\"b\"; 

aは運転時刻に付与される.bはコンパイル時に決定される.しかしながら、以降のアクセスでは、スタック上の配列はポインタが指す文字列(例えばスタック)よりも速い.例:


int main(){

  char a = 1;
  char c[] =\"1234567890\"; 
  char *p=\"1234567890\"; 

  a = c[1]; 
  a = p[1]; 

  return 0; 
} 

対応するアセンブリコード


10: a =c[1]; 
00401067 8A4D F1 mov cl,byte ptr [ebp-0Fh] 
0040106A 884D FC mov byte ptr [ebp-4],cl 

11: a =p[1]; 
0040106D 8B55 EC mov edx,dword ptr [ebp-14h] 
00401070 8A42 01 mov al,byte ptr [edx+1] 
00401073 8845 FC mov byte ptr [ebp-4],al 

1つ目は、読み出し時に文字列の要素をレジスタclに直接読み出し、2つ目はポインタ値をedxに読み出し、edxに基づいて文字を読み出すので、明らかに遅い.
7まとめ
スタックとスタックの主な違いは、次の点です.
1、管理方式が違う;
2、空間の大きさが違う.
3、破片ができるかどうか.
4、成長方向が違う.
5、分配方式が違う;
6、分配効率が違う;
管理方式:スタックにとって、コンパイラによって自動的に管理され、手動で制御する必要はありません.スタックにとって、解放作業はプログラマーによって制御され、memory leakが発生しやすい.
空間サイズ:一般的に32ビットシステムでは、スタックメモリは4 Gの空間に達することができ、この観点からスタックメモリはほとんど制限されていない.しかし、スタックについては、一般に一定の空間サイズがあり、例えば、VC 6の下では、デフォルトのスタック空間サイズは1 Mである.もちろん、この値は変更できます.
フラグメントの問題:スタックにとって、頻繁なnew/deleteはメモリ空間の不連続をもたらし、大量のフラグメントをもたらし、プログラムの効率を低下させるに違いない.スタックにとって、この問題は存在しません.スタックは先進的な後出キューであるため、スタックの間からメモリブロックがポップアップされることは永遠に不可能です.彼がポップアップする前に、彼の上の後進的なスタックの内容がポップアップされ、詳細はデータ構造を参照することができます.
成長方向:スタックにとって、成長方向は上向き、すなわちメモリアドレスが増加する方向である.スタックの場合、その成長方向は下向きであり、メモリアドレスが減少する方向に成長します.
割り当て方法:スタックはすべて動的に割り当てられ、静的に割り当てられていないスタックです.スタックには、静的割当てと動的割当ての2つの割当て方式があります.静的割当ては、ローカル変数の割当てなど、コンパイラによって完了します.動的割り当てはmalloca関数によって割り当てられますが、スタックの動的割り当てとスタックは異なり、彼の動的割り当てはコンパイラによって解放され、手動で実現する必要はありません.
分配効率:スタックは機械システムが提供するデータ構造であり、コンピュータは底層でスタックにサポートを提供する:専門のレジスタ格納スタックのアドレスを分配し、スタックを押し出すには専門の命令が実行され、これはスタックの効率が比較的高いことを決定した.スタックはC/C++関数ライブラリで提供され、そのメカニズムは複雑です.例えば、メモリを割り当てるために、ライブラリ関数は一定のアルゴリズム(具体的なアルゴリズムはデータ構造/オペレーティングシステムを参照できます)に従って、スタックメモリの中で利用可能な十分なサイズの空間を検索します.十分なサイズの空間がなければ(メモリの破片が多すぎるためかもしれません)、システム機能を呼び出してプログラムデータセグメントのメモリ領域を増やすことができ、十分なサイズのメモリに分けて返す機会があります.明らかに、スタックの効率はスタックよりずっと低い.
ここから、スタックはスタックに比べてnew/deleteの使用が大量になるため、メモリの破片が大量に発生しやすいことがわかります.専門的なシステムサポートがないため、効率が低い.ユーザ状態とコア状態の切り替えを引き起こす可能性があるため、メモリの申請は、より高価になります.したがって、スタックはプログラムの中で最も広く応用されており、関数の呼び出しでもスタックを利用して完了し、関数呼び出し中のパラメータ、戻りアドレス、EBP、ローカル変数はスタック方式で保存されている.だから、スタックではなくスタックを使うことをお勧めします.
スタックには多くのメリットがありますが、スタックに比べてそれほど柔軟ではないため、大量のメモリスペースを割り当てる場合があります.スタックを使うほうがいいです.
スタックでもスタックでも、境界現象の発生を防止しなければならない(故意に境界を越えない限り).境界を越えた結果はプログラムが崩壊するか、プログラムのスタック、スタック構造を破壊し、予想外の結果を生むからだ.
4.new/deleteとmalloc/freeの比較
C++の観点から、new割り当てスタック空間を使用してクラスのコンストラクション関数を呼び出すことができますが、malloc()関数は単なる関数呼び出しであり、コンストラクション関数は呼び出されません.パラメータはunsigned longタイプです.同様にdeleteはスタック空間を解放する前に構造関数を呼び出しますが、free関数は呼び出しません.


class Time{
public: 
  Time(int,int,int,string);
  ~Time(){ 
    cout<<\"callTime\'s destructor by:\"<<name<<endl; 
} 

private: 
  int hour; 
  int min; 
  int sec; 
  stringname; 
}; 

Time::Time(inth,int m,int s,string n){ 
  hour=h; 
  min=m; 
  sec=s; 
  name=n; 

  cout<<\"callTime\'s constructor by:\"<<name<<endl; 
} 

int main(){

  Time *t1; 
  t1=(Time*)malloc(sizeof(Time));
  free(t1); 
  
  Time *t2; 
  t2=newTime(0,0,0,\"t2\"); 
  delete t2; 

  system(\"PAUSE\");
  returnEXIT_SUCCESS; 
} 

結果:
callTime\'s constructor by:t2 

callTime\'s destructor by:t2 

結果から、new/deleteを使用すると、オブジェクトのコンストラクション関数とコンストラクション関数を呼び出すことができ、例ではデフォルトではないコンストラクション関数を呼び出すことができます.ただし、スタックにオブジェクト配列を割り当てる場合は、デフォルトのコンストラクション関数のみが呼び出され、他のコンストラクション関数は呼び出されません.
本文はCSDNブログから:http://blog.csdn.net/rujielaisusan/archive/2009/09/30/4622197.aspx