C言語/配列

出典: フリー教科書『ウィキブックス(Wikibooks)』
ナビゲーションに移動 検索に移動

配列の基本[編集]

配列型(はいれつがた)は、特定の型のオブジェクトの集合を連続して割り付けたものです[1][2]。 配列型を使うことで、同じ型の複数のデータを、共通の名前で、添字(そえじ)と呼ばれる番号を用いて、アクセスすることができます[3][4]

例えば、10個の変数の合計を求めるプログラムは、配列を用いて次のように書き換えることができます。

スカラー変数を使ったコード
#include <stdio.h>

int main(void) {
  const int i0 = 2, i1 = 3, i2 = 5, i3 = 7, i4 = 11,
      i5 = 13, i6 = 17, i7 = 19, i8 = 23, i9 = 29;
  int sum = i0 + i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9;
  printf("%d\n", sum);
}
配列変数を使ったコード
#include <stdio.h>

int main(void) {
  const int ary[] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29};
  int sum = 0;
  for (int i = 0; i < sizeof ary / sizeof *ary; i++) {
    sum += ary[i];
  }
  printf("%d\n", sum);
}
配列を宣言すると同時に要素を初期化リストで初期化すると、要素数は諸略できます。
また、ループカウンターやインデックスの変数名には慣例的に i が使われます(入れ子の場合は j, k ... とすることが多いです。また i1, i2 ... という流儀もありますが、一貫していることが大切です)。

配列には、添字が1つの1次元の配列と、添字が2つ以上の多次元の配列がある。

1次元の配列[編集]

1次元の配列の宣言[編集]

1次元の配列の宣言の記述は次のようになります[5]

データ型 配列名[配列の要素数];

データ型をデータ型に持って、 配列名を名前に持って、 配列の要素数だけ要素の数を持った配列を宣言します。 データ型と配列名は、変数の宣言と同様です。 C言語/データ型と変数#データ型と変数の基本を参照せよ。 配列の要素数は、配列の中の要素の数を表す。 要素数は0より大きな整数でなければならない。 [6]

図1
要素番号
0 不定
1 不定
2 不定
//例 配列を宣言します。
int main(void)
{
	int ary[3]; // int型3つ([0]~[2])の配列を宣言します。※図1
}

上の例では、int型でaryという名前で3つの要素を持つ配列を宣言しています。 3つの要素は配列名のあとに[]に囲まれた添字と呼ばれる番号を付けて区別します。 添字は[0]から順番に数え、[配列の要素数-1] までの整数です。 つまり ary[0]、ary[1]、ary[2] というようになります。

また、配列は、宣言と同時に、値のリストで初期化することもできます。 初期化していないローカルな配列の値は不定です。 初期化の記述は次のようになります[7][8]

データ型 配列名[配列の要素数] = { 値のリスト };
  • 値のリストは、コンマで区切った定数のリストです。
  • 値のリストの定数で、対応する配列の要素の値を初期化します。
  • 値のリストが配列の要素数より多い場合、コンパイルエラーとなるとは限りません。
  • 値のリストが配列の要素数より少ない場合、残りの要素は0で初期化されます。
  • 値のリストでの初期化は宣言時のみ可能です。
  • 宣言後に初期化代入することは出来ません。
  • 配列変数同士の代入も出来ません。
図2
要素番号
0 1
1 2
2 3
//例 配列を宣言し初期化します。

int main(void)
{
  int ary[3] = { 1, 2, 3 };  // int型3つ([0]~[2])の配列を宣言し、1,2,3で初期化します。※図2
}

上の例では、ary[0]が1、ary[1]が2、ary[2]が3でそれぞれ初期化されています。

配列を値のリストで初期化する場合、要素数を省略できます。

図3
要素番号
0 1
1 6
2 8
//例 要素数を省略して、配列を宣言し初期化します。
int main(void)
{
	int ary[] = { 1, 6, 8 }; // int型3つ([0]~[2])の配列を宣言し、1, 6, 8で初期化します。※図3
}

上の例では、値のリストの要素数で配列の要素数が決まります。

1次元の配列の代入[編集]

1次元配列の要素への代入の記述は次のようになります。

配列名[添字] = ;

配列名が指す配列の「添字」で指定した順位の要素に式の値を代入します。 添字とは、アクセスする配列の要素を指定するもので整数型であれば変数でも構いません。 添字は 0 から始まり、配列の要素数から 1 を減じた値を取りえる整数値です[9]

図4a
要素番号
0 不定
1 不定
2 不定
3 不定
図4b
要素番号
0 2
1 4
2 8
3 16
//例 配列に代入します。

int main(void)
{
	int ary[4]; // int型4つ([0]~[3])の配列を宣言します。※図4a
	ary[0] = 2; // 配列の夫々の要素に数値を代入します。※図4b
    ary[1] = 4;
    ary[2] = 8;
    ary[2] = 16;
}

上の例では、ary[0]に2、ary[1]に4、ary[2]に8、ary[2]に16をそれぞれ代入しています。

1次元の配列の参照[編集]

1次元配列の参照の記述は次のようになります。

配列名[添字]

配列名が指す配列の[添字]番目の要素を参照します(代入も左辺値参照ですという意味で参照の一種です)[9]

図5a
要素番号
0 0
1 10
2 20
図5b
要素番号
0 200
1 10
2 20
//例 配列を参照します。

int main(void)
{
	int ary[] = { 0, 10, 20 }; // int型3つ([0]~[2])の配列を宣言し、0, 10, 10で初期化します。※図5a
	ary[0] = ary[1] * ary[2]; // 配列の[1]と[2]の数値を参照し、配列の[0]にその和を代入します。※図5b
}

上の例では、ary[1]とary[2]の要素が参照され、 その積をary[0]の要素に代入しています。

注意!
C言語では、配列の添字に関しす範囲外チェックをしません。
範囲外にならないように、気をつけてください。

多次元の配列[編集]

多次元の配列の宣言[編集]

多次元配列の宣言は、配列名の後に、次元の数だけ[配列の要素数]を追加します。 例えば3行×4列の2次元配列の宣言は、次のように記述できます。

//例 多次元の配列を宣言します。
int main(void)
{
  int ary[3][4];
}

上の例では、次の表のように3行×4列の配列が確保されます。

行\列 [*][0] [*][1] [*][2] [*][3]
[0][*] ary[0][0] ary[0][1] ary[0][2] ary[0][3]
[1][*] ary[1][0] ary[1][1] ary[1][2] ary[1][3]
[2][*] ary[2][0] ary[2][1] ary[2][2] ary[2][3]

実際には、次の表のような順番で配列がメモリ上に確保されます。

メモリ
ary[0][0]
ary[0][1]
ary[0][2]
ary[0][3]
ary[1][0]
ary[1][1]
ary[1][2]
ary[1][3]
ary[2][0]
ary[2][1]
ary[2][2]
ary[2][3]
二次元配列の要素のアドレスを調べるコード
#include <stdio.h>
int main(void)
{
  int ary[3][4];
  for (int i = 0; i < 3; i++)
    for (int j = 0; j < 4; j++)
      printf("Address of ary[%d][%d] = %p\n", i, j , &ary[i][j]);
}
結果
Address of ary[0][0] = 0x7ffd24b47a80
Address of ary[0][1] = 0x7ffd24b47a84
Address of ary[0][2] = 0x7ffd24b47a88
Address of ary[0][3] = 0x7ffd24b47a8c
Address of ary[1][0] = 0x7ffd24b47a90
Address of ary[1][1] = 0x7ffd24b47a94
Address of ary[1][2] = 0x7ffd24b47a98
Address of ary[1][3] = 0x7ffd24b47a9c
Address of ary[2][0] = 0x7ffd24b47aa0
Address of ary[2][1] = 0x7ffd24b47aa4
Address of ary[2][2] = 0x7ffd24b47aa8
Address of ary[2][3] = 0x7ffd24b47aac

(16進数ですが)要素ごとのアドレスが4づつ離れているのが判ると思います(アドレスと増分は環境や処理系によって変わります)。

多次元配列も1次元配列と同様に、配列の宣言と同時に値のリストで初期化することができます。 値のリストは、各次元の添字が[0]のものから始まり、一番右の次元の添字が1つずつ増えていき、その添字の数が要素数まで増えたら、1つ左の次元へ繰り上がる。

//例 多次元の配列を宣言し初期化します。

int main(void)
{
	int ary[3][4] = {
		0, 1, 2, 3,
		4, 5, 6, 7,
		8, 9, 10, 11
	};
}

上の例では、次の表のように初期化されます。

行\列 [*][0] [*][1] [*][2] [*][3]
[0][*] ary[0][0]
0
ary[0][1]
1
ary[0][2]
2
ary[0][3]
3
[1][*] ary[1][0]
4
ary[1][1]
5
ary[1][2]
6
ary[1][3]
7
[2][*] ary[2][0]
8
ary[2][1]
9
ary[2][2]
10
ary[2][3]
11

多次元配列を値のリストで初期化する場合、1番左の次元の要素数は省略できます。

//例 要素数を省略して、多次元の配列を宣言し初期化します。

int main(void)
{
	int ary[][4] = {
		0, 1, 2, 3,
		4, 5, 6, 7,
		8, 9, 10, 11
	};
}

多次元の配列の代入[編集]

多次元配列の代入は、 配列名の後に、 次元の数だけ[添字]を追加します。 [9]

//例 多次元の配列に代入します。

int main(void)
{
	int ary[3][4];
	int x, y;

	for (int y = 0; y < 3; y++)
		for (int x = 0; x < 4; x++)
			ary[y][x] = 4 * y + x;
}

上の例では、次の表のように、代入されます。

行\列 [*][0] [*][1] [*][2] [*][3]
[0][*] ary[0][0]
0
ary[0][1]
1
ary[0][2]
2
ary[0][3]
3
[1][*] ary[1][0]
4
ary[1][1]
5
ary[1][2]
6
ary[1][3]
7
[2][*] ary[2][0]
8
ary[2][1]
9
ary[2][2]
10
ary[2][3]
11

多次元の配列の参照[編集]

多次元配列の参照も同様に、配列名の後に次元の数だけ[添字]を追加します[9]

//例 多次元の配列を参照します。
#include <stdio.h>

int main(void)
{
	int ary[3][4] = {
		0,1,2,3,
		4,5,6,7,
		8,9,10,11
	};
	int x, y;

	for (int y = 0; y < 3; y++){
		for (int x = 0; x < 4; x++){
			printf("%2d ", ary[y][x]);
		}
		printf("\n");
	}
}

配列全体のコピー[編集]

配列全体をコピーする場合、各要素を1つずつコピーしなければなりません[10]

配列の要素を1つずつコピーする例
#include <stdio.h>

int main(void)
{
	int ary1[] = { 0, 1, 2 };
	int ary2[sizeof ary1 / sizeof *ary1];
	for (int n = 0; n < sizeof ary1 / sizeof *ary1; n++)
		ary2[n] = ary1[n]; // ary1の要素をary2の要素に1つ1つコピーします。
	for (int i = 0; i < sizeof ary1 / sizeof *ary1; i++)
	    printf("ary2[%i] = %d\n", i, ary2[i]);
}

また、memcpy関数を用いることで、配列全体をコピーできます。 memcpy関数は、ヘッダファイル <string.h> で宣言されています。

memcpy関数を用いた例
#include <stdio.h>
#include <string.h>

int main(void)
{
	int ary1[] = { 0, 1, 2 };
	int ary2[sizeof ary1 / sizeof *ary1];
	memcpy(ary2, ary1, sizeof ary1); // ary1の要素を全てary2にコピーします。
	for (int i = 0; i < sizeof ary1 / sizeof *ary1; i++)
	    printf("ary2[%i] = %d\n", i, ary2[i]);
}

可変長配列[編集]

C99から、可変長配列がサポートされました[11]

可変長配列の例
#include <stdio.h>

int i = 2, j = 3, k, koo;
int *p = &i;

int func(int n) {
    char b[*p==n ? k = j++ : n+j];
    return (sizeof(b));
}

int main(void){
    // Your code here!
    koo = func(10);
    printf("koo = %d\n", koo);
}

が一例です[12]

全ての配列を可変長にできるわけではなく要件がありまます[11]

  • バリアブルモディファイド(VM)型の宣言はすべて、ブロックスコープか、関数プロトタイプスコープのいずれかでなければなりません。
  • 記憶クラス指定子が _Thread_local, static, extern のいずれかで宣言された配列オブジェクトは、可変長配列(VLA)型を持つことができません。
    • ただし、static 記憶クラス指定子で宣言されたオブジェクトは、VM 型(つまり VLA 型へのポインタ)を持つことができます。
  • 最後に、VM型で宣言されたすべての識別子は通常の識別子でなければならず、構造体やユニオンのメンバーにはなれません。

脚註[編集]

  1. ^ WG14/N1570 Committee Draft — April 12, 2011 ISO/IEC 9899:201x (C11). ISO/IEC. p. 42, §6.2.5 Types ¶20. http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf. 
  2. ^ 『JISX3010:2003』p.24「6.2.5 型」
  3. ^ WG14/N1570 Committee Draft — April 12, 2011 ISO/IEC 9899:201x (C11). ISO/IEC. p. 80, §6.5.2.1 Array subscripting. http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf. 
  4. ^ 『JISX3010:2003』p.51「6.5.2.1 配列の添字付け」
  5. ^ WG14/N1570 Committee Draft — April 12, 2011 ISO/IEC 9899:201x (C11). ISO/IEC. p. 130, §6.7.6.2 Array declarators. http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf. 
  6. ^ 『JISX3010:2003』p.86「6.7.5.2 配列宣言子」
  7. ^ WG14/N1570 Committee Draft — April 12, 2011 ISO/IEC 9899:201x (C11). ISO/IEC. p. 125, §6.7.8 Initialization. http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf. 
  8. ^ 『JISX3010:2003』p.94「6.7.8 初期化」
  9. ^ 9.0 9.1 9.2 9.3 『JISX3010:2003』p.51「6.5.2.1 配列の添字付け」
  10. ^ 配列全体のコピー(=配列の代入)ができなこととを以って、配列はC言語の第一級オブジェクトでないと言われます。文字列も文字の配列(に番兵として '\0' で終端したもの)なので、配列と同様に文字列も第一級オブジェクトではありません。
  11. ^ 11.0 11.1 C11: WG14/N1570 Committee Draft — April 12, 2011 ISO/IEC 9899:201x. ISO/IEC. p. 115, §6.7.5.2 Array declarators. http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf. 
  12. ^ Array of Variable length

参考文献[編集]