Cによるプログラミング入門12
配列と構造体
今回は、関連するデータをまとめておく方法についてです。
入門6で配列というものを紹介しました。これは、「同じ種類のもの」を番号をつけてまとめておくのに便利です。
入門6で考えた配列は、添字が1つだけのものでしたが、添字を複数つけることもできます。このようなものを一般に、多次元配列などといいます。たとえば、
int a[3][2];
と書けば、3x2の配列ができます。これは、
a[0][0] a[0][1]
a[1][0] a[1][1]
a[2][0] a[2][1]
という要素ができたということです。このような配列(上の例では、添字が2つなので、2次元配列などといいます)は
int a[3][2] = {{1, 2}, {3, 4}, {5, 6}};
などとして初期化することができます。
/* hairetu.c */
#include <stdio.h>
int main(void)
{
int a[3][2] = {{1, 2}, {3, 4}, {5, 6}};
int i, j;
for(i = 0; i < 3; i++){
for(j = 0; j < 2; j++){
printf("a[%d][%d]
= %d ", i, j, a[i][j]);
}
}
return 0;
}
Fig.1 Hairetu.exe
実は、初期化では、中の中カッコをはずして、
int a[3][2] = {1, 2, 3, 4, 5, 6};
とすることもできます。
また、Cでは改行を自由に使ってよいことを利用して
int a[3][2] = {{1, 2},
{3, 4},
{5, 6}};
などと書く人もいます。まあ、いずれにしても、「わかりやすく書く」ということが大事だと思います。
また、for文では、処理が1行なら中カッコをはずしてもよいという話をしました。hairetu.cの2重ループは、
for(i = 0; i < 3; i++)
for(j = 0; j < 2; j++)
printf("a[%d][%d] = %d ", i, j, a[i][j]);
と書くこともできるのです。外側のfor文に対して、内側のfor文は「1行」と考えられるからです。このような書き方は、好む人、嫌う人、いろいろです。
多次元配列は、配列の配列と考えることができます。たとえば、上の例の場合、
a[0]、a[1]、a[2]
が、それぞれ(1次元の)配列を表していると考えられるのです。つまり、
a[0]の要素:a[0][0]、a[0][1]
a[1]の要素:a[1][0]、a[1][1]
a[2]の要素:a[2][0]、a[2][1]
ということです。よく考えてみると、合理的ですね。
配列は、「同じ種類のもの」をまとめておくのに便利ですが、「違う種類のもの」をまとめておくには、構造体というものが便利です。これは、ちょっとわかりにくいものですが、例を見て考えるようにしてください。
たとえば、Manという構造体を、次のように、定義し、使うことができます。
struct Man
{
int num;
char name[51];
};
これは、numというint型変数とnameというchar配列を持った「もの」の「設計図」です。この構造体を使うには、次のようにします。
struct Man m;
これで、Manという構造体(「設計図」)の「実体」ができ、それがmで表されることになるのです。このとき、mの中には、numとnameがあり、それぞれm.num、m.nameと表されます。たとえば、あまり意味はありませんが、次のようなプログラムが書けます。
/* kouzoutai.c */
#include <stdio.h>
struct Man
{
int num;
char name[51];
};
int main(void)
{
struct Man m;
printf("Manの実体mを作りました。\n");
printf("mに番号をあげます。\n");
printf("好きな数字を入力してください。\n");
scanf("%d", &m.num);
printf("今度は、mに名前をあげます。\n");
printf("好きな名前を入力してください。\n");
scanf("%s", m.name);
printf("ありがとう。\n");
printf("mのnumは%d、mのnameは%sになりました。",
m.num, m.name);
return 0;
}
Fig.2 kouzoutai.exe
構造体は、関係のあるデータを一まとめにしておくのに使えるのです。たとえば、上のManは、「社員番号と名前」をまとめる構造体のつもりです。
ところで、「struct Man m;」のように、毎回structを書くのが面倒と思えることもあります。Manを別のところで使う予定がないなら、Manの定義と同時に、その変数を定義することもできます。
struct Man
{
int num;
char name[51];
}
m;
これで、構造体Manが定義され、と同時に、その変数mも定義されるのです。
しかし、Manをいろいろなところで使いたい場合、やはり、そのつど「struct Man m;」のように書かなければなりません。その場合、typedefというものを使って、簡単にすることもできます。
typedef struct Man SMan;
と書くと、SManが「struct Man」を表すようになるのです。これで、「struct Man」とあればそれは「SMan」のことである...という意味になるのです。これは名前の問題なので、Manの定義そのものには関係ありません。そのため、Manの定義の前に書いてもかまいません。こうすると、以後では、
struct Man m;
などのかわりに、
SMan m;
などと書くことができるのです。つまり、structを書かなくてよくなっただけ、すこーし、楽になるのです。
実は、また、
typedef struct Man SMan;
struct Man
{
int num;
char name[51];
};
と同じ意味のことを、一度に、
typedef struct Man
{
int num;
char name[51];
} SMan;
と書くこともできます。ちょっとややこしいですが、これで、「numとnameを持つ構造体Manが定義され、同時にSManがManの別名」になるのです。しかし、こう書くと、もうManが必要なくなりますね。というのは、あとはいつでも「SMan
m;」のように書けばよくなるからです。
もう、Manという名前を使わないなら、上のコードからManを削除してもかまいません。つまり、上のコードを
typedef struct
{
int num;
char name[51];
} SMan;
と書いてもよいようになっているのです。ちょっとサンプルを書いてみましょう。
/* kouzoutai2.c */
#include <stdio.h>
typedef struct
{
int num;
char name[51];
} SMan;
int main(void)
{
SMan m;
printf("Manの実体mを作りました。\n");
printf("mに番号をあげます。\n");
printf("好きな数字を入力してください。\n");
scanf("%d", &m.num);
printf("今度は、mに名前をあげます。\n");
printf("好きな名前を入力してください。\n");
scanf("%s", m.name);
printf("ありがとう。\n");
printf("mのnumは%d、mのnameは%sになりました。",
m.num, m.name);
return 0;
}
これは、kouzoutai.cと同じ動作をします。
たとえば、構造体の配列を作れば、名簿プログラムが作れそうですね。実際の名簿プログラムは次回考えることにして、ここでは、簡単な例をお見せしましょう。
/* kouzoutai3.c */
#include <stdio.h>
typedef struct
{
int num;
char name[51];
} SMan;
int main(void)
{
int i;
SMan m[5];
printf("社員5人分のデータを入力してください。\n");
for(i = 0; i < 5; i++){
printf("社員番号:");
scanf("%d", &m[i].num);
printf("氏名 :");
scanf("%s", m[i].name);
}
printf("全員のデータを出力します。\n");
for(i = 0; i < 5; i++){
printf("%d:%s\n", m[i].num,
m[i].name);
}
return 0;
}
Fig.3 kouzoutai3.c
構造体とその変数について、基本的な書き方は上で紹介したものです。しかし、初期化などで「ちょっと楽に書く方法」はいろいろあります。これは、そういう例に出くわしたときに、覚えるようにしてください。