トップC言語入門 > 構造体

構造体

同じデータ型のものをまとめて扱うのが配列(array)であり、 異なるデータ型(例えば、char配列型の名前とint型の年齢)をまとめて扱うのが構造体(structure)である。

1.構造体の使い方

下に、構造体の使用例を示す。 最初に、構造体がどのような要素で構成されるか、その中身を定義する。 この例では構造体タグ名は _Student である。 実際に構造体を使うときには、データ型を struct _Student とする。 初期化では、配列の初期化と全く同じで、 構造体要素の値を定義順に , で区切って書き、 全体を { と } で括る。

構造体の要素の参照にはドット演算子(.)を使う。

struct03.c
#include <stdio.h>

struct _Student {	// 構造体型枠の宣言
    char name[8];
    int  age;
};

void main() {
    int n;
    struct _Student students[] = {	// 構造体(変数)の宣言
        { "Taro",   10 },		// 構造体の初期化
        { "Hanako",  7 }
    };

    for (n = 0; n < 2; n++) {
        printf("名前: %-8s 年齢: %2d\n", students[n].name, students[n].age);
    }
}
c:\MH\www\c01\structure>struct03
名前: Taro     年齢: 10
名前: Hanako   年齢:  7

下のように、typedef を使って、 構造体をひとつのデータ型として宣言しておけば、 いちいち struct を冠せずに済む。 例えば、ファイルの入出力で使われる FILE 型がその例である。

struct02.c
#include <stdio.h>

typedef struct _Student {
    char name[8];
    int  age;
} Student;

void main() {
    int n;
    Student students[] = {
        { "Taro",   10 },
        { "Hanako",  7 }
    };

    for (n = 0; n < sizeof(students)/sizeof(Student); n++) {
        printf("名前: %-8s 年齢: %2d\n", students[n].name, students[n].age);
    }
}

2.構造体要素が構造体

構造体要素が構造体である例を下に示す。 末端の要素の参照ではドット演算子(.)が二度使われる。

struct04.c
#include <stdio.h>

typedef struct _Student {
    char name[8];
    int  age;
} Student;

typedef struct _Circle {
    char circlename[20];
    Student member[10];
} Circle;

void main() {
    int n;
    Circle circle1 = {
        "ABC",
        { {"Taro", 10}, {"Hanako", 7} }
    };

    printf("サークル名: %s\n", circle1.circlename);
    for (n = 0; n < 10 && circle1.member[n].name[0] != '\0'; n++) {
        printf("メンバー: %s\n", circle1.member[n].name);
    }
}
c:\MH\www\c01\structure>struct04
サークル名: ABC
メンバー: Taro
メンバー: Hanako

3.アロー演算子(->)

C言語の構造体は Javaのクラスに当たる。ただし、要素は変数のみで、関数は持たない。 C言語が難しいのは構造体の要素の参照にはドット演算子のほかにアロー演算子(->)が使われることである。 使用例を下に示す。実行結果はstruct04と同じのため省略した。

struct04.cでは _Circle のメンバーが Student member[10]; であったのが、 struct05.cでは Student *member[10];のように、Student から Student * に変わった。 struct04.c では構造体Studentの実体がCircleに含まれていたが、 struct05.c では、Studentの実体は別の場所にあり、Circleにはその番地が含まれている。

struct05.c
#include <stdio.h>

typedef struct _Student {
    char name[8];
    int  age;
} Student;

typedef struct _Circle {
    char circlename[20];
    Student *member[10];
} Circle;

void main() {
    int n;
    Student taro = {"Taro", 10};
    Student hanako = {"Hanako", 7};
    Circle circle1 = {
        "ABC",
        { &taro, &hanako }
    };

    printf("サークル名: %s\n", circle1.circlename);
    for (n = 0; n < 10 && circle1.member[n] != NULL; n++) {
        printf("メンバー: %s\n", circle1.member[n]->name);
    }
}

両者の違いを下図に示す。内部に包含する要素の参照にドット演算子(.)が使われ、 外部にある要素を参照するときにアロー演算子(->)が使われる。 アロー演算子が繰り返されるときもあるし、この例のように、 ドット演算子とアロー演算子を組み合わせて要素を参照することもある。 C言語の難しさの代表例であるから、C言語になれないうちは、 分かりにくいと思うが、気にしなくてよい。 因みにJavaはこの難しさを回避し、ドット演算子しか使わない。 記号は "." であるが、実質はC言語のアロー演算子に相当する。