https://youtu.be/Q6EDoHREs9Y
\textcolor{pink}{四国めたん: } 教師役ですわ
\textcolor{lime}{ずんだもん: } 生徒役なのだ
\footnotesize \textcolor{pink}{四国めたん:} 皆さん、こんにちは。四国めたんです
\footnotesize \textcolor{lime}{ずんだもん:} ずんだもんなのだ。こんにちはなのだ
\footnotesize \textcolor{pink}{四国めたん:} 今回もC言語のお勉強をしていきましょう
\footnotesize \textcolor{lime}{ずんだもん:} レッツゴーなのだ
\footnotesize \textcolor{pink}{四国めたん:} これまで3回にわたり、 ポインタ についてお話ししましたわ
\footnotesize \textcolor{lime}{ずんだもん:} 配列 や 構造体 との関係や関数での使い方について学んだのだ
\footnotesize \textcolor{pink}{四国めたん:} 今回は 大きなサイズの配列 と 動的メモリ配置 についてお話ししますわ
\footnotesize \textcolor{lime}{ずんだもん:} おねがいするのだ
\footnotesize \textcolor{pink}{四国めたん:} まず、C言語では 動的メモリ配置 は頻繁に使われますわ
\footnotesize \textcolor{lime}{ずんだもん:} 動的メモリ配置 ?
\footnotesize \textcolor{pink}{四国めたん:} はい、今までは変数や配列が必要な場合には、宣言によって自動的にメモリが確保されていましたわ
\footnotesize \textcolor{lime}{ずんだもん:} 自動的に確保されていたので、あまり意識していなかったのだ
\footnotesize \textcolor{pink}{四国めたん:} いっぽうで、 動的メモリ配置 については、必要に応じてメモリを明示的に確保する方法ですわ
\footnotesize \textcolor{lime}{ずんだもん:} 明示的ということは、関数を呼び出して確保するのか?
\footnotesize \textcolor{pink}{四国めたん:} はい、関連する関数は幾つかありますが、2つだけ覚えておけばOKですわ
\footnotesize \textcolor{lime}{ずんだもん:} お~、意外と簡単なのだ
\footnotesize \textcolor{pink}{四国めたん:} 簡単そうに思える分、ベテランでもバグで苦しむのが 動的メモリ配置 ですわね
\footnotesize \textcolor{lime}{ずんだもん:} 気を付けるのだ
\footnotesize \textcolor{pink}{四国めたん:} なお、 動的メモリ配置 は ポインタ とは切っても切れない関係ですわ
\footnotesize \textcolor{lime}{ずんだもん:} そうなのか?
\footnotesize \textcolor{pink}{四国めたん:} ですので、C言語を学習する上で苦手意識を持っている方も多いかと思いますわ
大きなサイズの配列は問題ですよ?
\footnotesize \textcolor{pink}{四国めたん:} 今までは配列と云えば要素数がせいぜい10個程度のものを扱ってきましたわ
\footnotesize \textcolor{lime}{ずんだもん:} たしかにそのとおりなのだ
\footnotesize \textcolor{pink}{四国めたん:} そして、その程度のサイズであれば、関数内で宣言して使っても問題ありませんでしたわ
\footnotesize \textcolor{lime}{ずんだもん:} 初期化も簡単だったのだ
\footnotesize \textcolor{pink}{四国めたん:} でも、サイズが巨大になると問題が出てきますわ
\footnotesize \textcolor{lime}{ずんだもん:} そうなのか?
\footnotesize \textcolor{pink}{四国めたん:} 例えば皆さんが今見ている画面をキャプチャして、そのデータを配列に収めようとすると、どうなるでしょうか?
\footnotesize \textcolor{lime}{ずんだもん:} 画面のサイズが、一般的には1980×1024だから、そのサイズの配列が必要となるのだ
\footnotesize \textcolor{pink}{四国めたん:} 最近の高画質なスクリーンであれば、3960×2048サイズの配列を使わなければなりませんわね
\footnotesize \textcolor{lime}{ずんだもん:} 結構、大きなサイズの配列になるのだ
\footnotesize \textcolor{pink}{四国めたん:} まして、動画であればその何十倍ものサイズの配列が必要となりますわ
\footnotesize \textcolor{lime}{ずんだもん:} 考えられないのだ
\footnotesize \textcolor{pink}{四国めたん:} では、その際にどのような問題が発生するか確認してみましょう
#include <stdio.h>
#define SIZE (3960 * 2048)
void main() {
int a[SIZE] = {0};
}
\footnotesize \textcolor{pink}{四国めたん:} とりあえず int型 の配列を宣言して、0クリアするだけのプログラムですわ
\footnotesize \textcolor{lime}{ずんだもん:} 配列のサイズは(3960 * 2048)
と、結構、巨大なのだ
\footnotesize \textcolor{pink}{四国めたん:} とりあえず実行してみましょう
\footnotesize \textcolor{lime}{ずんだもん:} 「バンドルされない例外」として「Stack overflow」が発生したのだ
\footnotesize \textcolor{pink}{四国めたん:} Stack overflow例外 は、 スタック と呼ばれるメモリの量が足りなくなった場合に発生しますわ
\footnotesize \textcolor{lime}{ずんだもん:} スタック ?
\footnotesize \textcolor{pink}{四国めたん:} コンピューターが予め用意していた、関数内で宣言された変数等に割り当てることができるメモリ領域のことですわ
\footnotesize \textcolor{lime}{ずんだもん:} なるほどなのだ
\footnotesize \textcolor{pink}{四国めたん:} さらに「関数はスタックの 32440320 バイトを使用します。データの一部をヒープに移動することを考慮してください。」と云う警告も出ています
\footnotesize \textcolor{lime}{ずんだもん:} ヒープ ?
\footnotesize \textcolor{pink}{四国めたん:} ヒープ については、後ほど説明しますわ
\footnotesize \textcolor{lime}{ずんだもん:} まっているのだ
\footnotesize \textcolor{pink}{四国めたん:} 今回は例外が発生してプログラムが止まりましたわ
\footnotesize \textcolor{lime}{ずんだもん:} 例外 ?
\footnotesize \textcolor{pink}{四国めたん:} はい、文字通り 例外的な異常 が発生して、プログラムの実行が止まってしまった際に発生するシグナルですわ
\footnotesize \textcolor{lime}{ずんだもん:} なるほどなのだ
\footnotesize \textcolor{pink}{四国めたん:} 今回は例外が発生しましたが、場合によっては例外が発生せずにコンピューターが暴走する可能性もありますわね
\footnotesize \textcolor{lime}{ずんだもん:} たいへんなのだ
\footnotesize \textcolor{pink}{四国めたん:} ですので、関数内でサイズの大きな配列や構造体等について宣言する場合には注意が必要ですわ
\footnotesize \textcolor{lime}{ずんだもん:} 気を付けるのだ
外部変数は制限がありません
\footnotesize \textcolor{pink}{四国めたん:} 配列を使って スタックオーバーフロー が発生する場合の対処法の1つは 外部変数 を使うことですわ
\footnotesize \textcolor{lime}{ずんだもん:} 外部変数 ?
\footnotesize \textcolor{pink}{四国めたん:} 外部変数 と云うのは関数の外で宣言された変数のことで、 グローバル変数 とも呼ばれますわね
\footnotesize \textcolor{lime}{ずんだもん:} 通常の変数とは違うのか?
\footnotesize \textcolor{pink}{四国めたん:} はい、通常の変数は関数内だけで有効ですが、 外部変数 は、宣言以降の関数で共通して使用できますわ
\footnotesize \textcolor{lime}{ずんだもん:} お~、データを関数の引数として渡したり、戻り値に指定する面倒がなくなるのだ
\footnotesize \textcolor{pink}{四国めたん:} はい、1つの関数内で 外部変数 の値を変更すると、別の関数にも影響を与えることになりますわね
\footnotesize \textcolor{pink}{四国めたん:} 当然、配列を宣言することもできますし、サイズも物理的なメモリの制約はあるものの概ね無制限ですわ
\footnotesize \textcolor{lime}{ずんだもん:} メリットだらけなのだ
\footnotesize \textcolor{pink}{四国めたん:} とりあえず(3960 * 2048)
サイズの配列を宣言して、初期化してみましょう
#include <stdio.h>
#define SIZE (3960 * 2048)
int a[SIZE];
void main() {
for (int i = 0; i < SIZE; i++) {
a[i] = 0;
}
}
\footnotesize \textcolor{lime}{ずんだもん:} 今回は正常に終了したのだ
\footnotesize \textcolor{pink}{四国めたん:} ちなみに、 外部変数 は、一般的なコンピューターでは初期化をしなければ、自動的に0クリアされますわ
\footnotesize \textcolor{pink}{四国めたん:} ただ、極まれに0クリアされない場合もありますので、念のために明示的に初期化することをお勧めしますわね
\footnotesize \textcolor{lime}{ずんだもん:} わかったのだ
\footnotesize \textcolor{lime}{ずんだもん:} ところで波括弧"{}"を使った初期化はできるのか?
\footnotesize \textcolor{pink}{四国めたん:} 可能ですわ
\footnotesize \textcolor{pink}{四国めたん:} 例えばint a[SIZE] = {1, 2, 3};
のようにすることもできますわ
動的にメモリを配置しましょう
\footnotesize \textcolor{lime}{ずんだもん:} ところで 外部変数 にデメリットはないのか?
\footnotesize \textcolor{pink}{四国めたん:} 外部変数 にはサイズに対する制限が緩かったり、関数間のデータ共用が容易にできるなどのメリットがありますわ
\footnotesize \textcolor{pink}{四国めたん:} その反面、どの関数によって何時データが変更されるかが判りづらく、バグになりやすいデメリットがありますわね
\footnotesize \textcolor{lime}{ずんだもん:} 解決する方法はないのか?
\footnotesize \textcolor{pink}{四国めたん:} 関数間のデータ共用が容易ではなくなりますが、 動的メモリ配置 により解決しますわ
\footnotesize \textcolor{lime}{ずんだもん:} 最初に話していたのだ
\footnotesize \textcolor{pink}{四国めたん:} 動的メモリ配置 では変数を スタック ではなく ヒープ と呼ばれる別のメモリ領域に配置しますわ
\footnotesize \textcolor{lime}{ずんだもん:} そういえば「データの一部をヒープに移動することを考慮してください。」と云う警告が出ていたのだ
\footnotesize \textcolor{lime}{ずんだもん:} ところで スタック や ヒープ とはなんなのだ?
\footnotesize \textcolor{pink}{四国めたん:} かんたんに言うと、 スタック は関数内で宣言した変数などに割り当てられるメモリ領域ですわ
\footnotesize \textcolor{pink}{四国めたん:} また、 ヒープ はmalloc
関数をコールすることで、明示的に割り当てるメモリ領域ですわね
\footnotesize \textcolor{lime}{ずんだもん:} つまり スタック は自動的に割り当てられるメモリ領域で、 ヒープ は意図して割り当てるメモリ領域ということか
\footnotesize \textcolor{pink}{四国めたん:} 概ね、その通りですわ
\footnotesize \textcolor{lime}{ずんだもん:} その他に違いはあるのか?
\footnotesize \textcolor{pink}{四国めたん:} 割り当て可能なメモリの合計サイズが違いますわね
\footnotesize \textcolor{lime}{ずんだもん:} スタック よりも ヒープ の方が大きいのか?
\footnotesize \textcolor{pink}{四国めたん:} はい、通常は ヒープ の方が、はるかに大きいですわ
\footnotesize \textcolor{pink}{四国めたん:} 一般的に スタック のサイズが数k~数Mバイトですわ
\footnotesize \textcolor{pink}{四国めたん:} 対して ヒープ のサイズは数Gバイト以上を割り当て可能な場合も少なくありませんわ
\footnotesize \textcolor{lime}{ずんだもん:} すごいのだ
malloc関数でメモリを確保
\footnotesize \textcolor{lime}{ずんだもん:} ところでmalloc
関数とは、なんなのだ?
\footnotesize \textcolor{pink}{四国めたん:} 動的メモリ配置 によるメモリ割り当てを行うための関数ですわ
\footnotesize \textcolor{pink}{四国めたん:} "stdlib.h"ファイルでの宣言はvoid *malloc(size_t size)
となっていますわね
\footnotesize \textcolor{lime}{ずんだもん:} "size"引数には何を入れるのだ?
\footnotesize \textcolor{pink}{四国めたん:} "size"引数には、割り当てるメモリのサイズをバイト単位で指定しますわ
\footnotesize \textcolor{lime}{ずんだもん:} なるほどなのだ
\footnotesize \textcolor{lime}{ずんだもん:} それと、 size_t型 とは、どんな型なのだ?
\footnotesize \textcolor{pink}{四国めたん:} size_t型 は、サイズを表すための型で「正の整数」ですわね
\footnotesize \textcolor{lime}{ずんだもん:} int型 とは違うのか?
\footnotesize \textcolor{pink}{四国めたん:} 負数の指定はできませんが、概ね int型 と同じと考えて問題ありませんわ
\footnotesize \textcolor{lime}{ずんだもん:} わかったのだ
\footnotesize \textcolor{lime}{ずんだもん:} ところで戻り値がvoid
のポインタ型になっているのだが、意味がわからないのだ
\footnotesize \textcolor{pink}{四国めたん:} 戻り値は、 ヒープ に割り当てたメモリのアドレスですわ
\footnotesize \textcolor{pink}{四国めたん:} ただ、割り当てられたメモリ領域の型が定まっていないため、void
のポインタ型となっていますわ
\footnotesize \textcolor{lime}{ずんだもん:} つまりvoid
のポインタ型は、実際の型が定まっていない場合に使われるポインタ型ということか
\footnotesize \textcolor{pink}{四国めたん:} そのとおりですわ
\footnotesize \textcolor{pink}{四国めたん:} ただ、実際に割り当てられたメモリ領域を使う前に、実際の型を指定する必要がありますわね
\footnotesize \textcolor{lime}{ずんだもん:} 具体的に教えてほしいのだ
\footnotesize \textcolor{pink}{四国めたん:} わかりましたわ
#include <stdio.h>
#include <stdlib.h>
#define SIZE (3960 * 2048)
void main() {
int* pa = (int*)malloc(sizeof(int) * SIZE);
if (pa != NULL) {
for (int i = 0; i < SIZE; i++) {
*(pa + i) = 0;
}
}
}
\footnotesize \textcolor{lime}{ずんだもん:} プログラムの説明をおねがいするのだ
\footnotesize \textcolor{pink}{四国めたん:} はい、まずは#include <stdlib.h>
でヘッダーファイル"stdlib.h"を読み込んでいますわ
\footnotesize \textcolor{lime}{ずんだもん:} "stdlib.h"ファイル内でmalloc
関数が宣言されているのだ
\footnotesize \textcolor{pink}{四国めたん:} 次にmalloc
関数で、 ヒープ にメモリ領域の割り当てを行っていますわ
\footnotesize \textcolor{lime}{ずんだもん:} malloc
関数の引数には定数以外も指定できるのか?
\footnotesize \textcolor{pink}{四国めたん:} はい、引数に渡す値は定数でも、変数でも問題ありませんわ
\footnotesize \textcolor{pink}{四国めたん:} これはサイズが固定である 配列 とは異なる 動的メモリ配置 のメリットのひとつですわね
sizeof演算子で型のサイズを取得します
\footnotesize \textcolor{lime}{ずんだもん:} ところで引数にsizeof
とあるが、なんなのだ?
\footnotesize \textcolor{pink}{四国めたん:} sizeof
は括弧内に指定した型や変数のサイズをバイト単位で返す演算子ですわ
\footnotesize \textcolor{lime}{ずんだもん:} つまりsizeof(int)
は int型 が4バイトなので4が返るのか
\footnotesize \textcolor{pink}{四国めたん:} その通りですわ
\footnotesize \textcolor{lime}{ずんだもん:} そしてmalloc
の引数には、SIZE
に int型 のサイズを掛けた値を渡すのか
\footnotesize \textcolor{pink}{四国めたん:} 正解ですわ
\footnotesize \textcolor{lime}{ずんだもん:} ちなみにsizeof
演算子の後に括弧"()"は必要なのか?
\footnotesize \textcolor{pink}{四国めたん:} 必要な場合と、必要ない場合がありますわね
\footnotesize \textcolor{pink}{四国めたん:} とはいえ、括弧"()"を付けておけば問題になることがないので、常に括弧"()"を付けることをお勧めしますわ
\footnotesize \textcolor{lime}{ずんだもん:} わかったのだ
sizeof演算子
sizeofは、後に続く変数や配列、型のサイズをバイト単位で返す演算子です
変数や配列の場合には、sizeofの後にスペースを挟んで変数名や配列名を指定します
例えばsizeof 変数名
やsizeof 配列名
とします
変数名や配列名に括弧"()"を付けて、sizeof(変数名)
やsizeof(配列名)
としもOKです
型の場合には、sizeofの後に括弧"()"を付けて、その内に型名を指定します
例えばsizeof(型名)
とします
ただ、型の場合には、括弧"()"を使わずにsizeof 型名
とすることはできません
include <stdio.h>
void main() {
int a = 0;
int b[] = {0, 1, 2, 3, 4};
int s = sizeof a;
int t = sizeof(int);
int u = sizeof b;
printf("int型の変数のサイズは%dです\n", s);
printf("int型のサイズは%dです\n", t);
printf("int型の配列の要素数は%dです\n", (u / t));
}
上記例では、コンソールに
int型の変数のサイズは4です
int型のサイズは4です
int型の配列の要素数は5です
と表示されます
なお、sizeof 配列名
だけでは、配列の要素数ではなく、バイト数が返ります
配列の要素数を得るためには、sizeof 配列名 / sizeof(配列の型)
もしくはsizeof 配列名 / sizeof 配列[0]
とします
ポインタにsizeof演算子
!
ポインタにsizeof演算子を使う場合には注意が必要です
以前にポインタと配列は同じように扱えるとお話ししました
ただ、ポインタに配列を代入したからと言って、ポインタにsizeof演算子を使っても、配列のサイズは得られません
ポインタはアドレスを入れるための変数ですので、sizeof演算子によって得られる値は、アドレスのサイズになります
以下のプログラムを実行すれば、違いは明確です
#include <stdio.h>
void main() {
int a[] = {0, 1, 2, 3, 4};
int* pa = a;
int s = sizeof a;
int t = sizeof pa;
printf("配列のサイズは%dです\n", s);
printf("ポインタのサイズは%dです\n", t);
}
結果は以下のようになります
ベテランのプログラマでも混同してしまうことが多いので、注意が必要です
キャスト演算子で型を変えます
\footnotesize \textcolor{lime}{ずんだもん:} ところでmalloc
の前に付加した(int*)
は、なんなのだ?
\footnotesize \textcolor{pink}{四国めたん:} malloc
の前に付加した(int*)
が キャスト演算子 ですわ
\footnotesize \textcolor{lime}{ずんだもん:} キャスト演算子 ?
\footnotesize \textcolor{pink}{四国めたん:} はい、変数や関数の前に(型)
という形で付けて、型を括弧内の型に強制的に変更する演算子ですわ
\footnotesize \textcolor{pink}{四国めたん:} 今回の場合は void*型 の戻り値を強制的に int*型 に変更していますわね
\footnotesize \textcolor{lime}{ずんだもん:} なんか、すごくべんりそうなのだ
\footnotesize \textcolor{pink}{四国めたん:} たしかに便利ですわね
\footnotesize \textcolor{pink}{四国めたん:} ただ キャスト演算子 は強力すぎてバグの温床になりますので、多用は厳禁ですわ
\footnotesize \textcolor{lime}{ずんだもん:} 今回は使ってもいいのか?
\footnotesize \textcolor{pink}{四国めたん:} はい、malloc
関数の戻り値に使うのは、例外的に必須となりますわね
\footnotesize \textcolor{lime}{ずんだもん:} なるほどなのだ
NULLの戻り値
\footnotesize \textcolor{pink}{四国めたん:} ところで、malloc
関数で 動的メモリ配置 が失敗した場合にはNULL
が返りますわ
\footnotesize \textcolor{lime}{ずんだもん:} 失敗することなんかあるのか?
\footnotesize \textcolor{pink}{四国めたん:} はい、物理的なメモリが足りなかったりすると失敗しますわね
\footnotesize \textcolor{lime}{ずんだもん:} だからif文でNULL
かどうかの確認をしているのか
\footnotesize \textcolor{pink}{四国めたん:} はい、プログラム例の場合はfor
ループで初期化を行う前にif
文で戻り値がNULL
ではないことを確認していますわ
free関数でメモリを解放
\footnotesize \textcolor{pink}{四国めたん:} 実はサンプルプログラムには、いちばんやってはいけない類のバグがありますわ
\footnotesize \textcolor{lime}{ずんだもん:} え~、そうなのか?
\footnotesize \textcolor{pink}{四国めたん:} はい、本来、動的メモリ配置によりメモリを確保したら、最後にメモリの解放を行わなければなりませんわ
\footnotesize \textcolor{lime}{ずんだもん:} メモリの解放?
\footnotesize \textcolor{pink}{四国めたん:} はい、使い終わったメモリの領域をそのままにしておくと、その内に使えるメモリがなくなってしまいますわ
\footnotesize \textcolor{lime}{ずんだもん:} なるほどなのだ
\footnotesize \textcolor{lime}{ずんだもん:} ところで、どのようにしてメモリの解放を行うのだ?
\footnotesize \textcolor{pink}{四国めたん:} free
関数を使いますわ
\footnotesize \textcolor{lime}{ずんだもん:} free
関数?
\footnotesize \textcolor{pink}{四国めたん:} はい、void free(void* block)
という形式の関数になりますわね
\footnotesize \textcolor{lime}{ずんだもん:} 使い方が、いまひとつわからないのだ
\footnotesize \textcolor{pink}{四国めたん:} とりあえずサンプルプログラムを修正してみましょう
#include <stdio.h>
#include <stdlib.h>
#define SIZE (3960 * 2048)
void main() {
int* pa = (int*)malloc(sizeof(int) * SIZE);
if (pa != NULL) {
for (int i = 0; i < SIZE; i++) {
*(pa + i) = 0;
}
}
free(pa);
pa = NULL;
}
\footnotesize \textcolor{lime}{ずんだもん:} プログラムの説明をおねがいするのだ
\footnotesize \textcolor{pink}{四国めたん:} わかりましたわ
\footnotesize \textcolor{pink}{四国めたん:} まず、free
関数の引数の型は void*型 ですわ
\footnotesize \textcolor{lime}{ずんだもん:} void
のポインタ型ということは、実際の型が定まっていないということか?
\footnotesize \textcolor{pink}{四国めたん:} 型が定まっていないというよりも、ポインタであればどのような型でも受け付けるという意味ですわね
\footnotesize \textcolor{lime}{ずんだもん:} なるほどなのだ
\footnotesize \textcolor{pink}{四国めたん:} そして、実際の引数としては、malloc
関数で得られた値をそのままセットするようにしますわ
\footnotesize \textcolor{lime}{ずんだもん:} malloc
関数で得られた値以外はダメなのか?
\footnotesize \textcolor{pink}{四国めたん:} はい、free
関数の引数に渡せる値は限定されていますわ
\footnotesize \textcolor{pink}{四国めたん:} 期待された値以外を引数に渡すと、最悪、暴走することがありますので、注意が必要ですわ
\footnotesize \textcolor{lime}{ずんだもん:} 気を付けるのだ
\footnotesize \textcolor{pink}{四国めたん:} 最後に、free
関数でメモリ領域を解放した後のポインタは必ずNULL
にしておきますわ
\footnotesize \textcolor{lime}{ずんだもん:} 無駄ではないのか?
\footnotesize \textcolor{pink}{四国めたん:} 解放した後のメモリ領域に間違えてアクセスしないようにするための処理ですわ
\footnotesize \textcolor{pink}{四国めたん:} 非常に重要なことなので忘れないようにしましょう
\footnotesize \textcolor{lime}{ずんだもん:} りょうかいなのだ
NULLの引数について
\footnotesize \textcolor{lime}{ずんだもん:} ところで少し疑問があるのだが…
\footnotesize \textcolor{pink}{四国めたん:} なんでしょう
\footnotesize \textcolor{lime}{ずんだもん:} malloc
関数が失敗してpa
にNULL
がセットされている場合にfree
関数の引数にNULL
が渡されるのではないか?
\footnotesize \textcolor{pink}{四国めたん:} たしかに、その通りですわね
\footnotesize \textcolor{pink}{四国めたん:} これは実は問題なくて、free
関数の引数にNULL
を指定した場合には、何もしないことになっていますわ
\footnotesize \textcolor{lime}{ずんだもん:} つまりNULL
はfree
関数の引数にセットできる値の1つということか
\footnotesize \textcolor{pink}{四国めたん:} その通りですわ
動的メモリ配置は解放まで有効
\footnotesize \textcolor{pink}{四国めたん:} ところで以前、関数内で宣言された配列や変数等は、関数を抜けると無効になるとお話ししましたわ
\footnotesize \textcolor{lime}{ずんだもん:} おぼえているのだ
\footnotesize \textcolor{pink}{四国めたん:} 関数内で宣言された配列や変数へのポインタを戻り値にすると、無効なメモリの領域を指すようになる原因でしたわね
\footnotesize \textcolor{lime}{ずんだもん:} そうだったのだ
\footnotesize \textcolor{pink}{四国めたん:} では関数内でmalloc
で割り当てられたメモリ領域は、関数を抜けた後はどうなるのでしょうか?
\footnotesize \textcolor{lime}{ずんだもん:} う~ん、free
で解放するまでは有効なのではないか?
\footnotesize \textcolor{pink}{四国めたん:} 鋭いですわね
\footnotesize \textcolor{pink}{四国めたん:} ですので、malloc
で割り当てられたメモリ領域のポインタを戻り値として返してもOKなのですわ
\footnotesize \textcolor{lime}{ずんだもん:} 具体的に教えてほしいのだ
\footnotesize \textcolor{pink}{四国めたん:} わかりましたわ
#include <stdio.h>
#include <stdlib.h>
#define SIZE (3960 * 2048)
int* get_array() {
int* p = (int*)malloc(sizeof(int) * SIZE);
return p;
}
void main() {
int* pa = get_array();
if (pa != NULL) {
for (int i = 0; i < SIZE; i++) {
*(pa + i) = 0;
}
}
free(pa);
pa = NULL;
}
\footnotesize \textcolor{lime}{ずんだもん:} 普通に実行できたのだ
\footnotesize \textcolor{pink}{四国めたん:} はい、get_array
関数内でmalloc
に割り当てたメモリ領域は、メイン関数でもfree
が呼ばれるまでは有効ですわ
\footnotesize \textcolor{pink}{四国めたん:} なお、他の関数内で割り当てたメモリ領域は、free
での解放を忘れることが多いので充分な注意が必要ですわ
\footnotesize \textcolor{lime}{ずんだもん:} わかったのだ
動的メモリ配置用の関数は他にもあります
\footnotesize \textcolor{lime}{ずんだもん:} ところで 動的メモリ配置 を行える関数はmalloc
だけなのか?
\footnotesize \textcolor{pink}{四国めたん:} いいえ、 動的メモリ配置 を行える関数としては、他にもcalloc
やrealloc
等がありますわ
\footnotesize \textcolor{lime}{ずんだもん:} 詳しく教えてほしいのだ
\footnotesize \textcolor{pink}{四国めたん:} malloc
以外は、余り使い勝手が良い関数ではありませんわ
\footnotesize \textcolor{pink}{四国めたん:} ですので今回は、詳細の解説を省きますわ
\footnotesize \textcolor{lime}{ずんだもん:} え~
\footnotesize \textcolor{pink}{四国めたん:} 大抵の場合はmalloc
で済んでしまいますので、問題ありませんわ
calloc / realloc
動的メモリ配置 を行う関数はmalloc
の他にcalloc
やrealloc
があります
どちらもmalloc
で簡単に代用できるので、できるだけmalloc
を使うようにして下さい
calloc
形式はvoid *calloc(size_t count, size_t size);
となります
指定されたサイズのメモリ領域をヒープ領域に確保し、0でクリアします
malloc
との最大の違いは、取得したメモリ領域を0でクリアすることです
また、引数では、"size"に確保するメモリの型のサイズを、"count"には要素数を指定します
例えば、配列int a[100];
と同じメモリ領域を取得する場合には、"size"にはsizeof(int)
を、"cont"には100
を指定します。
malloc
と比べて、メモリサイズをバイト単位に変換する必要がないことと、0による初期化が必要ないメリットがあります
ただ、メモリサイズのバイト単位への変換は掛け算ひとつで済んでしまいますし、0以外の初期化を必要とする場合には2度手間になってしまいます
malloc
を使って同じことができます
#include <stdlib.h>
#include <string.h>
void* another_calloc(size_t count, size_t size) {
size_t num = count * size;
void* p = malloc(num);
if (p != NULL) {
memset(p, 0, num);
}
return p;
}
関数名はcalloc
にすると被ってしまいますのでanother_calloc
としています
memset
は指定したメモリ領域を指定の値で埋める関数で、"string.h"内で宣言されています
形式はvoid *memset(void* ptr, int value, size_t num);
となります
"ptr"はメモリ領域の先頭アドレス、"value"はセットする値、"num"はメモリサイズでバイト単位です
memset
は次回に詳細を説明します
realloc
形式はvoid* realloc(void* ptr, size_t size);
となります
指定したメモリ領域のサイズを指定のサイズに変更します
"size"で指定したサイズのメモリ領域を確保し、"ptr"で指定したメモリ領域の値をコピーします
"ptr"で指定したメモリ領域は解放され、新たに確保されたメモリ領域が返ります
なお、"size"で指定した値が元のメモリ領域のサイズよりも小さい場合には、コピーできない部分の値は破棄されます
また、"size"で指定した値が元のメモリ領域のサイズよりも大きい場合には、値がコピーされた部分以外は不定の値がセットされます
色々と細かい部分は省いて、基本的にはmalloc
関数を使って同じことができます
#include <stdlib.h>
#include <string.h>
void* another_realloc(void* ptr, size_t size) {
void* p = malloc(size);
if (p != NULL) {
memmove(p, ptr, size);
free(ptr);
}
return p;
}
関数名はrealloc
にすると被ってしまいますのでanother_realloc
としています
memmove
は指定したメモリ領域の値を指定のメモリ領域に移動する関数で、"string.h"内で宣言されています
形式はvoid* memmove(void* destination, const void* source, size_t num);
となります
"destination"は移動先のメモリ領域の先頭アドレス、"source"は移動元のメモリ領域の先頭アドレス、"num"は移動するサイズでバイト単位です
memmove
は次回に詳細を説明します
realloc
は動作が処理系により違っていたり、使い方が独特でバグの温床になり易いので、多用は避けるべきです
まとめ
\footnotesize \textcolor{pink}{四国めたん:} お疲れさまでした
\footnotesize \textcolor{lime}{ずんだもん:} おつかれさまなのだ
\footnotesize \textcolor{pink}{四国めたん:} 以上で 大きなサイズの配列と動的メモリ配置 についての説明は終わりですわ
Discussion