C プログラミング入門 基幹7 (水5) 07: 配列 Linux にログインし、以下の講義ページ を開いておくこと http://www-it.sci.waseda.ac.jp/ teachers/w483692/CPR1/ 2016-05-25 1 例題 2 つの並列接続抵抗の合成抵抗を求めたい 𝑅 = 𝑅1−1 + 𝑅2−1 −1 { double R, R1, R2; scanf("%lf", &R1); scanf("%lf", &R2); R = 1 / ( 1/R1 + 1/R2 ); printf("%f\n", R); 2 例題 100個の合成抵抗の場合は? −1 100 { double R, R1, R2, …, R100; scanf("%lf", &R1); scanf("%lf", &R2); … scanf("%lf", &R100); −1 𝑅𝑖 𝑅= 𝑖=1 反復計算でどうにか したい R = 1 / ( 1/R1 + 1/R2 + 1/R3 + … + 1/R100 ); printf("%f\n", R); 100 個の項の和を書 くのはちょっと… 3 配列を使った方法 今回は以下のようなものを理解する { double R, R[100], S; int i; for(i = 0; i < 100; ++i) { scanf("%lf", &R[i]); } for(S=0, i=0; i < 100; ++i) { S += (1 / R[i]); } R = 1 / S; 𝑅 = 𝑆 −1 100 𝑅𝑖−1 𝑆= 𝑖=1 4 復習:メモリと変数 メモリの一部に名前を付けて、数値を格納す る機能=変数 int year = 2014; 位置は自動的に決定される 表現できる値は型で規定 メモリ上でのサイズは型依存 double value; char ch = 'C'; 初期値を与えない場合は不定 変数名 初期化しない場合、値として何が入っているかはわからない year 2014 value ch ?? 'C' それぞれの位置は自動で決まるので、並んでいるとは限らない 5 配列変数 複数個の値を連続して格納する変数 1つ1つを要素 (element) という 要素はインデックス (index) という整数で区別 各要素をインデック 最初の要素のインデックスは 0 スで区別 scores 配列変数名 scores[0] scores[1] scores[2] scores[3] 100 95 55 43 score1 100 score2 95 score3 55 score4 43 4人のスコアを 配列変数で格 納した場合 4人のスコアを 4つの変数で格 納した場合 6 配列変数の定義 (array) 構文 日本語では 0 オリジンという。 英語では zerobased などと 呼ぶ 型名 変数名[要素数]; 普通の変数と同じ名前は不可 インデックスは 0 から始まる 要素数 n の配列の最後の要素のインデック スは n-1 score[4] はない { scores[0] int score[4]; ?? scores[2] ?? scores[1] ?? ?? scores[3] 7 配列の初期化 初期化には特別な構文を用いる 指定する初期値が足りない場合は残りはすべて 0 となる 4つの要素の初期値は不定 a { ? int int int int int a[4]; b[4] = { 100, 50, 20, 1 }; c[4] = { 100, 50 }; すべてゼロに d[4] = { 0 }; する場合 e[] = { 100, 50, 20, 1 }; ? ? 50 0 0 0 0 0 c 100 d 0 要素数を省略すると初期化の要素数となる int f[4] = { 1, 2, 3, 4, 5 }; ? エラー 足りない分は すべて0となる 8 配列変数のアクセス 配列変数の各要素へはインデックスを指定し て、通常の変数と同じようにアクセスできる { int a[4] = { 100, 50, 20, 1 }; int sum; 要素への代入 a[0] = 99; 要素の値を読む printf("%d", a[0]); 計算式に要素の値を使う sum = a[0]+a[1]+a[2]+a[3]; 9 できないこと 配列全体への代入はできない 配列同士のコピーもできない { int a[4]; int b[4] = { 1, 2, 3, 4}; そのような文法 が用意されてい ないという意味 配列のコピーは、 標準ライブラリ の関数で行うこ とができる(後 の講義で解説) a = { 100, 50, 20, 1 }; // エラー a = b; // エラー 10 配列変数とループ 配列の利点として、ループでアクセスできる ループ変数にインデックスを対応させる 格納される値 { int tri[10], i; tri[0] == 0 i=3 tri[1] == 1 for(i = 0; i < 10; ++i) { tri[i] = i*(i+1)/2; } tri[2] == 3 tri[3] == 6 tri[4] == 10 この数列を三 角数という tri[5] == 15 ... 11 範囲外アクセスの注意 配列のアクセスではインデックスの範囲は チェックされない 範囲外の値を読んだり、書きこんだりするこ とは理由がない限り避ける 場合によってはシステムが検知してセグメンテー ションフォルト例外(segmentation fault; SEGV) が発生してプログラムが強制終了する a[-1] としてアクセスできるが なにが入っているかは不明 a[0] 100 95 55 a[4] としてアクセスした場合、 意味のある値としては読めない a[3] 43 pi 3.14 このようなアク セスを意図的に 行う場合もある 12 配列変数の制限 配列変数の要素数には限界がある 環境依存。せいぜい数千~数万要素程度 巨大な領域を取るには動的メモリを用いる 今後説明 配列サイズを変更できない プログラムの実行中に変更が必要な場合は、より 高度なデータ構造を使う必要がある 秋期の講義で扱う 配列のコピーを行う構文はない コピーを行う標準ライブラリ関数を用いる 今後説明 13 多次元配列 インデックスを複数使う配列 全部で 6 (=3×2) 要素 { int A[3][2] = { {1,2}, {3,4}, {5,6} }; 各要素がさらに 2 要素 に分かれている 全体として 3 要素 int B[3][2] = { { 0 } }; A[0][0] 1 A[1][0] 2 3 全ての要素を 0 で初期化する場合 A[2][0] 4 5 6 A[1][1] A[2][1] A[0][1] メモリ上では初期化と同じ順に並ぶ 行列で考えた場合の イメージ 𝑎00 𝑎01 𝐴 = 𝑎10 𝑎11 𝑎20 𝑎21 14 多次元配列のアクセス 宣言と同じ書式でアクセスする A[2,1] と書いても、これ自体はコンパイル エラーとならない。なぜなら、 "2,1" はカン マ演算子の式とみなされるためである(前回 参照)。しかし、結果として、 A[1] と同じ 意味になるが、この式の評価は意図したもの ではないので(次回以降説明)結局、別の意 味でエラーとなる。ただし、この場合、エ ラーメッセージの意味がわかりづらくなる。 { int A[3][2]; A[0][0] = 1; A[2][1] = 6; A[0][0] 1 A[1][0] ? A[0][1] ? A[2][0] ? A[1][1] ? 6 A[2][1] 行列で考えた場合の イメージ 𝑎00 𝑎01 𝐴 = 𝑎10 𝑎11 𝑎20 𝑎21 15 例題: マルバツゲーム (Tic-tac-toe) 3 × 3 の盤面を配列で表現 配列の値 意味 値の意味を右の表の通りと決める 0 空欄 1 ○ { 2 × それ以外 使わない int board[3][3] = {{0}}; 全要素をゼロ int i, j; で初期化 board[0][2] = 1; // O board[1][1] = 2; // X // 盤面の表示 // ...どう書く? board[0][2] 出力 ..O .X. ... board[1][1] 16 例題: マルバツゲーム (Tic-tac-toe) // 盤面の表示 for(i = 0; i < 3; ++i) { for(j = 0; j < 3; ++j) { if(board[i][j] == 0) { printf("."); } else if(board[i][j] == 1) { printf("O"); } else if(board[i][j] == 2) { printf("X"); } ..O } .X. printf("\n"); ... 行ごとの改行 } 配列の値 意味 0 空欄 1 ○ 2 × それ以外 使わない board[0][2] 出力 board[1][1] 17 例題: マルバツゲーム / コードの工夫 // 盤面の表示 for(i = 0; i < 3; ++i) { あらかじめ変数 for(j = 0; j < 3; ++j) に入れておく { int x = board[i][j]; if(x == 0) { printf("."); } else if(x == 1) { printf("O"); } else if(x == 2) { printf("X"); } } printf("\n"); 比較の式が読 みやすくなる } このような分岐コードは、 switch -case を用いることもできる。講 義では説明しない。 配列の値 意味 0 空欄 1 ○ 2 × それ以外 使わない board[0][2] board[1][1] 18 配列の要素数とリテラル C89 では要素数は リテラルでのみ指 定可能 変数で指定すること は禁止されている GCC は独自拡張で 許容している C99 以降は可能 実用上、変数を使って困ることは少 ないので、この講義でも場合によっ ては使うことがある 要素数とループ の式を変数 n で 統一できる { int n = 10; int tri[n], i; for(i = 0; i < n; ++i) { tri[i] = i*(i+1)/2; } -pedantic オプ ションで無効化 できる 19
© Copyright 2024 ExpyDoc