メモリリーク
リークを見つけて防ぎます。
「メモリリーク」はCoddyKit上の無料C Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはC Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 C Academyコースには全4レッスンが含まれています。
メモリリークとは
メモリリークとは、ヒープメモリを確保したまま解放せず、そのメモリへの唯一のポインターを失うことです。
メモリはプログラムが終了するまで確保されたままになり、リソースを浪費します。
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int *p = malloc(sizeof(int));
*p = 5;
printf("%d\n", *p);
free(p);
return 0;
}ポインターを失う
典型的なリークは、古いメモリを解放する前にポインターを新しい確保領域で上書きすることで発生します。
最初のブロックには到達できなくなります。
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int *p = malloc(sizeof(int));
free(p);
p = malloc(sizeof(int));
*p = 10;
printf("%d\n", *p);
free(p);
return 0;
}ループ内のリーク
各反復で解放せずにループ内でメモリを確保すると、リークが急速に増加します。
次の反復でポインターを上書きする前に解放することで修正できます。
#include <stdio.h>
#include <stdlib.h>
int main(void) {
for (int i = 0; i < 3; i++) {
int *p = malloc(sizeof(int));
*p = i;
printf("%d ", *p);
free(p);
}
printf("\n");
return 0;
}早期returnによるリーク
確保後に早期のreturnを実行すると、クリーンアップがスキップされることがあります。どの終了経路でも必ず解放してください。
#include <stdio.h>
#include <stdlib.h>
int process(int bad) {
int *p = malloc(sizeof(int));
if (bad) {
free(p);
return -1;
}
*p = 42;
int r = *p;
free(p);
return r;
}
int main(void) {
printf("%d\n", process(0));
return 0;
}gotoによるクリーンアップパターン
gotoで到達する単一のクリーンアップラベルを用意すると、すべてを解放する場所を1つにでき、重複したfree呼び出しを避けられます。
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int rc = 1;
int *p = malloc(sizeof(int));
if (!p) goto done;
*p = 7;
printf("%d\n", *p);
rc = 0;
done:
free(p);
return rc;
}すべての確保に対応させる
最も簡単なルールは、各malloc、calloc、reallocに対して、対応するfreeをちょうど1回行うことです。
#include <stdio.h>
#include <stdlib.h>
int main(void) {
char *a = malloc(8);
char *b = calloc(4, sizeof(char));
a[0] = 'x';
printf("%c %d\n", a[0], b[0]);
free(a);
free(b);
return 0;
}入れ子になった確保を解放する
構造体がヒープメモリを所有している場合は、外側のブロックを解放する前に内側のポインターを解放します。
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int *data;
} Box;
int main(void) {
Box *b = malloc(sizeof(Box));
b->data = malloc(3 * sizeof(int));
b->data[0] = 1;
printf("%d\n", b->data[0]);
free(b->data);
free(b);
return 0;
}ポインター配列を解放する
ポインターの配列を確保し、各要素がそれぞれ独自の確保領域を指している場合は、まずすべての要素を解放し、その後で配列を解放します。
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int n = 3;
int **rows = malloc(n * sizeof(int *));
for (int i = 0; i < n; i++) {
rows[i] = malloc(2 * sizeof(int));
rows[i][0] = i;
}
for (int i = 0; i < n; i++) free(rows[i]);
free(rows);
printf("freed all rows\n");
return 0;
}所有権を明確に管理する
各確保領域を所有し、解放する責任を持つ主体を決めます。呼び出し元が返されたポインターを解放する必要があるか分かるように、これを文書化してください。
#include <stdio.h>
#include <stdlib.h>
char *make_greeting(void) {
char *s = malloc(6);
s[0]='h'; s[1]='i'; s[2]='\0';
return s;
}
int main(void) {
char *g = make_greeting();
printf("%s\n", g);
free(g);
return 0;
}リークを見つけるツール
ValgrindやAddressSanitizerなどのツールは、すべてのメモリ確保を追跡し、解放されなかったものを示すことでリークを報告します。
実際のプログラムを検証するうえで不可欠です。
メモリ確保の適切なライフサイクル
要点をまとめると、確保、確認、使用、すべての経路での解放、そしてポインターのNULL化を行います。これにより、リークのないプログラムを維持できます。
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int *p = malloc(4 * sizeof(int));
if (!p) return 1;
for (int i = 0; i < 4; i++) p[i] = i + 1;
int sum = 0;
for (int i = 0; i < 4; i++) sum += p[i];
printf("sum = %d\n", sum);
free(p);
p = NULL;
return 0;
}理解度チェック
メモリリークについての理解を確認しましょう。
まとめ
メモリリークを防ぐ方法を学びました。
- すべてのメモリ確保に対して、ちょうど1回の
freeを対応させます。 - 早期returnやエラーを含め、すべての終了経路で解放します。
- 入れ子になった確保やポインター配列の要素は、内側から先に解放します。
- 所有権を明確に定義し、Valgrindなどのツールで検証します。
よくある質問
「メモリリーク」レッスンは無料ですか?
はい。「メモリリーク」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、C Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 C Academyコースには全4レッスンが含まれています。
「メモリリーク」で何を学びますか?
リークを見つけて防ぎます。 ブラウザで直接実行するハンズオンコードでC Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
C Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのC Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。
「メモリリーク」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このC Academyレッスンでコードを書いて実行できますか?
はい。すべてのC Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。