見出し画像

続C言語教室 - 第14回 動的メモリの使い方(その2)

前回、ヒープから動的にメモリを割り当てるライブラリ関数である malloc と free について説明しましたが、似ているけど少し違う仲間たちがいます。

最初は calloc という関数で、配列として使う領域を確保します。

void *calloc(
  size_t number,
  size_t size
);

callocのプロトタイプ
#include <stdio.h>
#include <stdlib.h>

int main() {
  size_t sz = 5;
  int *a = (int*)calloc(sz, sizeof(int));
  if (a != NULL) {
    for (size_t i = 0; i < sz; i++)
    printf("%d\t", a[i]);
  }
  printf("\n");
  free(a);
  return 0;
}

0 0 0 0 0

実行結果(数字の区切りはタブ)

配列に値を代入をしていませんが、ちゃんと0という値が出力されています。残念ながら初期値は常に0であることが決まっているので、スキな値で初期化することはできません。

もちろんmallocで確保した領域であっても、直後にそれぞれの要素に値を代入して初期化することも可能ですが、個別に代入するよりも memset などのライブラリ関数を使って一括してクリアしたほうがパフォーマンスが上がります。

C言語では、初期化子を書けば要素の数を省略できるとはいえ、基本的には要素数には定数しか書けません。実行時に配列のサイズを決めたいときは要素の数が変数になるので、この calloc を使ってヒープから動的にメモリを割り当てて配列として使うことができるようになるわけです。

ところがC99からは変数で要素の数を指定することができるようになりました。

#include <stdio.h>

int main() {
  size_t i = 5;
  char a[i + 1];
  for (i = 0; i < 5; i++) a[i] = '*';
  a[5] = '\0';
  printf("%s\n", a);
  return 0;
}

*****

実行結果

このコードはVSではエラーになりますが、gccでは通ります。もっとも、これが可能なのはローカル変数のみで、グローバル変数や静的変数に対しては相変わらず定数のみです(ちゃんとエラーになる)。

VSではローカル変数に対しては alloca を使うことで、動的な要素数の配列を確保できます(allocaはマクロで定義されており中身は_allocaでインライン関数です)。

void *alloca(size_t size);

allocaのプロトタイプ
#include <stdio.h>
#include <malloc.h>

int main() {
  size_t sz = 5;
  char *a = (char*)alloca(sz + 1);
  if (a != NULL) {
    for (size_t i = 0; i < sz; i++) a[i] = '*';
  }
  a[sz] = '\0';
  printf("%s\n", a);
  return 0;
}

*****

実行結果

allocaはスタックしか割り当てられないので、やはりグローバル変数や静的変数には使えません。allocaを使う時に注意する必要があるのは、その領域がスタックから割り当てられるので、あまり大きな領域を確保しようとしたり、再帰的に使われる場合にはスタックが溢れる可能性が高いことです。もちろん関数を抜ければ free する必要もなく(freeを呼び出すと実行時エラーになります)自動的に領域は解放されるのでメモリリークの可能性は減るのですが、この領域をグローバル変数に設定してしまうと、知らぬ間に解放されて無効な領域になるのでやってはいけません。またfreeを呼び出せないので関数を抜けるまで解放することが出来ないので、関数の作り方に配慮が必要です。

なおローカル変数のスコープは{}内だけですが、殆どの実装では関数を抜けるまではスタックフレームを更新しないので、有効範囲を抜けてもallocaで確保した領域は解放されないようです。

またallocaの良いところはメモリの割り当て(と解放)にかかるコストが低いことです。malloc(やcalloc)ではヒープから必要な大きさの空いている領域を探すのに一定のコストがかかります(そして解放するときにも空き領域の情報を更新するコストがかかる)。

C : alloca

さて、メモリを確保するやり方は済んだのですが、一度確保した領域のサイズを変えるにはどうするあたりからを次回にしましょう。

ヘッダ画像は、AIに描いてもらいました。

#プログラミング #C言語 #プログラミング講座 #C言語教室 #動的メモリ #ヒープ #スタック #calloc #alloca #free #変数の初期化


いいなと思ったら応援しよう!

kzn 頂いたチップは記事を書くための資料を揃えるために使わせていただきます!