動的配列
だいぶ前に書いたちょっとしたメモのせいで、このブログに「C/C++/C# 動的配列」といったキーワードで飛んでくる方が結構いるみたいです。大して書いてなくて申し訳ないので勉強兼ねてまとめときます。
動的配列とは
「動的にメモリを確保した配列」と「動的にサイズが変えられる配列」がごっちゃになっているかもしれません。通常は後者を指すようです。
Cの場合
おそらく標準では特に用意されてないので、自分で作るか若しくはどこかからライブラリを持ってくるかしなければなりません。
標準ライブラリでやるなら、mallocやcallocで動的に配列の領域を確保して、サイズが足りなくなったらreallocということになるかと。
// #include <stdlib.h> char* ary; char* temp; // 長さ10の配列を動的に確保 if ((ary = (char*)calloc(10, sizeof(char))) == NULL) { puts("calloc失敗"); exit(EXIT_FAILURE); } // 何か入れてみる strcpy(ary, "Hogehoge"); // サイズを増やす if ((tmp = (char*)realloc(ptr, 50*sizeof(char))) == NULL) { puts("realloc失敗"); free(ary); exit(EXIT_FAILURE); } else { ptr = tmp; } // ちゃんと"Hogehoge"が残っていることを確認 puts(ary); // 10文字以上入れられることを確認 strcpy(ary, "HogehogeFugafuga"); // メモリ解放 free(ary);
これを毎回書くのは大変なのでうまく関数とかにするといいんですが、次のC++やC#には標準ライブラリに用意されていて楽です。
C++の場合
色々ありますが、一番普通なのはstd::vector
// #include <vector> std::vector<char> vector; // いくらでも追加できる for(char c='a'; c<'z'; c++){ vector.push_back(c); } // インデックスでアクセスできる for(int i=0; i<vector.size(); i++){ vector[i]++; } vector.push_back('\0'); // 要素はメモリ上で連続していると保障されてるので、先頭要素のアドレスを取れば char* な配列として扱える puts( &vector[0] );
C#の場合
一番よく使われると思われるのはSystem.Collections.Generic.List
// using System.Collections.Generic; List<char> list = new List<char>(); // いくらでも追加できる for(char c='a'; c<'z'; c++){ list.Add(c); } // インデックスでアクセスできる for(int i=0; i<list.Count; i++){ vector[i]++; } // 普通の配列にしたければToArray char[] ary = list.ToArray();
なお、ほとんど使わないと思いますが、普通の配列のサイズをreallocのように変えたいときは、System.Array.Resize
int[] ary = new int[10]; Array.Resize(ref ary, 50);
C#で固定長配列
話が脱線しますが、C#でいう
int[] hoge = new int[10];
は、Cでいうところの
int* hoge = (int*)calloc(10, sizeof(int));
のようなものであり、Cでの
int hoge[10];
のようにコンパイル時に長さが決まってしまう固定長配列とはかなり意味合いが違います。
C#でCのような固定長配列を定義するには、unsafeの中でstackallocというキーワードを使います。ほぼCと同じように使えますが、バッファオーバーラン防止機能がついています。
unsafe { int* hoge = stackalloc int[10]; }
また、P/Invokeの際などで構造体に固定長配列のメンバ変数を持たせたい場合は、fixedというキーワードを使います。
unsafe struct MyStruct{ public fixed byte hoge[10]; }