こんにちは､ ようこそ｡
この講義では､ 配列の概念についてお話したいと思います｡
配列とは､ 基本的に1つの変数に多くの異なる値を格納することができる概念である｡
配列は､ ほとんどのプログラミング言語で一般的に使われています｡
これらは､ プログラムを簡素化し､ より効率的にするために役立ちます｡
そこで､ 今回はc言語での配列の作成と使用についてお話します｡
つまり配列ですね｡
プログラムでは､ 指定された型のデータ値を多数格納することはよくあることです｡
例えばスポーツのプログラムでは､ 全試合のスコアや各選手のスコアを保存したい場合があります｡
ここまでは､ 簡単に実装できたのですが､ スコアごとに違う変数を書かなければなりません｡
だから､ ゲームが大量にある場合を想像してください｡
野球は162試合あるじゃないですか｡
もし､ 野球のプログラムで､ 統計情報などを保存したいプログラムを書くとしたら､
スコアを保存するために､ 試合ごとに162個の変数を作らなければならないでしょう｡
今は､ それが面倒なんです｡
非常に面倒なことになりますね｡
と､ 本当に余計なお世話なんですが｡
そこで､ この問題を解決してくれるのが､
配列です｡
162個の変数を作成する必要はありません｡
一つの変数を作り､ ある方法でアクセスすることができます｡
つまり配列は､ 値を一つの名前でグループ化することができるのです｡
データの項目ごとに変数を分ける必要はありません｡
だから､ より効率的なプログラマーになることができるようになるのです｡
配列は､ すべて同じ型のデータを固定数で並べたものです｡
つまり､ 配列の唯一の問題は､ 配列のサイズを変更できないことです｡
一度アレイを作れば､ 常にそのサイズになります｡
そして､ もうひとつの制約は､ 複数の種類のデータを保存することができないことです｡
配列を宣言しても､ 数値やintを格納するのであれば､ intを格納しなければならない｡
だから､ いくつかの制限がある｡
配列はどのように宣言するのですか｡
変数を宣言するとき､ あなたは何をしているのか覚えていますか？
この変数がプログラム中に存在するということですね｡
そして､ これらのプリミティブなデータ型を宣言するときに､ メモリフォームを割り当てるのです｡
配列を宣言するとき､ 配列内のデータ項目を「要素」と呼びます｡
これも配列を考えるときのコンセプトなんですね｡
もし私が要素という言葉を使うなら､ それは配列の中の項目を指していることを意味します｡
なぜなら､ 配列は同じ名前の異なる値をたくさん持っていることを思い出してください｡
また､ 前述のとおり配列の要素は同じ型でなければならず､
データ型を混在させることはできません｡
intが5個､ longが5個の配列はありえない｡
複数のデータ型の配列は1つもありません｡
プログラムで配列を使用することを宣言するのは､ 通常の変数宣言と同様です｡
名前を指定して､ 括弧でくくるだけです｡
だから､ この名前の後に角括弧をつけるという考え方が唯一の違いです｡
これは普通の変数宣言のように見えますが､
ここに括弧があるのがわかります｡
それこそ普通じゃないし､ 中に数字が入っている｡
括弧内の数値はサイズ指定です｡
つまり､ ここでは､ numbers という名前の long
型の配列を作成し､ 10 個の要素を含むようにするということです｡
だから､ とてもわかりやすいんです｡
これは､ 最後にセミコロンがあるので､ 変数の宣言と同じですが､
括弧と数字があり､ これは配列のサイズまたは配列の次元を表します｡
配列を宣言したら､ 次はそれを使ってみましょう｡
その中のデータにアクセスしたり､ データを書き込んだりするようになるのです｡
そのためには､ 配列がどのようにメモリに格納され､ 実際にどのようにアクセスされるかを理解する必要があります｡
つまり､ 配列の各要素､ 各データ項目は､ 同じ名前を使ってアクセスされるのです｡
だから､ やはり変数名でアクセスすることになる｡
しかし､ そのレイの中の特定の値が欲しい場合は､ 今度はインデックスまたはサブスクリプトと呼ばれるものを使う必要があります｡
これらの用語は両方とも同じ意味で使われます｡
このインデックスや添え字の値は数値になり､ 配列の要素にアクセスしたり､
配列のその場所に値を書き込んだりするために､ 同様に大括弧の中に入ります｡
つまり､ インデックス値は0から始まる連続した整数である｡
つまり､ 配列とその要素について話すとき､ 配列のインデックスや添え字について話すとき､
それらの添え字はすべて0から始めることができます｡
これは基本的に､ 彼らの光線は0ベースだと言っているのです｡
つまり､ サイズ10の配列の要素のインデックス値は､ 0から9までとなります｡
0は配列の最初の要素を表し､ 9はサイズ10の配列の最後の要素を参照します｡
だから､ この0ベース配列の概念を理解する必要があるのです｡
直感的には､ 配列の最初の場所にアクセスしたいので､ インデックス1を指定すればいいと思うかもしれませんが､
配列は0から始まるのでダメです｡
そのため､ 配列にアクセスしようとすると､ 配列が1から始まっていると思い込んでしまい､
1だけずれてしまうということがよくあります｡
これをoff by 1エラーといいます｡
単純な整数を使用して､ アクセスしたい要素を明示的に参照することができます｡
例えば､ 配列の4番目の値にアクセスしたい場合は､
配列名を言ってから､ 括弧の中で3というようにします｡
つまり､ 配列名sub-3は0ベースなので､ 4番目の値にアクセスすることになります｡
つまり､ アクセスしたい要素の番号を用意すればいいわけです｡
そして､ もう一度､ 0ベースであることを忘れないでください｡
つまり､ 4番目の要素にアクセスするためには､ 3なんです｡
8番目の要素にアクセスしたい場合は､ インデックス7を行います｡
また､ 配列の要素に対して､ 式を用いてインデックスを指定することもできます｡
だから､ 今は数字だけでいいんです｡
名前の後に角括弧で囲んだ式も同様に使用できます｡
しかし､ この式は､ 可能なインデックス値の1つに対応する整数値にならなければならない｡
つまり､ サイズが10の配列で式を使用する場合､ その式は0か､
0から9のいずれかの数値に評価されなければなりません｡
配列に要素を格納する際にも､ ループを使ってアクセスするのが一般的ですが､
その例も挙げておきます｡
例えば､ ここではforループで､ ループ制御変数を0 Iに初期化し､ Iが10より小さくなったら終了しています｡
ここでやっているのは､ 配列の中の数字を表示することだけです｡
そして､ printf number is percent dと言いながら､
number sub-Iと言いながら､ Iは変数です｡
変数式では､ その変数が0から9までの数を表す限り､ 変数を使用することができ､
この場合､ その変数が使用されます｡
配列の要素にアクセスしたり､ その要素にデータを書き込んだりするために､
forループを使って反復処理することは非常に一般的です｡
そこで､ まとめとして､ ちょっといいイラストを見つけましたのでご紹介します｡
配列はこのような構造になっています｡
numbersという配列があったとして､ 配列の名前の後に､
配列の個々の要素を識別するための括弧が続くことになります｡
だから､ 型と名前を指定して､ 括弧の中でサイズを指定すれば､
配列を宣言できるんだ｡
そして､ アクセスするときは､ 配列名の後にインデックス値を付けるだけです｡
つまり､ サイズが10の配列では､
0から9まで有効なインデックスを持つことになるわけです｡
では､ 無効なインデックスで配列の要素にアクセスしようとした場合､
例えばサイズ10の配列に11をインデックスとしてアクセスしようとしたり､ 10をインデックスとした場合はどうなるのでしょう？
配列のout of boundsエラーになりますよ｡
では､ 配列の範囲外のインデックス値に式や変数､
定数を使用した場合､ 範囲外の配列は何を意味するのでしょうか？
つまり､ 負の数か､ サイズより大きい数､ つまり0ベースなのでサイズマイナス1より大きい数ということになりますね｡
つまり､ サイズ10の配列があったとして､ サイズから1を引いた値が9より大きいと､ array
out of boundsエラーになるわけです｡
さらに悪いことに､ プログラムが正常に動作しているように見えても､
実際には正常に動作していないことがあります｡
だから､ 安定した結果が出ないのです｡
時にはゴミのようなデータ､ つまり配列の中で何の意味も持たないデータもあるでしょう｡
繰り返しになりますが､ array of boundsエラーが発生すると､ 非常に予測不能な状態になります｡
そして問題は､ コンパイラが配列の境界外エラーをチェックできないことです｡
コンパイラは､ より大きなものを代入しているのか､ サイズより大きなものにアクセスしているのか､
わからないようになっています｡
このエリアアウトオブバウンズエラーが出ても､ コンパイルはできるんです｡
本来､ array of boundsエラーはランタイムエラーです｡
このエラーは､ プログラムを実行してみないとわからない｡
そしてまた､ プログラムが非常に予測不可能になる可能性があります｡
ですから､ 配列にアクセスする方法､ 使用するインデックス値､ インデックス値を使って配列にデータを書き込む方法について､
非常によく認識する必要があります｡
配列のインデックスと境界線に細心の注意を払ってください｡
配列にデータを書き込む場合､ それはとてもとても簡単なことです｡
基本的にインデックスを使うだけでいいということですね｡
つまり､ 等号の左側に配列要素を指定し､ 括弧で書き込む要素を指定するだけで､
配列要素に値を格納することができるのです｡
この例では､ gradeという名前の配列がありますが､
その大きさはわかりません｡
しかし､ この例では､ グレードサブ100は95に相当すると言っているのです｡
成績配列の要素番号100に値95が格納され､
非常にシンプルです｡
インデックスを指定するだけで､ あとは好きな値を符号化することができます｡
また､ 変数を使用して配列に値を割り当てることができます｡
つまり95は､ 他の変数を代入するのと同じように､ 変数に格納することができるのです｡
では､ 配列を使う例を見てみましょう｡
ここでは､ 配列を使用して､ 10個の数値を読み込んで､
その平均を求める例を示します｡
読み込んでいる間に､ 合計を計算し､
平均を表示します｡
そこで､ この例をコードブロックにまとめ､ 実際に動かしてみて､
どのように動作するかをお見せしたいと思います｡
ここでは､ 通常のビューで､ 簡単なプロジェクトで､ コードブロックの中で配列がどのように機能するかをお見せします｡
メイン関数の中にいることにお気づきでしょう｡
プログラムの入り口となる｡
そして12行目では､ 配列を宣言しています｡
サイズ10のint型の配列を宣言します｡
サイズは括弧の中にあるので､ セミコロンで終わらせています｡
これは､ 10個の値を格納する配列です｡
もしこの配列が保存されていなかったら､
学年1がint､ 学年2がint､ 学年3がintと言わなければなりません｡
それがいかに問題であり､ 非常に面倒なことかおわかりいただけると思います｡
10個の項目を格納する変数を1つ作るだけで､ それらがグループ化されるのはとてもいいことです｡
配列に項目を格納するとき､ データの値がすべて異なるものにならないようにします｡
この例のように､ 成績を保存しているので､ すべて成績です｡
配列に何か名前をつけて､ 「成績を格納する」「......を格納する」なんてことはしないようにしましょう｡
我が 我が 年齢がどうのこうのとか､ なんでもいいんです｡
配列の場合は､ 同じ種類のデータを保存するようにしましょう｡
13行目の次の変数は､ count変数です｡
10に相当します｡
配列を調べているので､ 読み込む値の数になります｡
Sumはlongで､ 値を見ながら数値を合計していきます｡
平均はfloatで､ 数値の平均を求めます｡
まず17行目で､ ユーザーに10個の成績を入力するように指示し､
それから値を読み込むことにします｡
20行目では､ インデントを行い､ 適切なネーミングでインデントし､
コードのブロックを上手に使ってインデントしています｡
20行目では､ ループ制御用の変数を宣言しています｡
cコンパイラ89という古いコンパイラを使っているので､ 宣言と初期化を同じ行で行うことができないのです｡
普通なら､ こうすればいいんでしょう｡
しかし､ C89コンパイラを動かしているので､ それを実行するとエラーが発生する｡
そこで､ 20行目で変数を宣言し､ forループを使用しています｡
以前の講義でおなじみのforループを使えば､ コードを繰り返すことができます｡
ここでは､ カウンタ制御のforループで､ 0から始めて10までずっと行くということを言っています｡
つまり､ 10回反復するわけですが､ インデックスが0であることに気づきます｡
この変数Iを使って配列にアクセスするのですから､
これは重要なことです｡
そして､ forループの最後で､ ループ制御変数をインクリメントして変更していることにお気づきでしょう｡
そこで､ 配列の中でIに1を足した値を出力して､
数値の位置を示すことにします｡
つまり､ 基本的にはグレード1グレード2グレード3グレード4を入力するように表示されることになります｡
そして､ scanfを使ってグレードを入力するように言われます｡
それを配列に格納するのです｡
配列に格納するときは､ 通常の変数アクセスと同じであることがわかると思います｡
グレードを使うだけで､ インデックスを指定する｡
ここでは､ Iをインデックスとして使用しています｡
つまり､ 最初の反復は0､ 2回目の反復は1､ 3回目の反復は2に等しくなります｡
配列は0ベースなので0から始めて､ 演算子のアドレス､
アンパサンドを使うだけで良いのです｡
通っているうちに､ 成績の合計が出るようになるんです｡
つまり､ サムイコール､ サム＋グレードサブIということになりますね｡
各グレードは基本的に合計を計算することになります｡
そして､ 10回実行した後にループを抜けると､ 合計をカウントで割った値を平均値として設定し､
それをfloatにキャストします（ここでキャストしています）｡
これはデータ型を変えているのです｡
sumをcountで割ると､ sumはlong､ countはintとなり､ これをfloatにキャストすることを思い出してください｡
そして最後の最後に､ 平均値をプリントアウトするのです｡
では､ さっそく実行してみて､ エラーがないことを確認しましょう｡
では､ 次に実行してみましょう｡
そうすれば､ 私が述べたとおりのことができるようになります｡
10段階の成績を入力すると､ 小さなマークで「1」が表示されるので､ 何番目の成績なのかがわかります｡
95 32 55のように､ ここに任意の数字を入れるだけで､
入ります｡
必ずしも意味があるわけではない､ それでもいい､ そして最後の23番｡
その10段階の成績の平均は51になるそうです｡ 20.
でも､ 10個も変数を作らなくて済むので､ 本当に助かります｡
添え字を使って配列にアクセスし､ 添え字を使って配列に値を代入することができるのです｡
これは､ 実はscanfをするときに代入しているのです｡
その添え字にデータを割り当てているのです｡
ここで､ これを変更して､ 1から始めて､ 10を数える代わりに､
実際に同じに設定するとします｡
だから実は1には変えないでおこう｡
例えば､ 0から始めるとして､ 今度はインデックス10まで｡
配列のサイズが10なのでインデックス10は範囲外ですが､ インデックス値は0ベースなので､
最大インデックスは9であることを忘れないでください｡
また､ コンパイラは境界外エラーを検出しないため､
コンパイルは可能ですが､ 問題は実行するときです｡
さて､ プログラムを実行すると､ 11年生で入学しようとすると､
大きな問題が発生します｡
だから､ 私たちはすべてうまくいく｡
10等級を入力すると､ 11等級になったときに問題が発生するんです｡
これで11個になったので､ データを保存してみることにします｡
何も悪いことは起きていないが､ 我々の平均は正しくはないだろう｡
そして､ アウトオブバウンズエラーが発生したときの挙動が非常に予測しにくいのです｡
ですから､ アイレッスンの回数を確認し､ 情報にアクセスする際も､
範囲を超えないようにしたいものです｡
そして､ これが配列の基本で､ 配列の宣言､
データ型だけの指定､ そしてサイズと括弧の指定､ 配列へのアクセスや配列へのデータの格納､
値の代入､ インデックスの中の添え字Iの使用､
です｡
そして､ 同様に添え字を使ってインデックスをプリントアウトすることもできるのです｡
配列は結構使うことになる｡
物事を単純化するために､ 配列を使った例をたくさん用意するつもりです｡
ただ､ 配列は固定サイズであることを忘れないでください｡
成績の大きさは変えられない｡
プログラムを走らせているうちに必ず10になるんです｡
だから､ それ以上のデータを割り当てても､ 膨らまないんです｡
また､ 成績は同じデータ型でなければならないことも覚えておいてください｡
グレードに文字列や単語を格納することはできず､ int型しか格納できないんです｡
フロートを格納することはできず､ intsのみを格納することができるんだ｡
ありがとうございます｡