はじめに
- これまでの学習に続いて、今回はヒープの利用について学んだ結果をまとめてみました。
- 【C】初めてのC言語(1. Hello world)
- 【C】初めてのC言語(2. 四則演算とフォーマット指定子)
- 【C】初めてのC言語(3. ポインタ)
- 【C】初めてのC言語(4. 構造体)
- 【C】初めてのC言語(5. 関数)
- 【C】初めてのC言語(6. 配列)
- 【C】初めてのC言語(7. for文)
- 【C】初めてのC言語(8. 2次元配列)
- 【C】初めてのC言語(9. 関数のプロトタイプ宣言)
- 【C】初めてのC言語(10. 構造体配列)
- 【C】初めてのC言語(11. ヘッダファイル)
- 【C】初めてのC言語(12. 3つのからくり構文+ポインタ演算)
- 【C】初めてのC言語(13. メモリを扱う標準関数)
※今回の内容は、スッキリわかるC言語入門 第2版のp.384~p.389となります。
学習環境
- 今回はpaiza.ioのC言語のエディタを使いました。
配列の限界
- 以下のコードでは
test()
で作った配列array
を戻り値としてmain()
に渡そうとしています。 - ところが関数
test()
の終了時点で、配列array
として確保されていたメモリが開放されてしまいます。- 配列
array
は、ここではメモリのスタック領域に作られます。
- 配列
- 結果的に、
main()
内でこの配列を操作しようとするとコンパイルエラーが発生してしまいます。
Main.c
#include <stdio.h>
int* test() {
int array[3];
return array; // 配列arrayの先頭アドレスが返される。
} // 関数終了に伴い、メモリ上で配列arrayが確保した領域が解除される。
int main(void) {
int* a = test();
a[0] = 5; // 確保されていないメモリにアクセスしてしまう。
return 0;
}
コンパイルエラーのメッセージ
Main.c:1:9: warning: treating Unicode character as whitespace [-Wunicode-whitespace]
#include <stdio.h>
^~
Main.c:5:9: warning: address of stack memory associated with local variable 'array' returned [-Wreturn-stack-address]
return array; // 配列arrayの先頭アドレスが返される。
^~~~~
2 warnings generated.
ヒープの利用
- 上記のように、スタック領域に確保したメモリは関数の終了とともに解放されてしまいますが、
malloc()
やcalloc()
でヒープ領域を確保すれば、関数が終了した後もメモリが解放されることはありません。 - そのため、ヒープ領域を利用した時は必ず確保した領域を、
free()
で明示的に解放する必要があります。- メモリを正しく解放しないと、メモリを食いつぶしていくメモリーリークの原因となります。
malloc関数
- ヒープ領域に連続したメモリを確保する場合に使われる関数です。
- 引数として確保したいサイズ(バイト数)を入れるだけで、ヒープ領域にメモリを確保できます。
- 確保したメモリ領域は、利用しなくなった所で
free()
を呼び出して明示的に解放します。
Main.c
#include <stdio.h>
#include <stdlib.h>
int* test(void) {
// ヒープに12バイト(※int型配列の3要素分)を確保する。
int* array = (int*)malloc(sizeof(int) * 3);
array[0] = 2;
array[1] = 4;
array[2] = 6;
return array; // arrayの先頭アドレスが返される。
} // 関数が終了しても、ヒープに確保したメモリは解放されない。
int main(void) {
int* a = test();
if (a==NULL) {
printf("ヒープ確保に失敗しました。");
} else {
// ヒープに確保されているメモリにアクセスする。
for (int i=0; i<3; i++) {
printf("%d番目の要素は%dです。\n", (i+1), a[i]);
}
free(a); // ヒープ領域を解放する。
}
return 0;
}
実行結果
1番目の要素は2です。
2番目の要素は4です。
3番目の要素は6です。
calloc関数
- ヒープ領域に連続したメモリを確保する場合に使われる関数ですが、mallocと違って「メモリを確保した直後に内容を0で埋める」という特徴があります。
- また
malloc()
とは違い、第1引数が「要素数」、第2引数が「各要素の長さ(バイト数)」となります。 - 確保したメモリ領域は、利用しなくなった所で
free()
を呼び出して明示的に解放します。
Main.c
#include <stdio.h>
#include <stdlib.h>
int* test(void) {
// ヒープにint型の3要素分(※12バイト)をのメモリを確保する。
int* array = (int*)calloc(3, sizeof(int));
array[1] = 4;
return array; // arrayの先頭アドレスが返される。
} // 関数が終了しても、ヒープに確保したメモリは解放されない。
int main(void) {
int* a = test();
if (a==NULL) {
printf("ヒープ確保に失敗しました。");
} else {
// ヒープに確保されているメモリにアクセスする。
for (int i=0; i<3; i++) {
printf("%d番目の要素は%dです。\n", (i+1), a[i]);
}
free(a); // ヒープ領域を解放する。
}
return 0;
}
実行結果
1番目の要素は0です。
2番目の要素は4です。
3番目の要素は0です。
参考URL
-
スッキリわかるC言語入門 第2版
- p.384~p.389
- C言語 mallocとcallocの違い