C言語基礎|typedef で構造体を使いやすくする

「struct が長い…を今日で卒業!typedef で構造体を“気持ちよく”書けるCへ。」

C言語の構造体って便利なんですが、使っているとちょっとした不満が出てきます。
そう、宣言のたびに struct 〜 と書くのが地味に長い…!

たとえば struct student のように、型名が2単語になるのが気になるんですよね。
そこで活躍するのが typedef。既存の型に「別名(型名として振る舞う名前)」を付けられるので、構造体も 1単語の型名 でスッキリ書けるようになります。

この記事では、typedef で構造体を読みやすくする基本を、表と図でしっかり整理していきます。
あわせて、入力でよく使う scanf の注意点(& が要る/要らない)もセットで理解します。

typedef って何?(まずは役割を確認)

書式(typedef 宣言)

typedef 既存の型 新しい型名;

何をする命令?

  • 既存の型に対して、別名(新しい型名)を付けます。
  • 以後、その別名を 型名として使えるようになります。

typedef のイメージ

beforeafter
既存の型をそのまま使う別名で呼べるようになる
long inttypedef long int LInt; → LInt

構造体に typedef を付けると何が嬉しい?

構造体は通常こう書きます。

struct Item {
    int id;
    int price;
};

変数を作るときはこう。

struct Item x;

これを typedef で別名を付けると、こうなります。

typedef struct Item {
    int id;
    int price;
} Item;

以後はこう書けます。

Item x;

struct あり/なしの違い

書き方変数宣言特徴
typedef なしstruct Item x;毎回 struct が必要
typedef ありItem x;1単語でスッキリ

タグ名(struct の名前)と typedef 名(別名)の関係

ここは混乱しやすいので、図で整理します。

タグ名と typedef 名

typedef struct TagName {
    ...
} TypeName;
  • TagName:構造体タグ(タグ名)
  • TypeName:typedef 名(型名として使える別名)

それぞれ何者?

名前何?
構造体タグstruct の後ろの識別子struct TagName
typedef 名型名として使える別名TypeName

タグ名は省略できる(ただし性質が変わる)

タグ名あり(両方使える)

typedef struct Device {
    int level;
} Device;
  • struct Device も Device も使える

タグ名なし(typedef 名だけ使える)

typedef struct {
    int level;
} Device;
  • Device は使える
  • struct Device は使えない(タグが無いので)

タグ省略の効果

宣言使える型名使えない型名
typedef struct Device { ... } Device;struct Device / Deviceなし
typedef struct { ... } Device;Devicestruct Device

命名のコツ(紛らわしさ回避)

元の例だと student と Student が大文字小文字だけ違って、ちょっと紛らわしいです。

おすすめは、こんな方針です。

方針理由
typedef 名は大文字始まりStudent, Device, Item型だと一目で分かる
タグ名は省略するtypedef struct { ... } Student;二重管理を避けられる
どうしてもタグ名を付けるなら別名にするtypedef struct student_tag { ... } Student;student と Student の混同を避ける

サンプルプログラム

商品情報を入力して表示するプログラム例です。

このプログラムでやること

  • 構造体 Product に typedef 名 Product を付ける
  • 商品名・在庫・価格を scanf で入力
  • 入力した内容を「登録しました!」というメッセージ付きで表示

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

Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。

#include <stdio.h>

#define NAME_LEN 32

typedef struct {
    char   name[NAME_LEN];  // 商品名
    int    stock;           // 在庫数
    double price;           // 価格
} Product;

int main(void)
{
    Product item;

    printf("商品名を入力してください:");
    scanf("%s", item.name);

    printf("在庫数を入力してください:");
    scanf("%d", &item.stock);

    printf("価格(円)を入力してください:");
    scanf("%lf", &item.price);

    printf("\n登録しました!\n");
    printf("商品名:%s\n", item.name);
    printf("在庫:%d 個\n", item.stock);
    printf("価格:%.0f 円\n", item.price);

    return 0;
}

このプログラムに出てくる項目を表でしっかり解説

typedef struct { ... } Product;

要素何をする?
struct { ... }構造体型(フォーマット)を定義する
typedefその型に別名を付ける
Product以後、型名として使える名前

説明
この1行で「構造体の型を作る」+「その型を Product という1単語で呼べるようにする」が同時にできます。
タグ名を付けていないので、struct 何々 という型名は使わず、Product だけで運用するスタイルです。

scanf で入力するときの & が要る/要らない(重要ポイント)

scanf は「入力した値を書き込む先の住所(アドレス)」が必要です。
だから多くの場合、& を付けます。

でも、配列だけは例外が出ます。

scanf と & の関係

入力先& が必要?理由
int 変数&item.stock必要値を書き込むため住所が必要
double 変数&item.price必要同上
char配列item.name不要配列名は先頭要素のアドレスのように扱われるため

図:配列名は先頭を指すイメージ

図の説明
配列 name はメモリ上で連続した箱です。配列名 item.name は「先頭の箱」を表すように使われるため、scanf にそのまま渡せます。

ついでに注意:scanf %s は長すぎる入力に弱い

学習用としてはこれでOKですが、実務では入力が長すぎると危険です。
安全寄りにするなら、読み取り幅を付けます。

書式(読み取り幅つき %s)

scanf("%31s", item.name);
  • NAME_LEN が 32 なら、最大31文字+終端文字 0 を想定

(この話は深掘りすると長くなるので、ここでは「幅を付けると安全」の感覚だけ持てばOKです)

まとめ(覚えておくと強い)

要点ひとこと
typedef は型の別名を付ける長い型名を短くできる
構造体に typedef を付けると 1単語で宣言できるstruct を毎回書かなくてよい
タグ名は省略できる省略すると struct Tag は使えなくなる
scanf は基本 & が必要ただし char配列は & が不要