問題の歴史:
動的メモリ割り当てのメカニズムは、<stdlib.h>の標準ライブラリ関数malloc/freeとともにCに登場しました。これにより、可変サイズの構造体、複雑なコレクション、およびオブジェクトの実装が可能になり、言語の発展と大規模プログラミングにおけるその使用が大きく促進されました。
問題:
動的メモリの操作は、プログラマに対してオブジェクトのライフサイクルに対する完全な制御を要求します。エラー(メモリリーク、二重解放、ポインタの不正使用)は、プログラムのクラッシュや脆弱性(例えば、use-after-freeのバグを利用した攻撃)を引き起こす可能性があります。
解決策:
— malloc/calloc/reallocの戻り値を常に確認する。割り当てが失敗した場合、NULLが返されます。 — メモリを解放する際には、解放されたブロックが使用されないようにポインタをNULLに設定する。 — freeの後にポインタを使用しない。 — malloc/freeとcalloc/freeの対応を正確に保つ。
コード例:
#include <stdio.h> #include <stdlib.h> int main() { int *arr = malloc(5 * sizeof(int)); if (!arr) { perror("メモリの割り当てに失敗しました"); return 1; } for (int i = 0; i < 5; ++i) arr[i] = i * i; for (int i = 0; i < 5; ++i) printf("%d ", arr[i]); printf(" "); free(arr); arr = NULL; return 0; }
重要な特徴:
mallocを使って割り当てたメモリをdelete演算子で解放した場合、あるいはその逆(C++の場合)はどうなりますか?
メモリの割り当てと解放のメカニズムを言語間で混在させることはできません(C/C++)。Cではmalloc/freeのみ、C++ではnew/deleteです。
free(NULL)を呼び出すとどうなりますか?
free(NULL)は安全です(C標準によって保証されています)。この呼び出しは何もしません。
reallocはメモリブロックを拡張または縮小するために使用できますか?元のポインタはどうなりますか?
reallocはメモリブロックを移動する場合があり、移動した場合、古いポインタは無効になります。常に新しいポインタを代入してください:
ptr = realloc(ptr, new_size);
ループで配列のメモリを割り当てたが、イテレーションの最後で解放するのを忘れました。一晩でサーバー上でプログラムが全てのRAMを消費しました。
利点:
欠点:
ループ内で使用後は必ずメモリを解放し、 mallocの直後にはNULLチェックを行い、メモリリークを監視するためにデバッギングツールを使用しました。
利点:
欠点: