目次

 

 

 

 

 

 

Top >C言語入門 とりあえずのC言語

10. 構造体

構造体
・*:..☆ プロローグ ☆..:*・

 むかしむかし、ドイツの北の方のある村に一匹のロバがいましたロバは年を取って力がなくなっていたので荷物運びの労働を辛く感じていましたそんな時にブレーメンという町で音楽隊を募集していると言う噂を聞いたので、雇ってもらおうと思いました

 ロバは、主人のところを出てブレーメンに行き、音楽隊募集事務所を訪ねました

「こんにちは。音楽隊を募集しているって聞いたのですが。」

「ああ、音楽隊を募集しているよ。正確には、“構造体ブレーメン音楽隊”だけど。」

「何ですか、その構造体って?」

「構造体っていうのは、いくつかの型を組み合わせた独自の型さ。ブレーメン音楽隊のメンバーは4人でね、それで...」

「わかりました。メンバーは4人ですね。」

「あ、ちょっと待って...」

 ロバは年を取って気が短くなっていたので、話を最後まで聞かずに飛び出して行き、村に戻ってメンバーを探しましたそして、ロバと同じように年を取ったイヌ、ネコ、オンドリが加わりましたメンバーが揃ったので再びブレーメンに行こうとしましたが、途中の森で日暮れになってしまったので、その日は森の中で寝ることにしました

 4匹が寝るところを探していると、オンドリが一軒の家を見つけましたロバが家の中をのぞくと、ドロボウたちが、おいしそうに食事をしていますそれを見てうらやましくなった4匹は、化け物のふりをしてドロボウを追い出し、食事と寝床を確保しました

 そして翌日、再びブレーメンの音楽隊募集事務所を訪ねました

「こんにちは。メンバーが揃いました。ソプラノのネコ、テノールのオンドリ、バリトンのイヌ、そしてバスのロバです。」

「うーん、困ったな。“構造体ブレーメン音楽隊”のメンバーは、ソプラノ、アルト、テノール、バスなんだよ。すまないが、型には厳密でないとね。」

 年を取って、あきらめの良くなっていた4匹は音楽隊を断念し、ドロボウを追い出した家に戻り、ずっと暮らしたということです

 構造体とは、関連のあるいくつかのデータを、一つにまとめて扱えるようにしたものです

 例えば、次のようなデータは密接な関連があると考えられます

  • 平面上の x 座標と y 座標
  • 住所録における、氏名、住所、電話番号
  • 日付を特定する、年、月、日

 

 日付を特定する場合について考えてみましょう

 日付は、年、月、日の整数3つによって、特定されるものとします1999年12月25日のようにです

 よって、日付を特定するために変数を用意する場合には、3つの変数が必要です

・年を表す整数 year
・月を表す整数 month
・日を表す整数 day

 ある関数があり、その関数は、日付のデータを引数とするものであるとしましょうその場合にも3つの引数が必要になります

関数名( 整数 year,整数 month,整数 day )

 配列についても考えてみましょう複数の日付を扱うため配列にするのであれば、整数の配列が3つ必要となります

・年を表す整数の配列 array_year
・月を表す整数の配列 array_month
・日を表す整数の配列 array_day

 このように、日付を特定するためには、常に3つの整数が必要となるのです

 

 さてここで、年、月、日の3つの整数を一組にしてみましょうそして、その組み合わせに日付という名前を付けることにします

 これは、変数名のように、実体、つまり、実際に値を持つことが出来るものに対して付けられた名前ではなく、データの組み合わせ方に対して付けた名前です

日付 { 年を表す整数 year
月を表す整数 month
日を表す整数 day

 すると、データの組み合わせ、日付を使用することにより、変数を1つにすることが出来ます

・日付 date

 関数の引数も1つに出来ます

関数名( 日付 date )

 配列の場合も同様です

・日付の配列 array_date

 このように、データの組み合わせを作ることによって、プログラムを簡潔に記述できるようになります

 そして、このデータの組み合わせ方が構造体であり、構造体は、自分で定義できるデータ型なのです

 さらに、単なる3つの整数ではなく、日付を特定するという明確な目的が与えられていることによって、プログラムが分かりやすくなるのです

 

 構造体と同様の仕組みを持つプログラミング言語は少なくありません

 また、構造体の考え方は、オブジェクト指向プログラミングのクラスにも通ずる重要なものです

10.1. 構造体の定義

構造体の定義

 構造体を使用する方法は、いくつかあるのですが、ここでは一般的と思われる方法を中心に説明します

 

 構造体は、定義することによって使用できるようになります構造体の定義とは、関連のあるデータの組み合わせを決め、それに名前を付けることです

 定義した構造体は、データ型の1つとして扱えるようになります

 

 構造体の定義は、以下のように記述します

struct 構造体タグ
{
    メンバー・リスト
};
  • 構造体の定義はstructキーワードから始まります
  • structの後に構造体タグを記述しますこれが、構造体の名前です
  • 続く波カッコ { } の中に、変数の定義を記述しますこの変数をメンバーまたは、メンバー変数と呼びます変数の定義は、変数宣言と同じように記述します
    データ型    変数名;
  • 構造体には、少なくとも1つのメンバーが必要です
  • メンバーのデータ型として、構造体を使用することも出来ます
  • 最後にセミコロン ; を記述します

 

 地球上の位置を表す、緯度と経度をメンバーに持つ構造体の例です

struct POSITION
{
    double latitude;     // 緯度
    double longitude;    // 経度
};

 

 構造体の定義は、変数宣言と同じようにブロックを形成する波カッコ { } の先頭で行えますが、通常は、関数定義の外側で行います

// 構造体の定義
struct POSITION
{
    double latitude;     // 緯度
    double longitude;    // 経度
};

// main関数の定義
main( )
{
    
    
}

 この位置に記述することによって、複数の関数で同じ構造体を使用することが出来ますソース・ファイルが複数になる場合には、ヘッダー・ファイルに記述するのが一般的です

構造体の変数

構造体の変数

 定義した構造体は、int、double 等の基本的なデータ型と同じように、変数を宣言して使用することが出来ます

 構造体の変数を宣言するには、次のように記述します

struct 構造体タグ    変数名;

 struct 構造体タグが、データ型の名前になると考えてください

 構造体 POSITION の変数宣言は、次のようになります

struct POSITION    pos;

 配列やポインタ変数の場合も、基本的なデータ型の場合と同様です

struct POSITION     aPos[12];
struct POSITION*    pPos;

 

構造体の定義を伴う変数宣言 構造体の定義を伴う変数宣言

 あまり使用される方法ではありませんが、次のように構造体の変数を宣言することも出来ます

void main( void )
{
    struct
    {
        double latitude;
        double longitude;
    } pos;
    
    
}

 このように記述すると、2つのメンバーを持った構造体の変数 pos の宣言が出来ます

 この場合には構造体タグを省略してかまいません

10.2. メンバーの参照

メンバーの参照

 プログラム中で構造体のデータを使用するためには、メンバーの参照が出来なければなりません

 構造体のメンバーを参照するには、以下のように記述します

変数名.メンバー変数名

 構造体の変数名の後に構造体メンバー演算子であるピリオド . を記述し、その後に、その構造体のメンバー変数名を記述します

 構造体 POSITION のメンバーに値を代入し、表示する例です

struct POSITION    pos;

pos.latitude = 35.65861;
pos.longitude = 139.745447;

printf( "緯度:%f、経度:%f\n", pos.latitude, pos.longitude );
  • 変数 pos のメンバーである latitude、longitude に値を代入しています
  • printf を使用し、latitude、longitude の値を表示しています両方とも double 型であるため、%fを使用します

10.3. 構造体の扱い方

構造体の扱い方

 定義した構造体は、基本的なデータ型と、ほとんど同じように扱うことができます

 構造体 POSITION を例に見てみましょう

変数の初期化

変数の初期化

 構造体の変数を初期化するには、波カッコ { } の中にメンバー変数の値を記述します記述する順番は、構造体の定義と同じにします

struct POSITION    pos = { 35.41, 139.45 };

printf( "東京→%f、%f\n", pos.latitude, pos.longitude );

 配列を初期化する場合には、波カッコ{ }を二重に使用します

struct POSITION    aPos[] ={ { 35.41, 139.45 },
                             { 34.41, 135.29 } };

printf( "東京→%f、%f\n", aPos[0].latitude, aPos[0].longitude );
printf( "大阪→%f、%f\n", aPos[1].latitude, aPos[1].longitude );

代入演算子

代入演算子

 構造体の変数に対して、算術演算子や関係演算子は使用できません演算子を適用した結果が確定できないことから、お分かりいただけると思います

 これに対し、代入演算子の=は、構造体の変数にも使用できます=演算子は、構造体の全てのメンバーをコピーします

struct POSITION    posTokyo ={ 35.41, 139.45 };
struct POSITION    pos;

pos = posTokyo;

printf( "緯度:%f、経度:%f\n", pos.latitude, pos.longitude );

関数の引数

関数の引数

 構造体を関数の引数とする例です

void PrintPosition( struct POSITION pos )
{
    printf( "緯度:%f、経度:%f\n", pos.latitude, pos.longitude );
}

void main( void )
{
    struct POSITION    pos ={ 35.41, 139.45 };

    PrintPosition( pos );
}
  • 引数が値渡しであるのは基本的なデータ型と同じで、呼び出し側の変数のコピーが渡されます

関数の戻り値

関数の戻り値

 構造体を関数の戻り値とする例です

struct POSITION GetTokyo( void )
{
    struct POSITION    pos ={ 35.41, 139.45 };

    return pos;
}

void main( void )
{
    struct POSITION pos;

    pos = GetTokyo();

    printf( "緯度:%f、経度:%f\n", pos.latitude, pos.longitude );
}

構造体の配列

構造体の配列

 基本的なデータ型と同じように角カッコ [ ] を使用します

struct POSITION    aPos[2];

aPos[0].latitude = 35.41;
aPos[0].longitude = 139.45;
aPos[1].latitude = 34.41;
aPos[1].longitude = 135.29;

printf( "東京→%f、%f\n", aPos[0].latitude, aPos[0].longitude );
printf( "大阪→%f、%f\n", aPos[1].latitude, aPos[1].longitude );

構造体をメンバーとする構造体

構造体をメンバーとする構造体

 構造体をメンバーとする構造体の例です

struct FROMTO
{
    struct POSITION    from;
    struct POSITION    to;
};

 以下のように、変数 fromto があった場合、

struct FROMTO    fromto;

 次の記述が参照するのは、位置を表す POSITION 型のメンバーです

fromto.from
fromto.to

 次の記述が参照するのは、緯度、経度を表す double 型のメンバーです

fromto.from.latitude
fromto.from.longitude
fromto.to.latitude
fromto.to.longitude

10.4. 構造体とポインタ

構造体とポインタ

 構造体の変数についても、基本的なデータ型の変数と同じようにポインタを使用することが出来ます

struct POSITION     pos;
struct POSITION*    ppos;    // 構造体のポインタ変数

ppos = &pos;    // アドレスを代入

 ポインタ変数 ppos から、メンバーを参照するには、演算子*を使用して次のように記述します

(*ppos).latitude = 35.41;
(*ppos).longitude = 139.45;

 演算子の優先度から *ppos を丸カッコ ( ) で囲まなければなりません

 

 構造体のポインタ変数からメンバーを参照するには、もう一つの方法が用意されています

 マイナス記号-と大なり記号>を組み合わせた矢印のような演算子->を使用することにより、構造体のポインタ変数から、演算子*を使用せず、メンバーを参照することが出来ます

ppos->latitude = 35.41;
ppos->longitude = 139.45;

 もちろん、どちらの方法でも結果は同じなのですが、記述が容易であることから->を使用する方が一般的です

PAPER BOWL
NEZEN