C言語

構造体の基本

これまで、1つの変数に1つの値しか入れられませんでした。
たとえば、int型の変数には整数を、char型の変数には文字を入れます。
しかし、プログラムの中では「いくつかの情報をひとまとめにして扱いたい」ことがあります。
たとえば、「生徒の情報」なら次のようなデータをまとめたいですよね。

  • 名前(文字列)
  • 年齢(整数)
  • 成績(小数)

このように、異なる種類のデータを1つにまとめる仕組みを「構造体(struct)」といいます。
また、同じ場所を複数の変数で共有して使う仕組みを「共用体(union)」といいます。

今回は、この2つの仕組みについて、初めての人でもわかるように解説します。

構造体とは

構造体は、異なる種類のデータをまとめて1つのかたまりにしたいときに使います。
たとえば、生徒の情報(名前・年齢・成績)をまとめて扱いたいときに便利です。

構造体の定義の構文は次の通りです。

struct 構造体型名 {
  型名1 メンバー名1;
  型名2 メンバー名2;
  型名3 メンバー名3;
};

構造体はブロックの中に変数などをまとめて記述したものです。
例えば、名前・年齢・成績の生徒の情報を管理するための構造体型 struct Student の定義は、次のようになります。

struct Student {
  char name[20];  // 名前
  int age;        // 年齢
  float score;    // 成績
};

ここでは、構造体に、char型の配列のメンバーname[20]と、int型の変数 age と、float型の変数 score を定義しています。

構造体型のブロック内に定義した変数名は、構造体のメンバーと呼びます。
あくまで「構造体型の中にどんな項目を持つか」を決めているだけで、まだ、変数そのものではないからです。

構造体変数を定義する

構造体型を定義したら、次は、その構造体型を使って、変数を定義します。
構造体変数の定義の構文は次の通りです。

構造体型名 構造体変数名;

先ほど定義した構造体型を、変数 student1 として定義した場合は、次の通りです。

struct Student student1;

定義したときに、student1 の中にある
nameagescore のためのメモリが実際に確保され、使えるようになります。

構造体変数の使い方

構造体変数を定義したら、その中のメンバーにアクセスします。
メンバーに値を入れたり取り出したりするときは、ドット( . )を使うというルールがあります。

student1.age = 15;        // 年齢に代入
student1.score = 92.5;    // 成績に代入
printf("%d\n", student1.age);  // 年齢を表示

このように、ドット( .を使うことで代入や参照することができます。
次に実際にプログラミングしてみましょう。

#include <stdio.h>

struct Student {
  char name[20];
  int age;
  float score;
};

int main(void)
{
  struct Student student1;

  // 値を代入
  student1.age = 15;
  student1.score = 92.5;

  printf("年齢:%d\n", student1.age);
  printf("成績:%.1f\n", student1.score);

  return 0;
}

※文字列(配列)のメンバーには、= で代入することはできません。
 このため、別の方法で代入する必要がありますが、そのあたりは、まだ解説できていないので、別記事で紹介します。

構造体の初期化

変数は、宣言と同時に初期化することができました。
構造体も同様に宣言と同時に初期化することができます。
構造体の初期化の構文は次の通りです。

構造体型名 構造体変数名 = { 初期値1, 初期値2, ... };

先ほどの、構造体型 struct Student を初期化する場合は、次のようになります。

struct Student student1 = { "Yamada", 15, 92.5 };

この場合は、
 name:"Yamada"
 age:15
 score:92.5
が順にセットされます。
※構造体型の定義で記述されているメンバーの順に初期値を並べる必要があります。

一部だけ初期化する場合

次のように、一部だけ初期化することもできます。
初期値を省略したメンバーには、0が代入されます。

struct Student student1 = { "Yamada" };

この場合は、
 name:"Yamada"
 age:0
 score:0.0
になります。

次のように、途中の初期値だけを入れるようなことはできません。

struct Student student1 = { , 15, };     // × ageだけ初期値を入れることはできない
struct Student student1 = { , , 92.5 };  // × scoreだけ初期値をいれることはできない

C99仕様では、メンバー名を指定して初期化する書き方もあるため、初期化したいメンバーだけ初期値を書くことができますが、このやり方については、中級編、または、上級編で紹介します。

構造体同士の代入

特にIoTでは、実務で使うことがあまりないのですが、構造体同士の代入もできます。

構造体同士を次のように 1行でコピー できます。

struct Student a = { "Yamada", 15, 92.5 };
struct Student b;

b = a;   // a の名前・年齢・成績が b に丸ごとコピーされる

このように、構造体変数同士を代入するだけで、全てのメンバーの値がコピーされます。

typedefで名前を割り当てる

構造体型は、struct Student という長い型名となっています。
構造体変数を定義する際、この長い型名を使って定義しなければならないのは不便です。
そこで、構造体型の型名を短縮するために、typedef というキーワードを使う方法が使われています。
typedef の構文は次の通りです。

typedef 元の型 新しい型名;

実際に typedef を使って構造体型の型名を短縮してみましょう。

typedef struct Student {
    char name[20];
    int age;
    float score;
} Student;

今までの構造体型の定義の最初に、typedef と付けます。
その後に、struct { … } が「元の型」になります。
構造体型の定義の終わりのブロックの後の Student が新しい型名になります。
このように構造体型を定義すると、次のように新しい型名で構造体型の変数が定義できます。

Student s1;

この Student は、構造体の別名として機能しています。
struct 〜 を使わなくても変数を作れるようになるため、コードが読みやすく、短くなるメリットがあります。
一般的には、typedef を付けて別名を定義しています。

まとめ

  • 構造体は、異なる種類のデータをひとまとめにして扱うための仕組み
  • {} の中に書く nameagescore などは、構造体の「メンバー」と呼ばれる
  • 構造体型を定義しただけではメモリは作られず、構造体変数を作ったときに実体ができる
  • メンバーにアクセスするときは ドット(.) を使う
    例:student1.age
  • 構造体は { } を使って初期化できる
  • 初期値はメンバーの順番どおりに書く
  • 一部だけ初期化すると、残りは 0 で初期化される
  • {} を使う初期化では、途中のメンバーだけ初期化することはできない
  • C99 以降は メンバー名を指定して初期化することもできる
  • 構造体同士は 1行で丸ごと代入できる(s2 = s1;
  • typedef で別名を定義することで、structを付けなくても構造体型の変数を定義することができる

-C言語