第19回|C言語で動的メモリ確保の基本を学ぶ:必要な分だけメモリを確保する

当サイトでは、コンテンツの一部に広告を掲載しています。
第19回|C言語で動的メモリ確保の基本を学ぶ:必要な分だけメモリを確保する

はじめての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 ~/Desktop

2. コンパイルする

clang malloc_basic.c -o malloc_basic

3. 実行する

./malloc_basic

実行結果:

100

コードの読み方

int *p

int *p;

pint 型の値を指すポインタです。
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;
}

メモリ確保に失敗すると、mallocNULL を返します。
そのため、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
50

sizeof(int) * size

scores = malloc(sizeof(int) * size);

intsize 個分保存できるだけのメモリを確保しています。
この例では、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 が指していたメモリは使い終わったものとして解放されます。
その後に *pp[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> を忘れる

mallocfree を使うには、#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 で解放する必要があります。
動的メモリ確保では、確保、確認、使用、解放の流れを守ることが重要です。

次回予告

次はいよいよ最終回、総合演習として、入力、配列、関数、構造体、ファイル入出力を組み合わせた小さなプログラムを作ります。

あわせて読みたい
第20回|総合演習:C言語で入力、配列、関数、構造体、ファイル入出力を組み合わせる はじめてのC言語 | 第20回 はじめに 前回は、動的メモリ確保の基本を学びました。 今回は、ここまで学んだ内容を組み合わせて、小さなプログラムを作ります。 この回...

さらに学びたいあなたへ

用途ごとに選ぶ Linux のおすすめ本

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

のいのアバター のい UNIX Cafe マスター

Macintosh Color Classicから始まった旅は、長いWindows時代を経て、Windows10のサポート終了をきっかけにUNIXの世界へ戻ってきました。UNIX Cafeでは、UNIX・Linux・そしてMacな世界を、むずかしい言葉を使わず、物語のように書いています。プログラミングは、アイデアをコンピューターに伝えるための言葉です。簡単な単語と文法を覚えれば、誰でもコマンドを使えます。ぜひ一度、やさしいプログラミングの世界をのぞいてみてください。

Created by UNIX Cafe

目次