このテキストは、運営している韓国のブログを日本語に翻訳したものです。

韓国のブログ:https://yunsuu.notion.site/e379eccd32c545419f0651f743c12c13?v=35310b282de24cc5a292fe45cc498a32&pvs=74

 

この記事は「Operating Systems: Three Easy Pieces」を読んでまとめた本です。

著者が無料で公開しているので、興味のある方はぜひ読んでみてください。

本にある良い問題は解いてgithubにアップロードしています。

github:https://github.com/yunsuu/ostep-homework-solution 

本のリンク:https://pages.cs.wisc.edu/~remzi/OSTEP/

メモリの割り当てはどのように行うべきか?

メモリの種類

スタックメモリ

void func() {
	int x; // スタック上に整数を宣言する
	...
}

コンパイラによって自動的に返され、自動メモリとも呼ばれます。関数を宣言すると変数のスタックメモリストレージが作られ、関数がリターンされると再びメモリが返されます。

ヒープメモリ

void func() {
	int *x = (int *) malloc(sizeof(int));
	...
}

ユーザが明示的に割り当てます。スタックメモリと同時に使用されることもあります。上記のメモリは多くのバグの原因となります。上記のコードでは、mallocでヒープ割り当てが発生します。ヒープを割り当てた後、そのアドレスを返し、スタックメモリに保存されているxの領域に返されたアドレスを保存します。

malloc()

malloc()はメモリを動的に割り当てる関数です。

double *d = (double *) malloc(sizeof(double));

sizeofについて

int *x = malloc(10 * sizeof(int));
printf("%d\\\\n", sizeof(x));

配列のサイズに関係なく、32bitコンピュータなら4、64bitコンピュータなら8が出力されます。動的に割り当てられたメモリのサイズがいくらであっても、sizeof関数は整数を指すポインタのサイズがいくらであるかを出力するからです。

int x[10];
printf("%d\\\\n", sizeof(x));

この場合、配列の数が10の場合は40、9の場合は36が出力されます。なぜなら、x[10]は動的に割り当てられたメモリではないからです。

free()

メモリの割り当てについて説明したので、次にメモリの解放について説明しましょう。メモリを解放するのはいつが最適でしょうか?

int *x = malloc(10 * sizeof(int));
...
free(x);

何言語かはfreeを使用せずに自動メモリ管理をサポートしています。この場合、ガベージコレクタが実行されてメモリが解放されます。

メモリの動的割り当て時に注意する点

メモリ管理はすべてのプログラマにとって重要なスキルです。特に動的メモリ割り当てを使用する際に注意すべきいくつかの点があります。これらの注意事項はプログラムの効率、安定性、安全性を大幅に向上させることができます。

メモリの割り当てを忘れる

動的メモリ割り当てとは、プログラムが実行時間中に必要なメモリを割り当てることを指します。時々、プログラマは必要なメモリを割り当てることを忘れることがあります。これは**NULL**ポインタ参照エラーや予期しない動作を引き起こす可能性があります。したがって、メモリを割り当てる際には常に確認手順を踏むことが重要です。

割り当てるメモリが不足している

割り当てるメモリのサイズが不足している場合、これはバッファオーバーランなどの深刻な問題を引き起こす可能性があります。例えば、ユーザが入力するデータのサイズを予測せずにメモリを割り当てると、入力データが割り当てられたメモリサイズを超える可能性があります。これを防ぐためには、常に十分なサイズのメモリを割り当て、可能な限り境界チェックを行う必要があります。

割り当てられたメモリを初期化しない

新しく割り当てられたメモリには任意のデータが含まれている可能性があります。初期化されていないメモリを使用すると、予測不能な結果を引き起こす可能性があります。したがって、動的に割り当てられたメモリは使用前に必ず初期化することが良いです。

メモリを解放しない

割り当てられたメモリを解放しないと、メモリリークが発生する可能性があります。これは特に長時間実行されるプログラムで深刻な問題を引き起こす可能性があります。したがって、使用が終わったメモリは常に**free()**関数などを使用して解放する必要があります。

メモリの使用が終わる前にメモリを解放する

メモリを早すぎる段階で解放することも問題になることがあります。これは、他の部分のコードがまだ該当のメモリを参照している場合に発生することがあり、これがプログラムの誤動作やクラッシュを引き起こす可能性があります。

メモリを繰り返し解放する

一度解放されたメモリを再度解放することは、プログラムに深刻なエラーを引き起こす可能性があります。二重解放は、しばしばメモリの破損やプログラムの予期せぬ終了につながることがあります。

free()を誤って呼び出す

  • *free()関数は動的に割り当てられたメモリを解放するために使用されます。しかし、free()を誤って使用すると、例えば動的に割り当てられていないメモリを解放しようと試みたり、誤ったポインタにfree()*を呼び出すと、プログラムに深刻な問題が発生する可能性があります。