c言語のメモリ管理
メモリの割り当て
詳細の前に簡単に説明するとメモリには静的な領域と動的な領域があります。
静的な領域はメモリに読み出された時から大きさが変わらない固まったメモリ、動的な領域はメモリが読みだされた時にはどれくらいの大きさになるのかわからない柔軟な領域です。
静的領域
プログラムがメモリに読み込まれた瞬間、もっと言えばプログラムが完成した瞬間には使用する大きさがわかっている領域です。
コード領域
コンピューターを動かす命令群はifなどにより様々な処理に分岐していますが、すべての命令は予め決定しておりプログラムがメモリへ読み出された時点から変更することはありません。変数にも数値を定数として設定し書き換えてほしくないものもあります。
このような書き換えてほしくない命令とデータが配置されるメモリ領域です。
c言語だとコードやconst修飾されたものです。
静的変数
プログラムをメモリへ読み出した時に読み出されます。
コードや定数と違って変更が可能です。
具体的にはグローバル変数やstaticなローカル変数などがここに配置されます。
static
staticをつけると関数が一番最初に呼び出された時にだけ初期化されます。
関数が呼び出される旅に更新する必要がある情報にstatic修飾します。
例えば関数が呼び出された回数を記録したいとかそんな処理です。
動的領域
スタック
スタックはプログラムがメモリへ読み出された時には大きさはありません。
先入先出(LIFO: Last In First Out; FILO: First In Last Out)方式でデータがメモリに格納されます。
先入とはデータの上にデータを積んでいくことで格納した順にデータが積まれます。
先出とは積まれたデータを上から取り出していく方法でデータを積んだ順に取り出して行きます。
スタックはヒープ領域と反対向き、メモリアドレスの若い方へ成長していきます。
c言語だと関数内で使用されるローカル変数がこれに当たります。
関数の処理を終えるとスタックは解放されてしまうので長い時間情報を保持することはできません。
スタックオーバーフロー
スタック領域の大きさはプログラムを読み出した時に固定されてしまうので大きな配列をここへ置くとスタックの大きさを超えて他のメモリ領域へデータがあふれ出してしまいます。
これをスタックオーバーフローと呼びます。これがただデータがあふれただけならプログラムが異常終了するだけです。
しかしスタックには関数処理から抜けた後の戻りアドレス(次に実行する命令)が格納されています。
仮にその戻りアドレスが格納される場所を特定された場合スタックを溢れさせて命令を書き換え、危険な処理を実行させることもできてしまいます。
なので大きなデータはここへ配置すべきではありません。
大きなデータは以下のヒープ領域へ格納します。
ヒープ領域
スタックのように動的にメモリの大きさが変わります。
例えばファイルを読み込むメモ帳はユーザーがどのファイルを選ぶかを決めるまで必要なメモリのサイズは分かりません。
上記のコードエリアのように初めから大きさを決めてしまうとメモリが足りなくなったり、また余分にメモリを使用するといった資源の無駄が生じてしまいます。
そこであるファイルをクリックした時初めてメモリ大きさが決まるように実装しておくのです。
c言語の場合だとmalloc()でユーザープログラムがOSへメモリの割り当てを要求し、free()で使い終わったメモリをOSへ返却することができます。
メモ帳で書いた内容はHDDやSSDなどのストレージに保存しますが、一文字変更を加えるたびにストレージに書き込んでいるとメモリとストレージを行ったり来たりするのでオーバヘッドが大きくなってしまい処理が重くなってしまいます。
なので作業が終了して「保存」をクリックすまでストレージに保存することはありません。
データがストレージに保存するされるまでの間一時的にデータを保存しておくためのメモリがヒープ領域のバッファです。
既述したように巨大なデータはスタックではなくヒープ領域に配置すべきです。
コメント