この文書の URL は http://www.cc.kyoto-su.ac.jp/~mtkg/lecture/comp_B/2012/13.html です。
配列とは
数学では n 次元ベクトル x の各要素を表すのに x1, x2, ..., xn といった書き方をします。 この要素の番号のことを「添字」といいます。
x = (x1, x2, ..., xn)
配列 (array) というデータ構造を使うと、添字を使ってベクトルや行列、表になったデータなどを簡単に扱うことができます。
配列の宣言
配列を定義するには変数宣言のときに配列名と配列の大きさ(要素数)を指定します。 次のように宣言すると double 型で要素数 3 の配列 x が定義されます。
double x[3]; /* x[0], x[1], x[2] が利用可能 */
配列の要素は x[0]
, x[1]
, x[2]
のように表されます。
配列名に続く [ ]
のことを「添字演算子」といいます。
C の配列の添字はつねに 0 番からはじまることに注意して下さい。
したがって、要素数 n の配列では 0 から n-1 までが有効な添字の範囲となります。
配列の例
次の例は、配列 a の各要素に先頭から 1, 2, 3, 4, 5 を代入し、逆順に表示するプログラムです。
/* work1301.c */ #include <stdio.h> int main(void) { int a[5]; /* 要素数 5 の int 型の配列 */ int i; /* 配列に値をセット */ for (i = 0; i < 5; i++) { /* i = 0, 1, ..., 5 */ a[i] = i + 1; } /* 逆順に表示 */ for (i = 0; i < 5; i++) { printf("%d\n", a[4-i]); /* 添字 4-i に注意 */ } return 0; }
エラーになる時は
範囲外の配列要素にアクセスすると、エラーメッセージが表示されたり、計算結果がおかしくなったりします。
例えば、要素数 10 の配列の 11 番目の要素にアクセスしようとした時などです。
このようなミスはなかなか見つけられないことがあります。
そういう時は -fbounds-check
というコンパイルオプション利用しましょう。
このオプションを付けてコンパイルすると、配列の添字のチェックをしてくれます。
% gcc -fbounds-check work1301.c
ただし、このオプションを付けると動作が遅くなります。 正しく動作するようになったらオプションを外して、コンパイルし直して下さい。
配列の初期化
配列を初期化するには { }
の中に各要素に対する初期値をコンマで区切って並べます。
要素数を省略することもできます。
int a[5] = { 1, 2, 3, 4, 5 }; int b[] = { 1, 2, 3, 4, 5 }; /* 要素数を省略すると自動的に 5 となる */
右辺の要素数が不足する場合、残りの要素はすべて 0 になります。 逆に、右辺の要素数が多すぎるとエラーになります。
int a[5] = { 1, 2, 3 }; /* a[3] = 0, a[4] = 0 となる */ int b[5] = { 0 }; /* 全要素を 0 で初期化 */ int c[5] = { 1, 2, 3, 4, 5, 6 }; /* エラー */
配列のコピー
配列 a から配列 b にデータをコピーするには、すべての要素についてひとつずつ代入する必要があります。
Fortran 90/95 のような配列名を使った代入 b = a
はできません。
/* work1302.c */ #include <stdio.h> int main(void) { int a[5] = { 1, 2, 3, 4, 5 }; int b[5] = { 0 }; int n; /* 配列のコピー: b = a; は不可 */ for (n = 0; n < 5; n++) { b[n] = a[n]; } return 0; }
練習問題
上のプログラムを修正して、配列 b が { 5, 4, 3, 2, 1 }
となるようにせよ。
配列に値を読み込む
配列に値を読み込み、それを逆順に並び替えてみましょう。 scanf の使い方はこれまでと同様です。
/* work1304.c */ #include <stdio.h> int main(void) { int a[5], n; /* 入力 */ for (n = 0; n < 5; n++) { printf("a[%d]: ", n); scanf("%d", &a[n]); /* & を忘れないように */ } /* 並び替え */ for (n = 0; n < 5/2; n++) { /* i = 0, 1 */ int tmp; /* ブロック内で一時変数を宣言 */ tmp = a[n]; a[n] = a[4-n]; a[4-n] = tmp; } /* 表示 */ for (n = 0; n < 5; n++) { printf("a[%d] = %d\n", n, a[n]); } return 0; }
実行すると次のようになります。
% ./a.out a[0]: 3 a[1]: 2 a[2]: 4 a[3]: 1 a[4]: 5 a[0] = 5 a[1] = 1 a[2] = 4 a[3] = 2 a[4] = 3
値の交換方法
x と y の値を入れ替えるのに
x = y; y = x;
としてはいけません(どういうことが起こるか考えてみましょう)。 値を交換するには、作業変数をひとつ用意して
tmp = x; /* x の値を作業変数に保存 */ x = y; /* x に y の値を代入 */ y = tmp; /* 保存しておいた x の値を y に代入 */
とする必要があります。
練習問題1
5個の整数を配列 a に読み込み、最大値を見つけるプログラムを作成せよ。
練習問題2
2つの3次元ベクトル x, y をキーボードから入力し、内積 x.y を計算するプログラムを作成せよ。
多次元配列
2次元配列を使うと行列が扱えるようになります。 次の例は2行3列の2つの行列 A, B の和を求めるプログラムです。 行列の初期化子の対応関係に注意してください。
/* work1307.c 行列の和 A = ( 1 2 3 ) B = ( 3 4 5 ) ( 4 5 6 ) ( 6 7 8 ) */ #include <stdio.h> int main(void) { int ma[2][3] = { {1, 2, 3}, {4, 5, 6} }; int mb[2][3] = { {3, 4, 5}, {6, 7, 8} }; int mc[2][3] = { 0 }; /* 全要素を 0 に */ int i, j; for (i = 0; i < 2; i++) { /* i = 0, 1 */ for (j = 0; j < 3; j++) { /* j = 0, 1, 2 */ mc[i][j] = ma[i][j] + mb[i][j]; } } for (i = 0; i < 2; i++) { for (j = 0; j < 3; j++) { printf("%5d", mc[i][j]); } printf("\n"); } return 0; }
3次元以上の配列も使えますが、次元数の上限は処理系によって異なるようです。
本日の課題
1. 最小値
キーボードから5つの整数を入力し、その最小値を求めるプログラムを作成せよ。
2. 平均値
キーボードから5つの整数を入力し、その平均値を求めるプログラムを作成せよ。
3. 行列の積
次のような3行3列の2つの行列 A, B の行列積 C=AB を求めるプログラムを作成せよ。
A = ( 1 2 3 ) B = ( 3 6 9 ) ( 4 5 6 ) ( 2 5 8 ) ( 7 8 9 ) ( 1 4 7 )
行列 C の (i, j) 成分 Ci,j は次のように計算される。
Ci,j = Σ Ai,k * Bk,j (Σは k = 0, 1, 2 を渡る)
チャレンジ問題
1. パスカルの三角形
以下の下三角行列はパスカルの三角形と呼ばれる。
1 0 0 0 ... 0 1 1 0 : : 1 2 1 0 1 3 3 1 0 1 4 6 4 1 0 : 1 5 10 10 5 1 0 1 6 15 20 15 6 1
この行列を配列 int p[10][10]
に作れ。
上三角行列はすべて 0 とし、第 0 列と対角要素をすべて 1 とすると、他の要素はすべて真上と左上の要素の和になる。
2. ルジャンドル多項式
ルジャンドル関数 Pn(x) は |x| <= 1 を満たす x に対して次の漸化式で定義される。
P0(x) = 1 P1(x) = x Pn(x) = [(2*n-1) * x * Pn-1(x) - (n-1) * Pn-2(x)] / n (n >= 2)
P5(x) の値を -1 から 1 までの x について 0.1 刻みで求めよ。
3. 黄金比の連分数展開
黄金比 (1 + √(5)) / 2 は次のような連分数で表される。
1 + √5 1 ------- = 1 + --------------- 2 1 1 + ---------- 1 1 + ----- :
右辺全体を X とすると、右辺第2項の分母が X に等しいから
1 X = 1 + --- X
という関係が成立することがわかる(実際、この方程式を解くと黄金比が得られる)。 そこで次のような漸化式を考える。
1 Xn+1 = 1 + ---- (n = 1, 2, ...) Xn
初項を X1 = 1 として、Xn の値が一定値に収束するまで計算を行い、結果が黄金比に一致することを確認せよ。 初項の値をいろいろに変えた場合について数値実験を行い、結果がどのように変わるか調べること。