【注意】下記はC言語での話であって、C++言語の話ではありません。混同しないように!
配列の長さを実行時に指定したい!
配列はプログラミングにおいて、重要な要素の一つである。しかし、C言語の配列は、コンパイル時に長さを固定化する必要があるため、実行時に長さを指定することができない。実行時に長さを指定して配列を作成するには、
malloc
等でメモリを動的に確保する必要がある。
と、思っていた時期が私にもありました。最新のC言語ではなんとかallocなんてよくわからないものを使う必要はありません。
可変長配列 (variable length array, VLA)
下のは、入力した値までの素数を数え上げるプログラムです。
#include <stdio.h>
int main(void)
{
int n;
printf(u8"自然数をいれてね: ");
fflush(stdout);
if (scanf("%d", &n) != 1) {
printf(u8"自然数をいれてねって言ったのに!\n");
return 1;
}
if (n <= 0) {
printf(u8"自然数の意味わかってる?\n");
return 2;
} else if (n > 10000) {
printf(u8"10000より大きい数字はちょっと・・・\n");
return 2;
}
printf(u8"%d までの素数を数えてあげるよ。\n", n);
n++;
int prime[n]; // VLAを使用!
for (int i = 0; i < n; i++) prime[i] = i;
prime[1] = 0;
for (int i = 2; i < n; i++)
if (prime[i] != 0) {
printf(u8"%d ", i);
for (int j = i; i * j < n; j++)
prime[i * j] = 0;
}
printf(u8"\n");
return 0;
}
u8""
ってなんだ?見たこと無いぞとか、Cのローカル変数宣言は関数の先頭でなければならないだよとか、//
はC++の行コメントであってCでは使えないとか、そういうことを指摘したがる時代遅れな人はおいとくとして、注目すべきはint prime[n];
です。n
はもちろん、マクロ定数じゃないです。ただのローカル変数です。しかも、n
の値は標準入力で入力された数値が入ります。これがC99から追加された(けど、C11でオプション機能に格下げされた)可変長配列(VLA)の機能になります。
もちろん、二次元以上の多次元配列も可能です。また、関数の仮引数でも使えます。ただし、何でも使えるというわけではありません。使えるのは関数内部のローカル変数と関数の仮引数のみで、それまでに出てきた宣言済みの変数を長さに使う必要があります(関数の仮引数の場合は、配列の仮引数よりも手前にある仮引数を使うなど)。また、静的(static)ローカル変数には使えません。
利点
- ローカル変数なので、関数(ブロック)が終わると自動的に破棄されます。
free
忘れのメモリリークの心配はいりません。 - メモリはスタック領域から確保されるので、メモリ確保に失敗するとプログラムが落ちます。面倒な
NULL
で比較しての処理をする必要がなくなります。 - 多次元配列も固定長の時と同じように扱えるので、処理がとてもすっきり書けます。
sizeof
とかもちゃんととれます。 - スタックからのメモリ確保なので、
malloc
とかより高速です。たぶん。
欠点
- ローカル変数なので戻り値としてそのまま使うことはできません。もう役に立たない(というか操作したら超危険な)ポインタが渡されちゃいます。
- メモリ確保に失敗するとプログラムごと落ちちゃうので、残りメモリが少なくてもなんとか生き延びようなんて処理はできません。
- C11ではオプション機能であるため、C11対応でもサポートしないコンパイラがあります。GCCとClangでは使えますが、GCCもClangも無いような特殊な環境は対応コンパイラが存在しない可能性があります。組み込み系とかよく知らないけど。
- Visual Studioという特定OSでしか動作せず、C++11すらまともに対応していないコンパイラでは使えません。そんなにそのOSが好きなら、C#でも使えばいいのです。
- __C++14の仕様には含まれていません。__C++14の草案にはRuntime-sized arrays N3639という劣化版の機能が含まれていましたが、正式版では削除されました。現在は Array Extensions N3820 として再提案されています。採用されるかどうかはわかりません。。
- 長さは後から変更できません。長さを動的に変えたいなら、
malloc
したのをrealloc
とかするしかないです。というか、std::vector
とか使った方がはy
結論
それよりも、C++でstd::vector
とか使えばいいんじゃね?