
続C言語教室 - 第16回 動的メモリの使い方(その4)
古き良きプログラミング言語であるFORTRANやCOBOLではコンパイル時に必ずすべての変数の領域が決定されていて、実行時に変更する術はありませんでした。LISPは、その言語仕様からリストを自由に作ったり変更できることから、動的なメモリの使い方をした最初の言語なのかもしれません。もっともBASICなどのインタプリタ言語であれば、そもそも実行時に1行ずつ解釈するので最初からメモリは動的に確保するものでした。その中で ALGOL で動的にメモリを確保したり実行時に動作を決定する機構が導入され、これがPL/IやC言語にも影響を与えたものと思われます。
C言語における動的メモリはヒープ領域と呼ばれるメモリ空間から割り当てられます。ヒープ領域はいわゆる静的なデータ領域やスタックとして使われるメモリとは独立した領域で、多くの場合、BSS領域の続きを割り当てられます。

(IA-32などセグメントを持っている場合はセグメントごとに領域が割り当てられる)
ヒープ領域とスタック領域は、プログラムを実行している間に必要に応じてそれぞれ上へ、下へ伸ばされて使われます。この境界を指定しているのがOSが管理している brk や sbrk というアドレスで、ヒープ領域が不足した場合、これらのアドレスを適切に変更することで使える動的メモリを増やすことが出来ます。そして無闇に増やして他の目的で使われているアドレスに食い込まないようにもしてくれているのですね。
第27章 brk / sbrk (unistd.h)
brk, sbrk - データセグメントのサイズの変更する
System Programming with C - Memory Management with brk and sbrk
これでどの範囲のアドレスをヒープとして管理しているかの全体像はわかったと思いますが、問題なのはこの領域の中で、空いているところはどの部分なのかという管理の方法です。初期の実装では空いているアドレスの領域を単純なリンクリストで管理していたのですが、この方法ではメモリの割当と解放を繰り返すに連れ、空き領域が細かく分断(フラグメント化)され適切な空き領域を探すのにも時間がかかるようになってしまいます。そこで空き領域の大きさごとに管理方法を変えるような汎用的なコードが作られ、C言語はもちろん多くの動的メモリを扱う言語で使われるようになりました。
malloc
動的メモリの割当と解放を繰り返すことにより、空き領域の状態がどのように変化していくかについては、以下のサイトでわかりやすく説明してくれています。
プログラムを動かす時にメモリがどのように割り当て・解放されるのかをめちゃくちゃわかりやすいイメージ画像で解説してくれるサイト「Memory Allocation」
動的メモリの割当には、必要な領域の探索と割り当てた領域に対する空き領域を管理しているリストの更新が必要になるので、ループの内側などで細かく割り当てるのではなく、可能な限りまとめて取得して使い回すほうが効率が良いのは確かです。
あなたのメモリはどこから来てる?malloc入門
mallocの動作を追いかける(main_arenaとsbrk編)
malloc(3)のメモリ管理構造
普段はライブラリがどのようにメモリを管理しているのかを知る必要は無いのですが、運悪くヒープが破壊されるようなバグに遭遇してしまった場合、これを追っかける必要が出ることもあり、その内部構造を知る必要が出てくることもあるにはあります。そんな場合には組み込まれているライブラリの構造を調べて使われているリストなりを追っかけることもあるのですが、それよりもデバッグしやすいオリジナルのメモリ割り当てを用意して、置き換えて使うということもすることがあります。そんな時の為にも自分で実装してみるのは良い経験になります。
malloc関数の簡単な実装例
このようにすべてのコードを自分で書くことはしなくても、既存のライブラリに対して、一旦、デバッグ情報などを記録する自分の書いたコードを経由して元のライブラリを呼び出すという方法はよく採ります。例えばメモリの割当が失敗するケースは最近では十分なメモリがあるために稀で、多くの場合にエラー処理がきちんと書かれておらず「いざ」メモリが足りなくなった時におかしな動作をすることもあります。そこで「常にメモリ不足になる」コードに置き換えてエラー処理の有無や、その場合に起こることを調べたりするのですが、「常に」だと特定の場所でエラーになってしまい、その後の処理に対するテストが出来ないので「稀に」失敗するコードを用意することもあります。探せばこのような動作をするデバッグ用のコードも見つかるはずですが、ヒープでトラブルを起こすのは、それなりによく発生し、その原因を調べるのがなかなか難しいので、その「作り」に詳しくなることはとても役に立ちます。
さて、動的メモリに関しては経験者であれば誰しも一家言を持っているとは思いますが、そろそろお腹いっぱいな感じもするので、次に行きましょうか。
ヘッダ画像は、AIで生成しました。
#プログラミング #C言語 #プログラミング講座 #C言語教室 #動的メモリ #ヒープ #アドレス空間 #brk #sbrk #malloc #デバッグ関数
いいなと思ったら応援しよう!
