
はじめてのC言語 | 第19回
はじめに
前回は、複数ファイル構成とヘッダファイルを学びました。
今回は、C言語の「動的メモリ確保」を学びます。
これまでの配列は、プログラムを書く時点で要素数を決めていました。
動的メモリ確保を使うと、プログラムの実行中に必要なサイズのメモリを確保できます。
この回の目的は次の5点です。
- 動的メモリ確保が何のために必要かを理解する
mallocの基本的な使い方を学ぶfreeでメモリを解放する理由を理解するNULLチェックの重要性を確認する- メモリリークの基本を理解する
動的メモリ確保とは何か
動的メモリ確保とは、プログラムの実行中に必要な量のメモリを確保する仕組みです。
たとえば、ユーザーが入力した個数に合わせて配列を作りたい場合に使います。
これまでの配列は、次のように要素数を固定して書いていました。
int scores[5];この場合、要素数は5個で固定です。
一方、動的メモリ確保を使うと、実行時に決まった個数に応じて領域を確保できます。
malloc と free
動的メモリ確保では、主に次の2つの関数を使います。
malloc: メモリを確保するfree: 確保したメモリを解放する
これらを使うには、#include <stdlib.h> が必要です。
#include <stdlib.h>確保したメモリは、使い終わったら free で解放します。
整数1個分のメモリを確保する
ソースコード
malloc_basic.c という名前で保存します。
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int *p;
p = malloc(sizeof(int));
if (p == NULL) {
printf("memory allocation failed\n");
return 1;
}
*p = 100;
printf("%d\n", *p);
free(p);
return 0;
}実行手順
1. 作業ディレクトリに移動する
cd ~/Desktop2. コンパイルする
clang malloc_basic.c -o malloc_basic3. 実行する
./malloc_basic実行結果:
100コードの読み方
int *p
int *p;p は int 型の値を指すポインタです。
malloc で確保したメモリの先頭アドレスを、このポインタに保存します。
malloc(sizeof(int))
p = malloc(sizeof(int));malloc は、指定したバイト数のメモリを確保します。
sizeof(int) は、int 1個分に必要なバイト数を表します。
malloc の戻り値は、確保したメモリの先頭アドレスです。
p == NULL
if (p == NULL) {
printf("memory allocation failed\n");
return 1;
}メモリ確保に失敗すると、malloc は NULL を返します。
そのため、malloc の直後に NULL かどうかを確認します。
free(p)
free(p);free は、malloc で確保したメモリを解放する関数です。
確保したメモリを使い終わったら、必ず free します。
配列のように使う
malloc で複数個分の領域を確保すると、配列のように扱えます。
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int *scores;
int i;
int size = 5;
scores = malloc(sizeof(int) * size);
if (scores == NULL) {
printf("memory allocation failed\n");
return 1;
}
for (i = 0; i < size; i++) {
scores[i] = (i + 1) * 10;
}
for (i = 0; i < size; i++) {
printf("%d\n", scores[i]);
}
free(scores);
return 0;
}実行結果:
10
20
30
40
50sizeof(int) * size
scores = malloc(sizeof(int) * size);int を size 個分保存できるだけのメモリを確保しています。
この例では、int 5個分の領域を確保しています。
入力された個数だけ確保する
動的メモリ確保は、実行時に必要な要素数が決まる場合に使えます。
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int *numbers;
int size;
int i;
printf("size: ");
if (scanf("%d", &size) != 1) {
printf("input error\n");
return 1;
}
if (size <= 0) {
printf("size must be positive\n");
return 1;
}
numbers = malloc(sizeof(int) * size);
if (numbers == NULL) {
printf("memory allocation failed\n");
return 1;
}
for (i = 0; i < size; i++) {
numbers[i] = i + 1;
}
for (i = 0; i < size; i++) {
printf("%d\n", numbers[i]);
}
free(numbers);
return 0;
}実行例:
size: 3
1
2
3このように、実行中に決まった size に合わせてメモリを確保できます。
free したあとのポインタ
free(p); を実行すると、p が指していたメモリは使い終わったものとして解放されます。
その後に *p や p[0] を使ってはいけません。
危険な例:
free(p);
printf("%d\n", *p);これは未定義動作です。
free したあとも、ポインタ変数 p の値自体は残ることがありますが、その先のメモリを使ってよいわけではありません。
メモリリークとは何か
malloc で確保したメモリを free しないまま使い終わると、そのメモリを解放できません。
このような状態をメモリリークと呼びます。
悪い例:
int *p = malloc(sizeof(int));
if (p == NULL) {
return 1;
}
*p = 10;
return 0;この例では、free(p); を呼ばずに終了しています。
小さなプログラムでは目立たないこともありますが、長く動くプログラムでは問題になります。
正しい例:
int *p = malloc(sizeof(int));
if (p == NULL) {
return 1;
}
*p = 10;
free(p);
return 0;初心者がつまずきやすい点
#include <stdlib.h> を忘れる
malloc と free を使うには、#include <stdlib.h> が必要です。
sizeof を使わずにバイト数を直接書く
次のように直接バイト数を書くのは避けます。
malloc(20);int のサイズは環境によって異なる可能性があります。
次のように sizeof を使います。
malloc(sizeof(int) * size);NULL チェックをしない
malloc は必ず成功するとは限りません。
戻り値が NULL かどうかを確認してから使います。
free を忘れる
malloc したメモリは、使い終わったら free します。
確保と解放をセットで考えることが重要です。
free したあとに使う
free したメモリを再び使ってはいけません。
必要な場合は、もう一度 malloc で確保します。
よくあるエラー
implicit declaration of function ‘malloc’
原因: #include <stdlib.h> を書いていません。
対処: ファイルの先頭に #include <stdlib.h> を追加します。
実行結果がおかしい、または異常終了する
原因: 確保した範囲外にアクセスしている可能性があります。
対処: 0 から size - 1 の範囲だけを使います。
メモリ確保に失敗する
原因: 非常に大きなサイズを指定している可能性があります。
対処: malloc の戻り値を確認し、NULL の場合は処理を中止します。
free で異常終了する
原因: malloc で確保していないポインタを free している、または同じポインタを2回 free している可能性があります。
対処: free するのは、malloc で確保したメモリに対して1回だけにします。
練習用コード
入力された個数の合計を求める
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int *numbers;
int size;
int i;
int sum = 0;
printf("size: ");
if (scanf("%d", &size) != 1 || size <= 0) {
printf("input error\n");
return 1;
}
numbers = malloc(sizeof(int) * size);
if (numbers == NULL) {
printf("memory allocation failed\n");
return 1;
}
for (i = 0; i < size; i++) {
numbers[i] = i + 1;
sum += numbers[i];
}
printf("%d\n", sum);
free(numbers);
return 0;
}実行例:
size: 4
10関数の中で動的配列を処理する
#include <stdio.h>
#include <stdlib.h>
int sum_array(int arr[], int size) {
int i;
int sum = 0;
for (i = 0; i < size; i++) {
sum += arr[i];
}
return sum;
}
int main(void) {
int *values;
int size = 5;
int i;
values = malloc(sizeof(int) * size);
if (values == NULL) {
printf("memory allocation failed\n");
return 1;
}
for (i = 0; i < size; i++) {
values[i] = (i + 1) * 2;
}
printf("%d\n", sum_array(values, size));
free(values);
return 0;
}実行結果:
30まとめ
今回のポイントは次のとおりです。
- 動的メモリ確保は、実行中に必要なサイズのメモリを確保する仕組みである
mallocは指定したバイト数のメモリを確保するmallocを使うには#include <stdlib.h>が必要であるmallocの戻り値がNULLでないか確認する- 確保したメモリは配列のように使える
mallocで確保したメモリはfreeで解放するfreeしたあとのメモリを使ってはいけないfreeし忘れるとメモリリークになる
この回では、動的メモリ確保の基本を学びました。
malloc を使うと、実行中に必要なサイズのメモリを確保できます。
一方で、確保したメモリは必ず free で解放する必要があります。
動的メモリ確保では、確保、確認、使用、解放の流れを守ることが重要です。
次回予告
次はいよいよ最終回、総合演習として、入力、配列、関数、構造体、ファイル入出力を組み合わせた小さなプログラムを作ります。


