D言語の動的配列

D言語の動的配列は、C言語などの配列とはかなり違うので、そのへんをつらつらと語っていきます。

D言語には、静的配列と動的配列という2種類の配列があります。


int[3] a1; // 要素数3の静的配列
int[] a2; // 動的配列

静的配列は、C言語の配列とあまり変わりません。 スタック上に確保され、要素数は変えられません。 一方、動的配列はヒープ上に確保され、要素数を変えられます。


a2 ~= 5;
assert(a2 == [5]);
assert(a2.length == 1);
a2.length = 3;
assert(a2 == [5, 0, 0]);
assert(a2.length == 3);

動的配列は、実のところ以下のような実装になってると考えることができます。


struct Array(T){
    *T p;
    size_t length;
}

要は、D言語の動的配列は、長さ情報を持っているポインタ、つまりスライスです。 なので、配列の一部を取ってくる操作などが定数時間で行えます。


int[] a = [0, 3, 5];
int[] b = a[1..$];
assert(b == [3, 5]);

簡単にスライスが取れるあたり、とても便利ですね。

動的配列は、要はポインタなので、以下のようなコードはコンパイルが通りません。


const(int[]) a = [1, 1, 2, 3, 5];
int[] b = a; // コンパイルエラー

このコードは、簡単に言うとconst(int)*をintに暗黙にキャストしようとしています。 D言語ではそのような暗黙のキャストはやってくれないので、コンパイルエラーが出ます。

初期化時に動的配列の確保サイズを指定したい場合は以下のように書きます。


int[] a = new int[](3);
int[][] b = new int[][](4, 5);

いちいち初期化後にa.length = 3と書く手間が省けます。

D言語は、動的配列の境界チェックを実行時に行なっているので、


int[] a = [1, 2, 3];
a[3] = 5; // RangeErrorが投げられる

範囲外にアクセスするとRangeErrorという例外が投げられます。 スタックトレースも表示されるので、どこのコードが範囲外にアクセスしたかがわかります。

コンパイルするときに-noboundsオプションをつけると、動的配列の境界チェックを無効にすることができます。 実行速度を稼ぐ手段として有効です。