複数の変数をまとめることができる構造体を説明します。

構造体

構造体を使うことで、複数の変数をまとめて扱うことが可能です。

例えば、ひとりの身長と体重のデータを変数として保存しようとすると、身長と体重それぞれの数字を保存するための変数を用意する必要があり、後からソースコードを見返した時に、身長と体重の変数の関連が、分かりにくいことがあります。

そこで、構造体を使って身長と体重がひとまとまりのデータであることを、分かりやすく表現できます。

#include <stdio.h>

struct human {
    double height;
    double weight;
    double bmi;
};

int main(void)
{
    struct human h;

    printf("身長を入力(単位 m): ");
    scanf("%lf", &h.height);
    printf("体重を入力(単位 kg): ");
    scanf("%lf", &h.weight);

    h.bmi = h.weight / (h.height * h.height);

    printf("BMI: %f\n", h.bmi);

    return 0;
}

実行すると、入力された身長と体重から、BMIを求めます。

BMIとは、肥満度を表す指標で、「体重 ÷ (身長 x 身長)」で求められます。

構造体は、最初にどのような変数から成り立っているのか定義する必要があります。

定義は基本的にmain関数の外で行い、ここでは、このようにしています。

struct human {
    double height;
    double weight;
    double bmi;
};

struct という文字から書き始めます。struct の後には、空白でその構造体の名前を書きます。ここでは、human という名前の構造体を定義しています。

この構造体は、中かっこの中を見ると「double height;」「double weight;」「bmi」の3つの変数から成り立っているということが分かります。これらの構造体を構成する要素をメンバやメンバ変数と呼びます。

height は身長、weight は体重、bmi はBMIを表しています。

このように、構造体を構成する変数は中かっこの中に書きます。

中かっこの最後には、; をつけるのですが、忘れやすいので、気をつけてください。

構造体を定義したら、使って見ましょう。

構造体は変数と同じように、データを入れておくための入れ物を用意しておく必要があります。

その入れ物は、「struct human h;」で用意しています。

「struct human」は、構造体を表す struct とその構造体の名前である human を空白で区切って書いたものです。その後に、今までの変数の時と同じように、構造体の変数の名前を書きます。

今回は、h という名前にしました。

構造体の height や weight に値を代入するには、構造体の変数の後にドット(.)をつけて構造体をメンバ変数の名前を書きます。

なので、身長の入力は「scanf("%lf", &h.height);」の「h.height」のように、構造体の変数の後にドットをつけてメンバ変数を書いています。

体重の入力も同じです。

入力が終わったら、BMI の計算をします。

BMIの計算と代入は、「h.bmi = h.weight / (h.height * h.height);」で行なっています。

入力の時と同じで、構造体の変数の中のデータを参照するには、構造体の変数の後にドットをつけてメンバ変数を書きます。

計算したものは、イコールで h.bmi に代入しています。

「printf(“BMI: %f”, h.bmi);」も同じで、h.bmi として、計算したBMIを表示しています。

このように、構造体は複数の変数をまとめて扱うことができます。

構造体と関数

関数の引数に構造体を使うことができます。

#include <stdio.h>

struct human {
    double height;
    double weight;
    double bmi;
}

double calc_bmi(struct human h);

int main(void)
{
    struct human h;

    printf("身長を入力: ");
    scanf("%lf", &h.height);
    printf("体重を入力: ");
    scanf("%lf", &h.weight);

    h.bmi = calc_bmi(h);

    printf("BMI: %f\n", h.bmi);

    return 0;
}

double calc_bmi(struct human h) {
    return h.weight / (h.height * h.height);
}

ここでは、calc_bmi関数という関数を定義して、その関数でBMIを計算するようにしました。

このように、引数に構造体を渡すことができます。

構造体にもポインタがあります。

引数に構造体のポインタを使うように書き換えます。

#include <stdio.h>

struct human {
    double height;
    double weight;
    double bmi;
}

double calc_bmi(struct human *h);

int main(void)
{
    struct human h;

    printf("身長を入力: ");
    scanf("%lf", &h.height);
    printf("体重を入力: ");
    scanf("%lf", &h.weight);

    calc_bmi(&h);

    printf("BMI: %f\n", h.bmi);

    return 0;
}

void calc_bmi(struct human h) {
    h->bmi = h->weight / (h->height * h->height);
}

構造体の変数で、値を参照したい場合は、構造体の変数の後にドットをつけてそのメンバ変数を指定すれば良いのですが、構造体のポインタでは、ドットを使うことができず、ドットの代わりに->を使います。

これで、構造体のポインタからメンバにアクセスすることが可能です。

ちなみに、構造体のポインタには構造体の変数のアドレスが代入されているということなので、どうしてもドットを使いたい場合は、「(*h).height」のように、* を使って元の構造体の変数を参照して使うこともできます。(*h)とかっこが付いているのは、* よりもドットの方が先に計算されるためかっこをつけないと「*(h.height)」のようになり、コンパイル時にエラーとなるからです。

関数で構造体のポインタを使うのには、理由があります。

関数の引数にポインタを使わない場合、関数の呼び出し時に引数として渡した値はコピーされて関数内で使われます。

そのコピーが変数のひとつやふたつであれば良いのですが、たくさんの引数があったり、構造体のような中に複数の変数を持っているようなものであれば、コピーに時間がかかりプログラムの実行が遅くなります。

そのような場合、ポインタが使われます。

ポインタは、値のコピーは行われず、その値のアドレスが渡されます。そのため、構造体であれば、構造体の中身のひとつひとつのデータをコピーしていたものが、アドレスというひとつの情報のみが渡されるため、プログラムの実行時間が短くなります。

しかし、このような場合、変数の値を変更すると、呼び出し元の引数となっている変数の値も変わるため、全てをポインタで渡せばいいというわけではありません。

注意するべき事をわかった上で、実行時間を早くしたい場合や関数内で呼び出し元の引数の変数の値を変えたい場合にポインタを使うようにしましょう。

構造体と配列

構造体も配列を使えます。

使えるとは、メンバとして使えるということと、構造体自体を配列として使えるという意味です。

ここでは、構造体自体を配列として使うことを例とします。

3人の身長と体重とBMIを扱うのであれば、要素数3の構造体の配列を使った方が分かりやすいです。

#include <stdio.h>

struct human {
    double height;
    double weight;
    double bmi;
};

void calc_bmi(struct human *h);

int main(void)
{
    struct human h[3];
    int i;

    h[0].height = 1.653;
    h[0].weight = 55.5;
    h[1].height = 1.521;
    h[1].weight = 46.8;
    h[2].height = 1.999;
    h[2].weight = 69.0;

    for (i = 0; i < 3; i++) {
        calc_bmi(&h[i]);
    }

    for (i = 0; i < 3; i++) {
        printf("%d人目のBMI: %f\n", i + 1, h[i].bmi);
    }

    return 0;
}

void calc_bmi(struct human *h) {
    h->bmi = h->weight / (h->height * h->height);
}

実行すると、あらかじめ決めておいた3人の身長と体重からBMIを求めて、表示します。

構造体の配列ですが、他の配列と同じで、添字を使ってひとつひとつの要素にアクセスできます。

まとめ

構造体で、C言語の入門に関する内容は終わりです。

しかし、C言語にはまだまだ説明していないことがあります。

ここから先は応用として、さらにC言語の詳しい内容を説明していきます。