C言語入門 第15回 構造体




2014年10月より個人の方を対象に、Study C無料提供を開始しました。
C言語を勉強中の方は、学習・教育に最適なC言語インタープリタのStudy Cを使ってみてください(個人の方は無料です)。
大学・高専・高校などの教育機関での採用実績も多数あるロングセラー商品Study Cが、個人向けに無料提供を始めました。
インタープリタの手軽さに加え、ゲームや3Dタートルグラフィックで楽しく勉強したりと、C言語の学習を強力にサポートします。
ブロック崩しゲーム 3Dツリー クリスマスツリー
また、このようなボタンの用意されているページでは、掲載しているプログラムをStudy Cに直接ロードし実行したりすることができます。
Study Cにロードする Study Cにロードし編集する Study Cにロードし実行する
Study C無料利用についての詳細は、このページを参照してください。



1.構造体の使い方

構造体の基礎について解説します。 まず、次の住所などの個人情報を取り扱うプログラムの一部をご覧ください。 個人情報は、住所や氏名などの情報から構成されています。 今までに出てきた文法では、これらを別々の文字型配列にして格納することになると思います。

char    zip[9];         /*郵便番号*/
char    addr[100];      /*住所*/
char    tel[15];        /*電話番号*/
char    fax[15];        /*FAX番号*/
char    company[50];    /*会社名*/
char    name[50];       /*氏名*/

main()
{
        strcpy(zip, "123-4567");
        strcpy(addr, "東京都千代田区霞が関");
        strcpy(tel, "03-1111-1111");
        ...
}

構造体はこれらの変数(配列は普通の変数でもかまいません)をひとまとまりにする機能です。
また、構造体定義は二つの部分に分かれます。 一つは構造体がどのような変数(これをメンバ変数と呼びます)から構成されているかを定義します。 この定義の方法は、簡単でstruct タグ名 { ... };で囲みます。 囲んだ中には普通の変数定義のように記述することができます。
次に定義した構造体の変数を、struct タグ名 変数名;のような形式で宣言します。 "struct タグ名"の部分がintやcharに相当します。 構造体の定義に引き続いて変数を宣言することもできます。

タグ名tag_nameで構造体を定義

struct tag_name {
        int     a;
        char    b[10];
        int     c,d;
};


タグ名tag_nameの構造体でvar1変数を宣言
struct tag_name
        var1;


タグ名tag_nameで構造体を定義しvar1、var2変数を同時に宣言
struct tag_name {
        int     a;
        char    b[10];
        int     c,d;
} var1, var2;


タグ名tag_nameの構造体でvar3変数を宣言
struct tag_name
        var3;

次に宣言した構造体型変数の使用方法ですが、var1.aのように"変数名.メンバ変数"という形式で使用します。 var1 = var2; といった使い方をするとvar2の各メンバ変数の内容全てがvar1に代入されます。 大きなサイズの配列をメンバ変数に持つような場合は、代入によって多くのメモリ転送が発生してしまいます。 でサイズを考えずに頻繁に代入するような使い方は、なるべく避けるようにしましょう(今回はふれませんが構造体型のポインタ変数を使うことでこれを避けることができます)。

最初のプログラムを構造体で書き換えたものが次のプログラムです。

struct personal_info {
        char    zip[9];         /*郵便番号*/
        char    addr[100];      /*住所*/
        char    tel[15];        /*電話番号*/
        char    fax[15];        /*FAX番号*/
        char    company[50];    /*会社名*/
        char    name[50];       /*氏名*/
} pinfo;

main()
{
        strcpy(pinfo.zip, "123-4567");
        strcpy(pinfo.addr, "東京都千代田区霞が関");
        strcpy(pinfo.tel, "03-1111-1111");
        ...
}


2.構造体の使用例

構造体への書き換え自体はそれほど難しい作業ではないと思います。 次に最初のプログラムを、100人分のデータが取り扱えるように配列に変更してみます。 構造体を使わない場合は次のようになります。

char    zip[100][9];         /*郵便番号*/
char    addr[100][100];      /*住所*/
char    tel[100][15];        /*電話番号*/
char    fax[100][15];        /*FAX番号*/
char    company[100][50];    /*会社名*/
char    name[100][50];       /*氏名*/

main()
{
        strcpy(zip[0], "123-4567");
        strcpy(addr[0], "東京都千代田区霞が関");
        strcpy(tel[0], "03-1111-1111");
        ...
}

これを構造体で書き換えます。構造体型の変数も次のように、そのまま配列にすることができます。

struct personal_info {
	char    zip[9];         /*郵便番号*/
        char    addr[100];      /*住所*/
        char    tel[15];        /*電話番号*/
        char    fax[15];        /*FAX番号*/
        char    company[50];    /*会社名*/
        char    name[50];       /*氏名*/
} pinfo[100];

main()
{
        strcpy(pinfo.zip[0], "123-4567");
        strcpy(pinfo.addr[0], "東京都千代田区霞が関");
        strcpy(pinfo.tel[0], "03-1111-1111");
        ...
}

次に、前のプログラムのある人の情報を書き換えるプログラムを考えてみましょう。 書き換えは途中でやめてしまうかもしれないので別の変数に一時的に入力情報を蓄えておく必要があります。 inp_...という変数を作成することにします。

char    zip[100][9];         /*郵便番号*/
char    addr[100][100];      /*住所*/
char    tel[100][15];        /*電話番号*/
char    fax[100][15];        /*FAX番号*/
char    company[100][50];    /*会社名*/
char    name[100][50];       /*氏名*/

main()
{
        char    inp_zip[9];
        char    inp_addr[100];
        char    inp_tel[15];
        char    inp_fax[15];
        char    inp_company[50];
        char    inp_name[50];

        inp_zipの入力
        inp_addrの入力
        ...

        zip、addr、tel、...の更新
        strcpy(zip, inp_zip);
        strcpy(addr, inp_addr);
        strcpy(tel, inp_tel);
}

このプログラムも構造体を使用して書き換えます。だいぶすっきりしたプログラムになることが理解いただけるでしょうか。

struct personal_info {
        char    zip[9];         /*郵便番号*/
        char    addr[100];      /*住所*/
        char    tel[15];        /*電話番号*/
        char    fax[15];        /*FAX番号*/
        char    company[50];    /*会社名*/
        char    name[50];       /*氏名*/
} pinfo[100];

main()
{
        struct personal_info
                inp_data;

        inp_data.zipの入力
        inp_data.addrの入力
        ...

        zip、addr、tel、...の更新
        pinfo = inp_data;
}

最後に個人情報を仕事関係とそれ以外に分けて管理することを想定してプログラムを書き換えてみます。 かなり冗長なプログラムに見えると思います。

char    job_zip[100][9];        /*郵便番号*/
char    job_addr[100][100];     /*住所*/
char    job_tel[100][15];       /*電話番号*/
char    job_fax[100][15];       /*FAX番号*/
char    job_company[100][50];   /*会社名*/
char    job_name[100][50];      /*氏名*/

char    other_zip[100][9];      /*郵便番号*/
char    other_addr[100][100];   /*住所*/
char    other_tel[100][15];     /*電話番号*/
char    other_fax[100][15];     /*FAX番号*/
char    other_company[100][50]; /*会社名*/
char    other_name[100][50];    /*氏名*/

main()
{
        char    inp_zip[9];
        char    inp_addr[100];
        char    inp_tel[15];
        char    inp_fax[15];
        char    inp_company[50];
        char    inp_name[50];

        inp_zipの入力
        inp_addrの入力
        ...

        zip、addr、tel、...の更新
        strcpy(job_zip, inp_zip);
        strcpy(job_addr, inp_addr);
        strcpy(job_tel, inp_tel);
        ...
        または
        strcpy(job_zip, inp_zip);
        strcpy(job_addr, inp_addr);
        strcpy(job_tel, inp_tel);
        ...
}

これも構造体を使用すると次のようなすっきりしたプログラムになります。

struct personal_info {
        char    zip[9];         /*郵便番号*/
        char    addr[100];      /*住所*/
        char    tel[15];        /*電話番号*/
        char    fax[15];        /*FAX番号*/
        char    company[50];    /*会社名*/
        char    name[50];       /*氏名*/
} job_pinfo[100], other_pinfo[100];

main()
{
        struct personal_info
                inp_data;

        inp_data.zipの入力
        inp_data.addrの入力
        ...

        zip、addr、tel、...の更新
        job_pinfo = inp_data;
        または
        other_pinfo = inp_data;
}

構造体は、ちょっと規模の大きなプログラムでは必ずといってよい程使用されます。 構造体の使用によってプログラムはすっきりとした構造になります。 入門書などのサンプルプログラムでは、構造体の説明の章以外であまり使われないかもしれません。 しかし、有用な機能なのでよく覚えておいてください。