MMGames  C言語入門  C言語開発環境  今すぐC言語  便利アプリ  お問い合わせ
     
 MMGames
     

バージョンを自動判別する

SpineViewer

見た目でズバリ分かる

応答速度チェッカー

パソコンつけっぱなしでできる

マウス掃除タイム

しんで覚えるC言語
しんで覚えるC言語

異なる型の変数をまとめる

まとめてデータを扱いたい場合
第13章では、同じ型の複数の変数をまとめて扱う配列を説明しました。
しかし、場合によっては、異なる型もまとめて扱いたい場合があります。
また、各要素を番号で区別するよりも、名前で区別した方が便利なこともあります。

例えば、学校で生徒の身体測定の結果を保存しておきたい場合には、
各生徒につき、クラス、学年、出席番号、名前、身長、体重、などのデータが必要です。
これを、素直に変数で用意した場合、次のようになります。

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


clas?
clas は class が正しいスペルです。
ですが、classという名前は使用できないため、あえて clas にしています。

なぜなら、ほとんどのC言語コンパイラはC++にも対応していますが、
C++には クラスという機能があり、class はその機能のために使用されています。
つまり、C++では class は予約語となっているため、名前には使用できないのです。
clas が気に入らないようでしたら group にすることもできます。

身長と体重は、小数第1位まで表現することが多いので、実数型にしています。

当然、これでも、生徒のデータとして十分機能します。
しかし、これらのデータは、全て関連したデータであるにもかかわらず、
1つ1つが別の変数として宣言されているため、あまりわかりやすくありません。

この様な場合に、複数の異なる型の変数を1つにまとめて取り扱う方法として、
構造体という機能が用意されています。

キーワード
【構造体】

複数の異なる型をまとめて作られた型のこと。


構造体では、複数の型をまとめた新しい型を作り出すことが出来ます。
例えば、生徒のデータを新しい型の構造体として作るには、次のようにします。

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

構造体の型を宣言する時には、始めにstruct(ストラクト)をつけます。
その次には、変数と同じルールの元、新しく宣言する構造体の型名をつけます。
この型名を特に、構造体タグ名と呼ぶことがあります。

キーワード
【構造体タグ名】

作成した構造体それ自体の名前。
型名ではないため、扱い方はすこし異なります。


今回は、生徒と言う意味の、studentという名前をつけています。
後は、{}で囲んだ中に、まとめたい変数の型と名前を宣言していきます。

この様にして、新しい構造体の型を作ることが出来ますが、
これだけでは、単に型を宣言しただけなので、実際に使うことは出来ません。
実際に使うためには、構造体の型の変数を宣言する必要があります。
構造体の型の変数を宣言するには、次のようにします。

構造体の型の変数を宣言
struct student data;

この例では、student構造体タグの、data構造体変数を宣言しています。
構造体タグの変数を宣言する場合、始めにstructをつけます。
次に、構造体の型名、最後に、構造体変数の名前を指定します。
今後、構造体の型を構造体タグ、構造体タグで宣言された変数を構造体変数と呼びます。

C++では
C言語の拡張版であるC++では
structをつけなくても構造体変数を宣言出来ます。
現在のコンパイラはほとんどがC++用なので、
structをつけなくても動作してしまうことがおおいです。

この様にして、構造体タグと構造体変数を宣言することが出来ます。
次のプログラムは、構造体タグと構造体変数を宣言する例です。

構造体タグと構造体変数を宣言
struct student
{
    int year;       /* 学年 */
    int clas;       /* クラス */
    int number;     /* 出席番号 */
    char name[64];  /* 名前 */
    double stature; /* 身長 */
    double weight;  /* 体重 */
};

int main(void)
{
    struct student data;
    return 0;
}

この例のように、構造体タグは、関数よりも先に宣言するのが普通です。
何故なら、そうすれば、後に登場する全ての関数でこの構造体が使えるからです。
構造体の使い方
前項では、構造体タグと構造体変数の宣言について説明しました。
せっかく宣言した構造体なのですから、さっそく使ってみることにしましょう!

構造体変数は、元となった構造体タグで宣言されていた全ての型を持っています。
配列の時には、同じ型の変数を番号によって区別していましたが、
構造体変数では、型に関係なく、全ての要素を名前によって区別します。
構造体変数の持つ1つの要素にアクセスするには、次のようにします。

構造体変数の持つ1つの要素にアクセス
構造体変数名.要素名

ここで、. は小数点記号を使用します。カンマではありません。
ただし、小数点記号を流用しているだけであって、少数そのものとは全く関係ありません。
ポインタといい、C言語って無関係の記号を流用する悪い癖があるよね・・・

この様にして、個々の要素にアクセスすることが出来ます。
次のプログラムは、先ほどの構造体の、year要素を使う例です。

ソースコード
#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;
}

このプログラムの実行結果は次の通りになります。

実行結果
10

この例では、student構造体タグのdata構造体変数のyear要素を使っています。
この様にしてアクセスした場合、もはや普通の変数と全く同じです。
また、同様にして、配列にアクセスすることも出来ます。
次のプログラムは、先ほどの構造体の、name要素にアクセスする例です。

ソースコード
#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 data;

    strcpy(data.name, "MARIO");
    printf("%s\n", data.name);

    return 0;
}

このプログラムの実行結果は次の通りになります。

実行結果
MARIO

もちろん、[]をつけて、配列の各要素にアクセスすることも可能です。
構造体変数自体の処理
前項では、構造体の各要素にアクセスする方法を説明しましたが、
これでは、見た目には確かにまとまっているようにも見えるものの、
実際の使い方は、普通の変数と全く同じで、あまり意味がないようにも思えます。

しかし、構造体の場合、構造体変数自体を変数として取り扱うことが出来ます。
例えば、構造体変数同士で、代入を行うことが可能となっています。
次のプログラムは、構造体変数同士で代入を行う例です。

ソースコード
#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へコピー */

    /* data1とdata2の内容を表示 */
    printf("data1.year = %d : data2.year = %d\n", data1.year, data2.year);
    printf("data1.clas = %d : data2.clas = %d\n", data1.clas, data2.clas);
    printf("data1.number = %d : data2.number = %d\n", data1.number, data2.number);
    printf("data1.name = %s : data2.name = %s\n", data1.name, data2.name);
    printf("data1.stature = %f : data2.stature = %f\n", data1.stature, data2.stature);
    printf("data1.weight = %f : data2.weight = %f\n", data1.weight, data2.weight);

    return 0;
}

このプログラムの実行結果は次の通りになります。

実行結果
data1.year = 3 : data2.year = 3
data1.clas = 4 : data2.clas = 4
data1.number = 18 : data2.number = 18
data1.name = MARIO : data2.year = MARIO
data1.stature = 168.200000 : data2.stature = 168.200000
data1.weight = 72.400000 : data2.weight = 72.400000

このプログラムでは、まず、data1の各要素に代入を行っています。
そして、data2にdata1を代入しています。
その結果を表示してみると、data1とdata2の中身は同じになっています。

この様に、構造体変数では、全要素を一括して代入することが出来ます。
後で説明することですが、他にも、関数の引数として利用したりなど、
構造体変数はそれ自体を1つの変数として使うことが出来、
1つ1つ代入しなければならなかった配列よりも便利です。

構造体変数の比較
構造体変数は、それ自体を1つの変数として使えると説明しましたが、
残念ながら、構造体変数同士での演算や比較は行えません。つまり、

struct student data1,data2;
/* data1とdata2に代入 */
if (data1 == data2) {
/* 何かの処理 */
}

のようなプログラムは書けません。
加算や減算はともかく、同一比較はできてもいいと思うんだけどなあ・・・


今までは、構造体タグを宣言してから構造体を使用していました。
この場合、構造体を使う時は、必ずstructが必要になります。
しかし、構造体タグを、新しい型として1度に宣言してしまう方法があります。

C言語では、新しい型を宣言するtypedef(タイプデフ)が用意されています。
詳しい説明は後でしますが、次のようにtypedefを使うと新しい型を宣言出来ます。

ソースコード
typedef 新しい型の形 新しい型名

これを利用すると、構造体タグを直接新しい型に出来ます。
次のプログラムは、構造体タグを元に新しい型を作る例です。

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

typedef struct student_tag student;

この例では、student_tagタグを、student型にすることが出来ます。
こうすれば、構造体変数を宣言する時に、structが不要になります。
今後、先ほどの方法で宣言された型を、構造体型と呼ぶことにします。

構造体型にすることで、型と同様に扱えるため、便利なのですが、
毎回 typedef を使って型を定義するのは面倒です。
より簡単にするために、構造体タグと構造体型を同時に宣言してしまうことが出来ます。

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

更に、この場合には、構造体タグを省略することができます。

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

この方法が、1番簡潔に構造体型を宣言することが出来ます。
現在では上記の形がほぼ慣用句となっておりますので、この形で覚えてしまっていいと思います。


本サイトについて

苦しんで覚えるC言語(苦C)は
C言語入門サイトの決定版です。
C言語の基本機能を体系立てて解説しており、
市販書籍と同等以上の完成度です。

第0部:プログラム概要編
  1. プログラムとは何か?
2章:プログラムの書き方
  1. 書き方のルール
  2. 書き方の慣習
  3. 練習問題2
3章:画面への表示
  1. 文字列の表示
  2. 改行文字
  3. 練習問題3
6章:キーボードからの入力
  1. 入力用の関数
  2. 入力の恐怖
  3. 練習問題6
9章:回数が決まっている繰り返し
  1. 繰り返しを行う文
  2. ループ動作の仕組み
  3. 練習問題9
10章:回数がわからない繰り返し
  1. 回数不明ループ
  2. 入力チェック
  3. 練習問題10
13章:複数の変数を一括して扱う
  1. 複数の変数をまとめて扱う
  2. 配列の使い方
  3. 練習問題13
20章:複数のソースファイル
  1. 最小限の分割
  2. 分割の定石
  3. 練習問題20

💬 コメント投稿欄を開く