配列
この記事は中立的な観点に基づく疑問が提出されているか、議論中です。(2018年11月) |
配列とは
[編集]int score1;
int score2;
int score3;
int score4;
int score5;
int score6;
// 例えば標準入力経由で各生徒の得点を各変数に読み込んだとする。
double mean = (double)(score1 + score2 + score3 + score4 + score5 + score6) / 6;
しかし、この方法では生徒数が増減したときに、変更や拡張が大変になってしまう。より良い解は6要素の配列を使うことである。
int score[6]; // 6要素の配列が作られる。
// 例えば標準入力経由で各生徒の得点を配列scoreに読み込んだとする。
double mean = 0;
for (int i = 0; i < 6; ++i) {
mean += score[i]; // 配列の各要素へは、変数scoreを通してscore[0]からscore[5]のようにしてアクセスする。
}
mean /= 6;
配列を用いることで、添え字演算子による統一的なアクセスおよび一括処理が可能となる。また、処理すべきデータ個数が増減したときにも対応しやすくなる。
配列の動的確保
[編集]前述の例では、プログラム中で宣言時に指定した固定のサイズ(整数定数)による配列確保(静的確保)であった。実用的には、配列の要素数が宣言時(あるいはコンパイル時)に静的に決まってしまうよりも、実行時に要素数を動的に指定して配列を確保できたほうが便利なことがある。例えば、縦横任意サイズの画像ファイルから全画素情報を読み出す場合や、コンピュータで利用可能な空きメモリ量に合わせて扱うデータ個数上限を変化させたい場合などである。
多くのプログラミング言語では、配列のサイズをプログラム実行時に指定して配列を生成する(動的に確保する)手段が用意されている。例えばC言語ではmalloc関数やcalloc関数を利用する。確保に成功するとメモリブロック(配列先頭要素)へのポインタが返却され、このポインタ経由で配列を操作する。
int numStudents; // 例えば標準入力経由でnumStudentsに生徒数 (> 0) を読み込んだとする。 int* score = calloc(numStudents, sizeof(int)); // 要素数がnumStudents、各要素のサイズがint型のサイズであるような配列を動的に確保し、0で初期化する。 // 例えば標準入力経由で各生徒の得点を配列scoreに読み込んだとする。 double mean = 0; for (int i = 0; i < numStudents; ++i) { mean += score[i]; // 動的に確保した場合でも、配列の添え字シンタックスは同じ。 } mean /= numStudents; free(score); // 使い終わった配列のメモリ領域を解放する。
C++などの後発の言語では、動的メモリ確保のために通例new演算子が用意されていることが多く、配列の動的確保には型と要素数を指定するnew[]
演算子を使用する。
int numStudents; // 例えば標準入力経由でnumStudentsに生徒数 (> 0) を読み込んだとする。 int* score = new int[numStudents](); // 要素数がnumStudentsであるようなint型の配列を動的に確保し、0で初期化する。 // 例えば標準入力経由で各生徒の得点を配列scoreに読み込んだとする。 double mean = 0; for (int i = 0; i < numStudents; ++i) { mean += score[i]; // 動的に確保した場合でも、配列の添え字シンタックスは同じ。 } mean /= numStudents; delete[] score; // 使い終わった配列のメモリ領域を解放する。
計算オーダー
[編集]さまざまな配列
[編集]連想配列
[編集]添え字は一般に通例0か1始まりの (非負) 整数である。一方、文字列など他のデータ型を添え字のように使用できる配列を連想配列という。
固定長配列
[編集]決まった要素数しか格納できない配列を、固定長配列 (fixed-length array, fixed-size array) あるいは静的配列 (static array) と呼ぶ。
動的配列
[編集]可変長配列
[編集]void func(size_t n) {
int data[n];
}
多次元配列
[編集]a[i, j]
などといったような構文でアクセスする。
C#による多次元配列の例を示す。
int[,] array2d = {
{0, 1, 2, 3},
{4, 5, 6, 7},
{8, 9, 10, 11},
};
System.Console.WriteLine(array2d[2, 3]);
C#には、後述するジャグ配列となる「配列の配列」もある。
C言語の場合
[編集]C言語は規格で多次元配列に関する言及があるが、実際にサポートされているのは「配列の配列」であって、真の多次元配列ではない[8]。次のようなコードのことを考えてみればわかる。
void f(int (*p_arr3)[3]) {
……
}
int main(void) {
int arr5_arr3[5][3];
f(arr5_arr3);
return 0;
}
ここで arr5_arr3
は「『intの3要素の配列』の5要素の配列」である。そして、関数fに渡される際には、C言語の「配列は引数として渡される際は、その先頭要素を指すポインタに縮退する」というルールにより、その先頭の「intの3要素の配列」を指すポインタがp_arr3
に渡される。
もし仮にC言語で真の多次元配列がサポートされているならば、それぞれ「intの5x3要素の配列」「『intの5x3要素』を指すポインタ」(あるいは、単にintを指すポインタに縮退するかもしれない)などがサポートされるはずだが、実際にはサポートされない。
Javaの場合
[編集]ジャグ配列
[編集]「配列の配列」の場合、内側の配列について、要素数が揃っていることを要求しないデータ構造であることもある。ジャグ配列 (jagged array)、不規則配列などと言う。これに対し、内側の配列の要素数が揃った配列を矩形配列 (rectangular array) などと言う。Javaにおける配列の配列はジャグ配列である。C#には前述の通り、「真の多次元配列」もあるが、それとは別に配列の配列もあり、そちらはJavaと同様のジャグ配列である。
Javaによるジャグ配列の例を示す。
int[][] numArr = new int[3][];
numArr[0] = new int[]{1, 2, 3};
numArr[1] = new int[]{4, 5, 6, 7};
numArr[2] = new int[]{8, 9};
System.out.println(numArr[1][1]);
C#によるジャグ配列の例を示す。
int[][] numArr = new int[3][];
numArr[0] = new int[]{1, 2, 3};
numArr[1] = new int[]{4, 5, 6, 7};
numArr[2] = new int[]{8, 9};
System.Console.WriteLine(numArr[1][1]);
int *numArr[3];
int tmp0[] = {1, 2, 3};
int tmp1[] = {4, 5, 6, 7};
int tmp2[] = {8, 9};
numArr[0] = tmp0;
numArr[1] = tmp1;
numArr[2] = tmp2;
printf("%d\n", numArr[1][1]); // print "5"
Iliffe vector
[編集]脚注
[編集]注釈
[編集]- ^ リンクリストではなく、メモリ上で連続しているため、ランダムアクセスは定数時間のO(1)となる。