苦しんで覚えるC言語 16章 複数の型をまとめる P326 – P335

まとめてデータを扱いたい場合

変数は一つのデータしか扱えません。
配列は変数の集合体なので、指定数だけデータを扱えますが、型やデータ種類を分けて管理することはできません。

そこで、型やデータの種類を好きなように組み合わせて一元管理する媒体があります。
この媒体を構造体と呼びます。
例えば、参考書の例を元に以下のコードで解説をします。

struct student{
    int year;       /* 学年 */
    int clas;       /* クラス */
    int number;     /* 出席番号 */
    char name[64];  /* 名前 */
    double stature; /* 身長 */
    double weight;  /* 体重 */
};

構造体を宣言するのは、変数と同じ位置になります。
つまり、ファイルの先頭部(=上部)や関数の先頭になります。
後は、今後でてきますがヘッダーファイル内での宣言をすることもあります(説明は後述します)

構造体は、struct を記載することから始まります。
structは、構造体の型になりますので、変数でいうint型、short型などと同じです。

structの後にタグ名を記載します。
これは、用意した構造体の名前です。
上記のコードでは、studentがタグ名です。

{ } 内の変数は、構造体に紐づけしたい変数になります。
この変数を構造体変数とかメンバ変数と呼びます。
上記では、以下が構造体変数です。

  int year; /* 学年 */
int clas; /* クラス */
int number; /* 出席番号 */
char name[64]; /* 名前 */
double stature; /* 身長 */
double weight; /* 体重 */

見ると理解できるのですが、個人の身体データと学校での在籍データが変数になっています。
つまり、studentという名前は、学校の生徒を連想します。
構造体の各変数は、生徒の個人データになります。

この状態では構造体をプログラム上で使用することはできません。
上記のコードは構造体の型として、テンプレートを用意した状態になります。
このテンプレートを実体として宣言する必要があります。
つまりどういうことか・・・。以下を見ながら解説します。

struct student{
    int year;       /* 学年 */
    int clas;       /* クラス */
    int number;     /* 出席番号 */
    char name[64];  /* 名前 */
    double stature; /* 身長 */
    double weight;  /* 体重 */
};

int main(void)
{
    struct student data;

    return 0;
}

構造体のテンプレートは、ファイルの上部に記載します。
main関数内で、このテンプレートを実際の実体として、
struct student data; と書いて宣言をしています。

テンプレートとして構造体を記載する時も struct と書きましたが、
実体を宣言する時も struct から書きます。

テンプレートで用意されているまま、
構造体型 + 構造体名を同じ様に struct studentとして書いて、
続いて、実態となる構造体名を書きます。

上記のコードでは data と書いていますが、ここは変数名と同じく、
処理に関連のある名前を付けていただいてよいです。
例えば、struct student gakusei; でもいいでしょう。

整理しますと、struct student data;は、
dataという名前で、studentという構造体名の構造体を用意。

このdataという構造体には、以下の変数が包括されています。

 int year; /* 学年 */
int clas; /* クラス */
int number; /* 出席番号 */
char name[64]; /* 名前 */
double stature; /* 身長 */
double weight; /* 体重 */

変数が包括されているとはどういうことか?
以下で解説をします。

構造体の使い方

dataという構造体には、テンプレートで用意されている構造体変数が存在します。
存在する変数にアクセスして、値を代入したり値を取り出したりすることができます。
どの様にしてアクセスするのかは、以下になります。

#include <stdio.h>

struct student{
    int year;       /* 学年 */
    int clas;       /* クラス */
    int number;     /* 出席番号 */
    char name[64];  /* 名前 */
    double stature; /* 身長 */
    double weight;  /* 体重 */
};

int main(void)
{
    struct student data;

    data.year = 10;   /* 構造体の変数であるyear要素にアクセス */

    printf("%d\n", data.year);

    return 0;
}

構造体のテンプレートを用意し、main関数内の先頭でdataという名前で、構造体の実体を宣言します。

dataという構造体には変数が包括されていますので、包括された各変数にアクセスすることが可能です。
ここでは、int yearにアクセスしているのですが、書き方にルールがあります。

 data.year

構造体の宣言した変数名 . 要素名 と書きます。

data.year = 10; は通常の変数と同じ様に10を代入しています。
これと同じ様に以下の要素にアクセスする書き方となります。

 data.clas = 1; /* 1組という意味で 1 を代入 */
data.number = 20; /* 出席番号20番 */
data.name = “MARIO”; /* MARIO という名前 */
data.stature = 170.5; /* 170.5cmの身長 */
data.weight = 65.5; /* 65.5kgの体重 */

この様な感じで、要素の前に . を付けて各要素にアクセスします。

「変数が包括する」という表現は、struct student data; とした時点で、
dataという構造体変数名には、テンプレートがコピーされた状態を意味しています。

その為、別の構造体変数名でいくつもコピーを作ることが可能です。

 struct student data;
struct student seito;
struct student gakusei;

全てstruct studentのテンプレートをコピーした構造体になります。
data、seito、gakuseiはそれぞれ別々の構造体として用意されたことになります。

構造体変数自体の処理

ここまでの内容は、通常の変数と同じ使い方に見えますし、構造体にする意味がどこにあるのか、あまりわからないかもしれません。
メリットの一つとして、以下の様な使い方ができます。

#include <stdio.h>
#include <string.h>

struct student{
    int year;       /* 学年 */
    int clas;       /* クラス */
    int number;     /* 出席番号 */
    char name[64];  /* 名前 */
    double stature; /* 身長 */
    double weight;  /* 体重 */
};

int main(void)
{
    struct student data1,data2;

    /* data1へ代入 */
    data1.year = 3;
    data1.clas = 4;
    data1.number = 18;
    strcpy(data1.name, "MARIO");
    data1.stature = 168.2;
    data1.weight = 72.4;

    data2 = data1;  /* data1の内容をdata2へ丸っとコピー */

  以下参考書のコードは割愛・・・

    return 0;
}

上記は参考書のコードになります。
途中で割愛していますが、ここでの要点は、data2 = data1; として、
構造体を構造体へコピーすることができます。

通常の変数の場合、変数の数だけコピー処理を書かなければなりませんが、
構造体にしてまとめていると、構造体変数名を代入するだけで中身の要素がコピーされます。

但し、同じテンプレートの構造体同士のみコピーができることに注意が必要です。
もう一つ、構造体を構造体へコピーできるものの、比較はできません。

if(data2 == data1)

この様な処理は成立しないことに注意が必要です。

構造体の簡潔な宣言

テンプレートの書き方はこれまでの書き方で問題ないのですが、一般的には以下の様に省略版を書くことが多いです。

typedef struct{
    int year;       /* 学年 */
    int clas;       /* クラス */
    int number;     /* 出席番号 */
    char name[64];  /* 名前 */
    double stature; /* 身長 */
    double weight;  /* 体重 */ 
} student;

構造体の型である struct の前に、typedef と書くことで、これから書くテンプレートは簡潔に宣言することになりますよ~。という宣言になります。

そして、structの後の構造体名 student は、} の後に来ています。
この様に書くことで、以下の様に簡潔に宣言が書けるようになります。

#include <stdio.h>

typedef struct{
    int year;       /* 学年 */
    int clas;       /* クラス */
    int number;     /* 出席番号 */
    char name[64];  /* 名前 */
    double stature; /* 身長 */
    double weight;  /* 体重 */
}student;

int main(void)
{
    student data;   /* 実体宣言時に struct が要らなくなる */

    data.year = 10;

    printf("%d\n", data.year);

    return 0;
}

実態の宣言時に、構造体名 + 構造体変数名 で宣言することができます。
これまでの書き方ですと、struct を毎回宣言時に書いていましたが、structが不要になります。