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