C言語基礎|現実世界と構造体

「現実の“モノ”を、そのままプログラムへ。構造体でデータ設計が自然になる!」

プログラムって、現実世界そのものを扱っているようで、実はちょっと違います。
現実の「人」「商品」「センサー」「注文」みたいな“モノ”を、そのままコードに入れられるわけじゃないんですよね。

だから私たちは、現実のモノから「必要な側面(情報)」だけを抜き出して、変数として表現します。
身長なら height、体重なら weight、という具合です。

でも、現実のモノはたいてい 複数の情報がセットです。
「人」は名前も身長も体重もあるし、「商品」なら名前・価格・在庫がセット。
この“セット感”をプログラムでも自然に表せるのが 構造体なんです。

現実世界 → プログラム世界への「投影」という考え方

ここでは「投影」という言葉を、現実のモノを、プログラムで扱える形に写し取ることとして使います。

図:1つの側面だけに注目すると、変数になる

図の説明

  • 現実の「人」そのものはプログラムに持ち込めないので
  • 「身長という側面」だけ切り取って
  • 数値として変数 height にする、というイメージです

複数の側面をまとめて投影すると「構造体」になる

身長だけでは足りなくて、体重や名前も要るなら…変数が増えて散らかりがちです。
そこで「まとまり」を作ります。

図:人を“ひとまとめ”で投影する

図の説明

  • 現実の「人」は、名前・身長・体重がセット
  • それをプログラムでもセットで扱うために
  • 1つの構造体オブジェクトにまとめる、という考え方です

構造体が「素直なプログラム」を作る理由

構造体を使うと、コードの考え方が現実に近くなります。

書き方つらさ/うれしさ
ばらばらの変数name, height, weight が別々同じ人の情報なのに関連が見えにくい
構造体でまとめるperson.name, person.height, person.weight同じ人の情報だと一目で分かる

集成体型(aggregate type)としての配列と構造体

配列と構造体はどちらも「複数のデータをまとめる型」なので、まとめて 集成体型 と呼ばれます。
でも、性格はけっこう違います。

配列と構造体の違い(超重要)

観点配列構造体
まとめる対象同じ型の集まりいろいろな型の集まりが得意
int scores[5]name(文字列), age(int), height(double)
目的同種データを効率よく現実の“モノ”を自然に表現

要素型の違い(同じ型 vs 異なる型)

配列は「同じ型」を並べる

  • 例:テスト点数、温度ログ、座標のリストなど

構造体は「異なる型」をひとまとめにするのが得意

  • 例:人、商品、センサー状態、ユーザー設定など

代入の可否:配列はダメ、構造体はOK

ここ、Cの基本ルールとして押さえておくと強いです。

配列は代入できない

int a[3] = {1,2,3};
int b[3] = {9,8,7};

a = b;   // エラー

構造体は同じ型同士なら代入できる

struct Person x, y;

x = y;   // OK(メンバがまとめてコピーされる)

なぜ違う?

代入ざっくり理由
配列できない配列名は“配列そのもの”の代入になり、言語仕様で禁止
構造体できる構造体は「1つの値」として扱えるので、メンバごとにコピーされる

サンプルプログラム

元の「人の身長・体重」ではなく、現実世界で分かりやすい センサー機器 を例にします。
表示メッセージも別の日本語に置き換えています。

何をするプログラム?

  • センサー機器の情報(名前・温度・電池残量)を構造体でまとめる
  • 別の機器へ丸ごと代入でコピーする(構造体代入ができることを体験)
  • 結果を表示する

プロジェクト名:chap12-5-1 ソースファイル名:chap12-5-1.c

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

#define NAME_LEN 16

struct Sensor {
    char   name[NAME_LEN];  // 機器名
    double temp;            // 温度
    int    battery;         // 電池残量(%)
};

int main(void)
{
    struct Sensor a;
    struct Sensor b;

    strcpy(a.name, "room");
    a.temp = 23.5;
    a.battery = 80;

    // 構造体は代入でまとめてコピーできる
    b = a;

    printf("コピーしました。\n");
    printf("機器A:%s  温度=%.1f  電池=%d%%\n", a.name, a.temp, a.battery);
    printf("機器B:%s  温度=%.1f  電池=%d%%\n", b.name, b.temp, b.battery);

    return 0;
}

登場する命令(要素)の書式と役割を解説

struct Sensor { ... };

要素書式何をする?
構造体宣言struct Tag { メンバ宣言... };「現実のモノ」を表す型(フォーマット)を作る
メンバ宣言型 メンバ名;構造体の中に入る項目を定義する

strcpy

項目書式何をする?
strcpystrcpy(コピー先, コピー元);文字列を配列へコピーする

説明
Cでは、配列(char name[ ])へ文字列を入れるときに、単純な代入ではなくコピー関数を使うことが多いです。
(実務では安全な入力方法も大事ですが、ここでは構造体の理解が主役なのでシンプルにしています)

構造体の代入(b = a;)

項目書式何をする?
構造体代入左辺 = 右辺;同じ型の構造体なら、メンバをまとめてコピーする

説明
b = a; を実行すると、a.name, a.temp, a.battery が b 側に一括コピーされます。
「現実世界の“機器カード”を丸ごと複製した」みたいな感覚ですね。

printf

項目書式何をする?
printfprintf(書式文字列, ...);画面に文字を表示する

図:構造体の投影とコピー(このプログラムの見え方)

図の説明

  • a は「機器Aの情報を投影したもの」
  • b = a で、投影された情報を丸ごと複製できる
  • 配列ではこの“丸ごとコピー”ができないので、構造体の気持ちよさが見えます

まとめ:現実のまとまりを、そのままコードへ

  • 変数は「現実の一側面」を投影したもの
  • 構造体は「現実のまとまり」を投影したもの
  • 配列は同じ型の集まり、構造体は異なる型をまとめるのが得意
  • 配列は代入できないが、構造体は同型なら代入できる。
  • だから構造体は、プログラムを素直で読みやすくしてくれます。