PowerPoint

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