C言語入門::配列と構造体
だいぶ佳境に入ってきました.ついてきてますか?
配列
プログラムというのは,たくさんのデータを扱うことが多々あります.関数電卓で計算できるような計算しかできないならプログラムなんか書く意味がありません.
例えば,入力された10個の数の平均を整数で求めるプログラムを作ってみましょう.
int a1,a2,a3,a4,a5,a6,a7,a8,a9,a10; /* a1~a10を入力させる*/ scanf("%d",&a1); scanf("%d",&a2); scanf("%d",&a3); scanf("%d",&a4); scanf("%d",&a5); scanf("%d",&a6); scanf("%d",&a7); scanf("%d",&a8); scanf("%d",&a9); scanf("%d",&a10); printf("平均は%dです\n",(a1+a2+…(略)…+a10)/10);
という風に10個の変数を宣言して,みるのも良いでしょう.この時点で,私はやりたく無いですが.
さて,データの数が100個に増えたらどうしましょう.「a100」まで変数を宣言しますか? 努力家な人なら入力できるでしょうか.しかし,もっと多くて,100万個だったら無理かもしれません.
そこで配列が登場します.
int a[100];
という風に,宣言すると,a[0]~a[99]という変数が使えます.この「[~]」のところには,変数や式を入れることも出来るので,a[i]という風に,aという配列のi番目という指定が出来ます.
int a[100]; int i,sum; /* 配列a[]に入力 */ for (i=0;i<100;i++) scanf("%d",&a[i]); sum=0; for (i=0;i<100;i++) sum+=a[i]; printf("平均は%dです\n",sum/100);
こんな風に,100個でも簡単です.
…………この説明で納得してしまった人は,ちょっと注意力が足りません.
int i,sum,d; sum=0; for (i=0;i<100;i++) { scanf("%d",&d); sum+=d; } printf("平均は%dです\n",sum/100);
とすれば,配列は無くても同じことができますし,簡単です.
しかし,入力されたデータを大きい順に並べる場合などは,この手は使えません.
構造体
複数のデータをひとつにまとめて扱うと便利な場合があります.アドレス帳を管理するプログラム等がよく例に挙げられます.というより,ある程度のプログラムを書くためには,構造体は必須と思ったほうが良いでしょう.
例えば,以下のようなアドレス帳を管理したいとします.
名前 | 電話 | メール |
Aさん | 090-ABCDEFGH | dareka@docoka.co.jp |
Bさん | 090-jklmnopq | bsan@docoka.co.jp |
Cさん | 090-12345678 | csan@docoka.co.jp |
人が増えたときのために,配列を使うとよさそうですね?データは全部文字列で扱いましょう.文字列自体が配列なので文字列の配列ということは,配列の配列,つまり2次元配列を使う必要が出てきました.
char name[20][100]; char tel[20][100]; char mail[40][100]; /*適当にデータを入力*/ printf("名前:%s\n",name[i]); printf("電話:%s\n",tel[i]); printf("メール:%s\n",mail[i]);
name[20][100]というのは,20文字入れられる文字列が100個あるということです.まぁ,名前の文字数が20文字越える人はニックネームで登録するとして,友達は100人いれば十分です(笑).
友達がもっと増えると思う人は,適当に数字を大きくしてください,予測できないほど増える可能性がある人は,リスト構造というものを調べてください.
最後にi番目の人のデータを表示していますが,表示は色々なところで使いそうなので,出来れば関数にしたいですね.
void print_profile(char *name, char *tel, char*mail) { printf("名前:%s\n",name); printf("電話:%s\n",tel; printf("メール:%s\n",mail); }
という関数を作って,
print_profile(name[i],tel[i],mail[i]);
と呼び出すのも良いかも知れません.でも,何かが嫌ですね.何が嫌かというと,表示したいときに「[i]」というのをたくさん書かなければいけないし.例えば,住所も管理したくなったとき,print_profileを変更した後,print_profileを呼び出しているところを全部,
print_profile(name[i],tel[i],mail[i],address[i]);
という風に直さなければいけません.print_profileをプログラム中でたくさん呼び出していると面倒です.しかも,さらに生年月日,名前のふりがな,自宅の電話番号,も管理したいと思うと,関数呼び出しの部分がどんどん長くなります.
そこで,構造体を使ってすっきりさせましょう.
typedef struct{ char name[20]; char tel[20]; char mail[40]; } PERSON;
構造体はstruct{~}の部分です.データを宣言するときに,毎回structを書くのは大変なので,typedefを使って,この構造体をPERSONという名前の型として定義しています.つまり,intやlongとかと同じように PERSON型 の変数が作れるようになります.
構造体の要素(メンバと言います)を参照するには「.」を使います.
PERSON a; scanf("%s",a.name); scanf("%s",a.tel); scanf("%s",a.mail); printf("名前:%s",a.name); printf("電話:%s",a.tel); printf("メール:%s",a.mail);
構造体は,サイズが大きくなる場合があるので,ポインタを使って渡すのが普通です.ポインタは後で説明するので,ここでは適当に.
構造体のポインタの中にアクセスするには,「(*p).name」という風にします.括弧で囲ってるのは,「*」よりも「.」の優先順位が高いため,「*(p.name)」という意味になってしまうのを防ぐためです.
構造体のポインタはよく使うので,専用の演算子が用意されています.「(*p).name」は「p->name」と書いても同じ意味です.
構造体も普通に配列が作れます.
PERSON person[100];
関数呼び出しも,
void print_profile(PERSON *p) { printf("名前:%s\n",p->name); printf("電話:%s\n",p->tel; printf("メール:%s\n",p->mail); }
という関数を作って,
print_profile(&person[i]);
とすれば,かなり良い感じです.
共用体
共用体を使っている人って少ない?
共用体を作るにはunionというキーワードを使います.書き方はstructと殆ど同じです.
union { int a; int b;};
さて,共用体とは何者でしょうか?.
実は同じ共用体の中のデータはメモリ領域を共有しています.上の例では,aとbは同じメモリ領域に存在します.そのため,aを書き換えるとbも同じように変化します(逆も同様).
float型のデータの1ビット目を取り出したいときがあったとします.どうしますか?
union { float a; long b;};
としておくと,long型のbの1ビット目を取り出せばよいわけです.(longとfloatの大きさが違う環境では上手く動きません.動くように改良するにはどうすべきか考えるのも良いでしょう)
サイズ
配列や構造体のサイズはsizeofで取得できます.
残り
説明していない単語の残りは以下の通りです.
auto,const,enum,extern,register,static,volatile
普段からC言語でプログラムを書いている人でも,ここで残っているものの意味は知らないかもしれません.
ちょっとした小話
「構造体は何の役に立つのか?」とか「関数は便利なのか?」とかいう質問をされたりすると,適当な例を挙げて「ほら,便利でしょ?」とか言ったりもするのですが,「役に立つとか便利とかいう以前のもの」と言うほうが正しい雰囲気だと思います.プログラミング言語の入門書等に出てくる多くのことは,プログラムを構成する元素のようなものです.
「炭素は何の役に立つのか?」とか「鉄は便利なのか?」とは普通は聞きませんよね(小学生とかは別として).確かに,便利で役に立ちますが,便利とかとは関係なく,当たり前のように使わなければいけないものです.
この文書の履歴
- 2006-04-XX まだ未公開