成績データ などを
読み込んで処理を行なうときには、プログラムで全てのデータを
記憶しておいた方がその後の処理がしやすい。
山田太郎 50 公益太郎 90 飯森花子 91 鶴岡一人 60 酒田三吉 52 三川一二三 12
これらのデータ全てを、配列変数に格納していく手順を考えよう。
C言語の文字列は char 型(8ビット)整数の配列である
のは既に学習したとおりである。複数の文字列をまとめて記憶したい場合は、
さらに文字列を配列にすれば良い。
たとえば、最大50バイトの名前を格納するchar型配列を確保したとしよう。
char name[50];
これは一人の名前しか入れられない。これをさらに300人分にしたかったら、
name という変数を一かたまりと見て、さらにそれを
300個用意する配列にすれば良い。
char name[300][50];
こうすると、50個の箱を持つ配列が、300個できる。
name[0][*]name[0][0]name[0][1]………name[0][48]name[0][49]1文字目 2文字目 ……… 49文字目 50文字目 name[1][*]name[1][0]name[1][1]………name[1][48]name[1][49]1文字目 2文字目 ……… 49文字目 50文字目 name[2][*]name[2][0]name[2][1]………name[2][48]name[2][49]1文字目 2文字目 ……… 49文字目 50文字目 :
:name[299][*]name[299][0]name[299][1]………name[299][48]name[299][49]1文字目 2文字目 ……… 49文字目 50文字目
つまり、char name[300][50]の宣言により、
name[0][50] (50文字分) name[1][50] (50文字分) name[2][50] (50文字分) name[3][50] (50文字分) : name[297][50] (50文字分) name[298][50] (50文字分) name[299][50] (50文字分)
50文字分のchar型配列が300個用意される。
このように、配列をさらに複数あつかえるようにした 配列を二次元配列 という。さらに、二次元配列を 複数まとめることもでき、それを三次元配列という。 このように配列をさらにまとめた配列のことを 多次元配列 といい、三次元以上何次元でも積み重ねられる。
配列とそのアドレスの関係を思い出そう。
char x[10];
と宣言した場合、char 型(8bit)の入れ物が10個確保される。
最初は中味は未定義。
| x[0] | x[1] | x[2] | x[3] | x[4] | x[5] | x[6] | x[7] | x[8] | x[9] |
| ?? | ?? | ?? | ?? | ?? | ?? | ?? | ?? | ?? | ?? |
この配列の先頭のアドレスは、&x[0] となるが、
配列の先頭アドレスの場合、&と[0]を取ってしまった
x
だけで、先頭のアドレスを意味する。同様に、
char name[300][50];
と宣言し、50個分の配列が300個できた場合の、300個、それぞれの先頭の アドレスは、
name[0][50] の先頭は &name[0][0] つまり name[0] name[1][50] の先頭は &name[1][0] つまり name[1] name[2][50] の先頭は &name[2][0] つまり name[2] name[3][50] の先頭は &name[3][0] つまり name[3] : name[297][50] の先頭は &name[297][0] つまり name[297] name[298][50] の先頭は &name[298][0] つまり name[298] name[299][50] の先頭は &name[299][0] つまり name[299]
のように表現できる。
300人分のメモリを確保した上で、データを読んでは記憶して行こう。
#include <stdio.h>
int main()
{
/* 1行は100バイト、氏名は50バイトあればいいだろう */
char buffer[100], name[300][50];/* nameは 50バイト×300人分 */
int point[300]; /* 得点も300人分 */
int n=0; /* 今何人目? (0から) */
int i; /* ループ用変数 (最初に定義すべし) */
while (NULL != fgets(buffer, sizeof buffer, stdin)) {
sscanf(buffer, "%49s %d", name[n], &point[n]);
n++; /* nを1増やす */
}
puts("全部読み終わったよ!");
/* では改めて最初から表示*/
/* この時点で n は最後の配列要素より1大きくなっている */
for (i=0; i<n; i++) {
printf("%2d番目: %14s さんは %d 点\n", i+1, name[i], point[i]);
}
puts("以上");
}
for構文も復習のこと。
コンパイルして実行してみよう。
% cat score.txt | ./store
ただし、このプログラムも、sscanf を呼ぶ部分に着目すると、
while (NULL != fgets(buffer, sizeof buffer, stdin)) {
sscanf(buffer, "%49s %d", name[n], &point[n]);
n++; /* nを1増やす */
}
のように、sscanf が失敗したかどうかのチェックを怠ってい
る。これでは、bad-score.txt
のような以上データが来たときにプログラムの動作がおかしくなってしまうので、
while (NULL != fgets(buffer, sizeof buffer, stdin)) {
if (2 == sscanf(buffer, "%49s %d", name[n], &point[n])) {
n++; /* nを1増やす */
}
}
のように修正すべきである。