OK mallocに関するいくつかの回答はすでに投稿されています。
さらに興味深いのは、free がどのように機能するかです(この方向では、malloc もよりよく理解できます)。
多くの malloc/free 実装では、free は通常、メモリをオペレーティング システムに返しません (または、少なくともまれなケースに限られます)。その理由は、ヒープにギャップが生じるため、2 GB または 4 GB の仮想メモリをギャップで締めくくってしまう可能性があるためです。仮想メモリが終了するとすぐに、非常に大きな問題が発生するため、これは避ける必要があります。もう 1 つの理由は、OS が特定のサイズと配置のメモリ チャンクしか処理できないことです。具体的に言うと、通常、OS は仮想メモリ マネージャが処理できるブロック (ほとんどの場合、512 バイトの倍数、たとえば 4KB) のみを処理できます。
そのため、OS に 40 バイトを返すことはできません。では、無料は何をするのでしょうか?
Free は、メモリ ブロックを独自の空きブロック リストに入れます。通常、アドレス空間内の隣接するブロックを融合しようとします。フリー ブロック リストは、最初に管理データを含むメモリ チャンクの単なる循環リストです。これは、標準の malloc/free で非常に小さなメモリ要素を管理することが効率的でない理由でもあります。すべてのメモリ チャンクには追加のデータが必要であり、サイズが小さいほど断片化が発生します。
free-list は、新しいメモリ チャンクが必要なときに malloc が最初に確認する場所でもあります。OS から新しいメモリを要求する前にスキャンされます。必要なメモリよりも大きなチャンクが見つかった場合、チャンクは 2 つの部分に分割されます。1 つは呼び出し元に返され、もう 1 つは空きリストに戻されます。
この標準的な動作には、さまざまな最適化があります (たとえば、メモリの小さなチャンクの場合)。しかし、malloc と free は非常に普遍的なものでなければならないため、代替手段が使用できない場合は、標準の動作が常にフォールバックになります。フリーリストの処理にも最適化があります。たとえば、サイズでソートされたリストにチャンクを格納します。ただし、すべての最適化には独自の制限もあります。
コードがクラッシュする理由:
その理由は、9 文字 (末尾の null バイトを忘れないでください) を 4 文字のサイズの領域に書き込むことによって、データのチャンクの「背後」にある別のメモリ チャンクに保存されている管理データをおそらく上書きするからです (ほとんどの場合、このデータはメモリ チャンクの「前」に格納されるためです)。その後、free がチャンクをフリー リストに入れようとすると、この管理データに触れて、上書きされたポインターに遭遇する可能性があります。これにより、システムがクラッシュします。
これはかなり優雅な振る舞いです。どこかのランナウェイ ポインタがメモリ フリー リストのデータを上書きし、システムがすぐにはクラッシュせず、後でいくつかのサブルーチンがクラッシュする状況も見てきました。中程度の複雑さのシステムであっても、このような問題をデバッグするのは非常に困難です。私が関与した 1 つのケースでは、メモリ ダンプで示された場所とはまったく異なる場所にあったため、クラッシュの原因を特定するのに数日かかりました。時限爆弾のようなものです。次の「free」または「malloc」がクラッシュすることはわかっていますが、その理由はわかりません!
これらは C/C++ の最悪の問題の一部であり、ポインターが非常に問題になる理由の 1 つです。