ログイン
kid's world

構造体

構造体

ところで,点数を大きい順に並び換えたとき,「誰の得点だかわからなくなってしまうのはよくない」と感じた人もいるだろう。これを回避するには,たとえば名前を扱う配列を別に用意しておいて「点数のデータの交換を行うときにその名前のデータも同時に交換する」とすればよいだろう。しかし,このように「複数のデータをひとまとめに扱いたいとき」は構造体というものを使うのが普通である。

構造体とは,「複数の変数を内部に含んでいる特殊な型である」と理解すれば間違いはない。すなわち,自分で型を作ってしまうのである。最終的に,点数を扱うint型と20文字入力できるchar型の配列を組み合わせた型を構造体で作り,名前も表示できるようにList 2を書き換えてみたい。

座標型

ここで,構造体の説明をするために「座標型」なるものを作ってみよう。例として2次元の座標を考えてみたい。型の名前は「position」とし,x座標用とy座標用にはint型を利用することにしよう。すなわち,2つのint型を組み合わせた構造体を作るわけである。

仮に,新しく作成したposition型の変数posを宣言したとしよう。しかしここで問題が生じる。x座標やy座標はどのようにアクセスすればよいのだろうか。int型の変数iに対して,

i=pos;

としても,もちろんうまくいかない。これではx座標を代入するのか,y座標を代入するのか,わからない。そこで,構造体を作成するときはあらかじめそれぞれの構成要素であるメンバに名前をつけ,変数名とともにその名前を指定することになっている。たとえば,x座標には「x」,y座標には「y」という名前をつけたとしよう。すると,x座標を代入したければ,

i=pos.x;

というように,変数名の後ろにドット「.」を書いて,そのあとにメンバ名を書くことによってメンバにアクセスできるようになるのだ。

もちろん,代入も,

pos.y=100;

というようにできるし,変数pのy座標のメンバへのポインタは「&pos.y」で表現することができる。このドットを構造体メンバ演算子という。単純にドット演算子ということもある。

では,実際にposition型を作成してみよう。

構造体を作成(定義)するときには,structという予約語を使う。基本的な構造体の定義の形式を次に示す。

struct 構造体名{
  メンバ1
  メンバ2
  ...
};

ここでは構造体名は「position」で置き換える。そして内部には「メンバ1」「メンバ2」という箇所があるが,ここには変数の宣言と同じような書式で書く。たとえば,ここではint型のxとyをメンバとしたいのだから,「int x;」「int y;」と書く。したがって,position型を作るには,

struct position{
  int x;
  int y;
};

と書けばよい。

これを書く位置は基本的にはグローバル変数を宣言する位置と同じで,インクルード文の直後である。そうすれば,すべての関数からこの型を利用できるようになる。

position構造体の宣言

それでは,position型の変数を宣言して,実際に利用してみよう。構造体は定義しただけでは使えない。ようするに型を作っただけであるので,変数を宣言しなければならないのである。int型という型だけあってもだめで,変数iを宣言しなければ使えないのと同じだ。

構造体の変数の宣言は普通の型の宣言と方法が少し違い,「struct」が構造体名の前に必要となる。よくわからなければ,「struct position」という型を作ったと覚えてしまってもいいだろう。

つまり,posという名前の変数を宣言したければ,

struct position pos;

と書くのだ。これで,position型のpos変数を宣言することができる。

構造体の変数ができること

構造体に対して利用できる演算子は,代入演算子「=」,アドレス演算子「&」,構造体メンバ演算子「.」だけである。比較や足し算などはできない。

代入ができることは実はすばらしいことだ。position型の変数pos1とpos2があったとき,

pos1=pos2;

と代入するということは,

pos1.x=pos2.x;
pos1.y=pos2.y;

とすることと同じなのだ。非常にスッキリしている。メンバが多ければ多いほど,その恩恵にあずかることができるだろう。

このことをよく考えてみれば,関数を使って一度に構造体のメンバを書き換えることができると気づくだろう。

たとえば,

struct position point(int x, int y)
{
  struct position p;
  p.x=x;
  p.y=y;
  return p;
}

という関数を作れば,

pos=point(10,20);

という非常にわかりやすい記法でxとyメンバを書き換えることができるのだ。

ここまでの内容をList 3にまとめた(実行結果はFig. 10)。なお,show_pointという関数は,引数に与えられた座標に「o」を表示する関数だ。ただしx座標は右に,y座標は下に向かって大きくなるような座標系となっている。

List 3: 構造体で座標型を作る

#include <stdio.h>

struct position{
  int x;
  int y;
};

void show_point(struct position p);
struct position point(int x, int y);

void show_point(struct position p)
{
  int i;
  for(i=0; i<p.y; i++)
    printf("\n");
  for(i=0; i<p.x; i++)
    printf(" ");
  printf("o\n");
}

struct position point(int x, int y)
{
  struct position p;
  p.x=x;
  p.y=y;
  return p;
}

void main()
{
  struct position pos;
  
  pos=point(20,5);
  show_point(pos);
}

Fig. 9: List 3の実行結果

/c/algorithm-fig9.png

main関数ではposition型の変数posを宣言し,point関数で(20, 5)という座標を書き込んでいる。そしてそれをshow_point関数で画面に表示している。

それでは,List 2を改良して「入力されたデータを点数の高い順に並び換え,名前とともに表示するプログラム」を作成してみよう。成績を扱う型の名前は「result」とし,点数を表すメンバを「score」,名前を表すメンバを「name」としよう。

構造体が定義できればあとは簡単だ。つまり,int型の配列をresult型の配列にしてしまうだけである。このようにして改良した結果をList 4に,実行例をFig. 11に示しておく。

List 4: 成績順に名前とともに表示する

#include <stdio.h>

struct result{
  int score;
  char name[20];
};

void input_data(struct result data[]);
void show_all(struct result data[]);

void input_data(struct result data[])
{
  /* データ入力を行う(省略) */
}

void show_all(struct result data[])
{
 /* 配列の内容を表示する */

  int i;
  for(i=0; i<10; i++)
    printf("%d : %s\n", data[i].score, data[i].name);
}

void main()
{
  int i, j, m;
  struct result t, a[10];
  
  input_data(a);
  
  printf("整列前:\n");
  show_all(a);
  
  for(i=0; i<9; i++){
    for(j=i+1,m=i; j<10; j++){
      if(a[m].score < a[j].score)
        m=j;
    }
    
    t=a[i];
    a[i]=a[m];
    a[m]=t;
  }
  
  printf("整列後:\n");
  show_all(a);
}

Fig.10: List 4の実行結果

/c/algorithm-fig10.png

おわりに

今回はアルゴリズムとは何なのかについて解説した。アルゴリズムの例として,最大値を求めるアルゴリズム,ソートを行うアルゴリズムを解説した。また,複数の型を組み合わせて作る構造体についても解説した。

ここで解説したアルゴリズムは基本中の基本であり,もっと高速なアルゴリズムや,もっと複雑な問題を解くアルゴリズムが山のようにある。たとえば,データの圧縮や暗号化もアルゴリズムがあってこそできることである。

効率のよいアルゴリズムをC言語で実装するときに,構造体を使うことがある。今回は単なるデータの組み合わせにしか使わなかったが,メンバとして構造体へのポインタを持つと,柔軟なリスト構造を作ることができるようになる。詳しくはほかの文献を参考にしていただきたい。

筆者のお勧めの文献として,『アルゴリズムC 第1〜3巻』(R.セジウィック原著,近代科学社)と,『定本 Cプログラマのためのアルゴリズムとデータ構造』(近藤嘉雪著,ソフトバンク パブリッシング)を紹介しておこう。ぜひ読んでいただきたい。

「C言語学習帳」は2002年4月号から連載を続けてきたが,ついに次回で最終回である。これまでの全11回の内容の総まとめをしたいと考えている。


コンピュータとC言語の基礎」へ進む

広告


©Toshio Koide 1996-2007.

目次

リンクについて

リンクは御自由にどうぞ。

メール

mail.gif

広告