
C言語のきほん|構造体宣言とtypedefの活用
構造体の書き方を整理すると、コードはもっと短く、もっと読みやすくなる。
構造体を使えるようになると、関連するデータをひとまとまりにして扱えるようになり、C言語のプログラムがぐっと整理しやすくなります。
ただし、構造体は書き方が1つだけではありません。実は、目的に応じていくつかの宣言方法を使い分けることができます。
たとえば、構造体の型だけを先に宣言してあとから変数を作る方法もあれば、型の宣言と変数の定義を同時に行う方法もあります。さらに、タグ名を省略した無名構造体や、typedef を使って別名を付ける方法もあります。
このあたりは最初は少しややこしく感じるかもしれませんが、ひとつひとつ整理して見ていくと、それぞれの役割はとても分かりやすいです。
特に typedef は、構造体を使うたびに毎回 struct と書かなくてよくなるので、コードをすっきり見せたいときにとても便利です。
構造体の宣言方法を理解すると、単に文法を覚えるだけでなく、
- どの書き方が読みやすいか
- どの場面で使うと自然か
- どの方法が再利用しやすいか
といった視点も身についてきます。
ここでは、構造体のいろいろな宣言方法を順番に整理しながら、typedef の便利さや使いどころまで、やさしく丁寧に確認していきましょう。
構造体の宣言方法は1つではない
これまで学んできた基本の書き方は、まず構造体の型を宣言し、そのあとで変数を宣言する形でした。
struct point {
int x;
int y;
};
struct point p1;この形はとても分かりやすく、構造体の基本として最初に覚えるのにぴったりです。
ただ、C言語ではこれ以外にもいくつかの書き方が用意されています。
今回押さえたいのは、次の4つです。
| 宣言方法 | 特徴 |
|---|---|
| 型の宣言と実体の定義を同時に行う | 1か所でまとめて書ける |
| 定義と同時に初期化を行う | 値を最初から入れられる |
| タグ名を省略して定義する | 一度きりの用途に使える |
| typedef を使う | struct を省略して書ける |
どれも文法としては正しいですが、使いやすさや再利用のしやすさに違いがあります。
その違いを理解すると、コードの見通しがかなりよくなります。
まずは基本形を確認する
比較しやすいように、まずは通常の構造体宣言を見ておきましょう。
struct point {
int x;
int y;
};
struct point p1;この書き方では、
- struct point で構造体の型を宣言する
- struct point p1; でその型の変数を作る
という2段階になっています。
この方法のよいところは、型の定義と変数の定義が分かれているため、役割がはっきり見えることです。
一方で、変数を宣言するたびに struct point と書かなければならないので、少し長く感じることがあります。
型の宣言と実体の定義を同時に行う
最初のバリエーションは、構造体の型を宣言すると同時に、その変数も定義する方法です。
struct point {
int x;
int y;
} p1;この書き方では、構造体の型 point を宣言しながら、同時に p1 という変数も作っています。
この書き方のポイント
- 型の宣言と変数の定義を1か所にまとめられる
- すぐに使う変数が決まっている場合には便利
- あとで別の変数を作るときは、やはり struct point と書く必要がある
つまり、「型だけを先に作る」のではなく、「型と最初の1個の変数を一緒に用意する」イメージです。
表で整理する
| 書き方 | 意味 |
|---|---|
| struct point { ... }; | 型だけを宣言 |
| struct point { ... } p1; | 型を宣言しながら p1 も定義 |
この形は文法として自然ですが、設計を整理して書きたい場合は、型宣言と変数宣言を分けた方が読みやすいことも多いです。
定義と同時に初期化を行う
さらに、型の宣言と変数の定義を同時に行うだけでなく、その場で初期化もできます。
struct point {
int x;
int y;
} p1 = {10, 20};この場合は、
- p1.x に 10
- p1.y に 20
が入ります。
この書き方の特徴
- 宣言、定義、初期化を一度に書ける
- コードをコンパクトにできる
- 初期値がすぐ分かるので見やすい場合もある
ただし、型定義と変数定義と初期化が1行のまとまりになるため、長くなると少し読みにくく感じることもあります。
タグ名を省略して定義する
次に、構造体タグ名を省略する方法です。
struct {
int x;
int y;
} p1 = {10, 20};これは無名構造体と呼ばれる形です。
構造体の中身は定義されていますが、point のようなタグ名がありません。
無名構造体の特徴
- p1 は作れる
- でも、その構造体型に名前がない
- 同じ型で別の変数を作りたくても、型名で再利用できない
つまり、この書き方は「この場でこの変数だけ使えればよい」という用途には使えますが、一般的な再利用にはあまり向いていません。
なぜ再利用しにくいのか
たとえば次のようには書けません。
struct ??? p2;型に名前がないので、あとからその型を指定できないからです。
表で整理する
| 書き方 | 型名があるか | 再利用しやすいか |
|---|---|---|
| struct point { ... } p1; | ある | しやすい |
| struct { ... } p1; | ない | しにくい |
無名構造体は文法上使えますが、初心者のうちは「型名がないので再利用しにくい」という点をしっかり押さえておくとよいです。
typedef を使って宣言を簡潔にする
ここでとても便利なのが typedef です。
typedef を使うと、構造体型に別名を付けることができます。
たとえば次のように書きます。
typedef struct {
int x;
int y;
} Point;これで Point という新しい型名が使えるようになります。
その結果、変数宣言は次のように書けます。
Point p1 = {100, 200};これまでのように struct point p1; と書く必要がなくなり、かなりすっきりします。
typedef のよさ
| 利点 | 内容 |
|---|---|
| struct を毎回書かなくてよい | 変数宣言が短くなる |
| コードが見やすい | 型名が自然に読める |
| 再利用しやすい | 他の場所でも同じ型名を使いやすい |
| 大きなプログラムで便利 | 宣言が増えても読みやすさを保ちやすい |
このため、実際のコードでは typedef を使った構造体宣言がよく使われます。
学習が進んでいくと、この形を基本として扱うことが多くなります。
typedef の書き方を整理する
typedef の書き方はいくつかありますが、まずは次の形を基本として覚えると分かりやすいです。
typedef struct {
int x;
int y;
} Point;この書き方の意味を分解すると、
- struct { ... } で構造体を定義する
- typedef によってその型に Point という別名を付ける
という流れです。
すると、以後は Point が型名として使えます。
Point a;
Point b;
Point points[3];この見た目は、int や double と同じように自然ですね。
これが typedef の大きな魅力です。

この図では、構造体の宣言方法ごとの違いを一覧で比較できます。
特に大切なのは、無名構造体には型名がないため再利用しにくいこと、そして typedef を使うと型名が短く扱いやすくなることです。
コードを読みやすく保ちたい場面では、typedef の利点がかなり大きいと分かります。
サンプルプログラムで typedef の便利さを確認する
ここでは、元の point の例を、より分かりやすい商品情報の例に置き換えて説明します。
表示メッセージも日本語にし、コメントも日本語にしています。
ファイル名:15_4_1.c
#include <stdio.h>
// 商品情報を表す構造体に Item という別名を付ける
typedef struct {
int code; // 商品番号
double price; // 価格
} Item;
int main(void)
{
// typedef で定義した型名を使って変数を宣言し、初期化する
Item item1 = {101, 1980.0};
Item item2 = {102, 2500.0};
printf("商品1: 番号=%d 価格=%.0f円\n", item1.code, item1.price);
printf("商品2: 番号=%d 価格=%.0f円\n", item2.code, item2.price);
return 0;
}実行結果例
商品1: 番号=101 価格=1980円
商品2: 番号=102 価格=2500円サンプルプログラムを読み解く
最初に、typedef を使って構造体型に Item という別名を付けています。
typedef struct {
int code;
double price;
} Item;この時点で、Item は型名として使えるようになります。
そのため、変数宣言では次のように書けます。
Item item1 = {101, 1980.0};
Item item2 = {102, 2500.0};もし typedef を使わないなら、次のように書く必要があります。
struct item {
int code;
double price;
};
struct item item1 = {101, 1980.0};
struct item item2 = {102, 2500.0};もちろんこれでも正しいのですが、何度も struct item と書くのは少し長く感じます。
typedef を使うと、その部分を Item に置き換えられるので、かなり読みやすくなります。
typedef は新しい構造体を作るものではない
ここでひとつ大事な点があります。
typedef は、新しい構造体の仕組みを生み出しているわけではありません。
あくまで既存の型に別名を付けているだけです。
つまり、
typedef struct {
int x;
int y;
} Point;は、「Point という新しい概念の構造体を発明した」というより、
「この構造体型を Point という名前で呼べるようにした」と考えると分かりやすいです。
タグ名付き typedef という書き方もある
typedef には、タグ名を付けたまま別名を作る書き方もあります。
typedef struct point {
int x;
int y;
} Point;この場合は、
- struct point というタグ名もある
- Point という別名もある
という状態になります。
そのため、どちらでも使えます。
struct point a;
Point b;ただ、学習の最初の段階では、タグ名なしの typedef の方がシンプルで分かりやすいことが多いです。
どの宣言方法を使えばよいのか
実際にどの書き方を使うかは、目的次第です。
ただ、一般的な考え方としては次のように整理できます。
| 書き方 | 向いている場面 |
|---|---|
| 基本の struct タグ名 | 構造体の仕組みを学ぶとき |
| 型と変数を同時定義 | その場で1個だけすぐ使いたいとき |
| 無名構造体 | 一度きりで再利用しないとき |
| typedef | 以後も何度も使う型を読みやすくしたいとき |
学習用としては基本形を理解することが大切ですが、実際のコードを読みやすく保ちたいなら typedef はとても有力です。
実践問題
次の要件を満たすプログラムを作成してください。
① 本の情報を表す構造体 book_info を定義する。構造体には以下のメンバを持たせる。
code:本の番号(int型)
title:本のタイトル(char型配列、最大20文字)
price:価格(double型)
genre:ジャンル名を表す文字列(char型配列、最大20文字)
② 構造体の配列を使って、3冊分の本の情報を管理する。
③ genre 以外の情報は初期化で設定する。
④ genre は初期化後に設定する。
⑤ 各本の情報を表示する。
解答例
ファイル名:15_4_2.c
#include <stdio.h>
#include <string.h>
// 本の情報を表す構造体
struct book_info {
int code; // 本の番号
char title[20]; // タイトル
double price; // 価格
char genre[20]; // ジャンル
};
int main(void)
{
struct book_info books[3] = {
{1, "C言語基礎", 2200.0, ""},
{2, "配列入門", 1800.0, ""},
{3, "関数のしくみ", 2500.0, ""}
};
strcpy(books[0].genre, "技術書");
strcpy(books[1].genre, "入門書");
strcpy(books[2].genre, "参考書");
printf("本の情報\n");
for (int i = 0; i < 3; i++) {
printf("本%d:番号 %d, タイトル %s, 価格 %.0f円, ジャンル %s\n",
i + 1,
books[i].code,
books[i].title,
books[i].price,
books[i].genre);
}
return 0;
}実行結果例
本の情報
本1:番号 1, タイトル C言語基礎, 価格 2200円, ジャンル 技術書
本2:番号 2, タイトル 配列入門, 価格 1800円, ジャンル 入門書
本3:番号 3, タイトル 関数のしくみ, 価格 2500円, ジャンル 参考書解説
この問題では、構造体配列を使って複数の本の情報を管理しています。
code、title、price は初期化子で設定し、genre はあとから strcpy で代入しています。
文字列メンバには = で代入できないため、char型配列のメンバに対しては strcpy を使う必要があります。
また、配列と for文を組み合わせることで、3冊分のデータをまとめて表示できています。
実践問題
次の要件を満たすプログラムを作成してください。
① 三角形の情報を表す構造体を定義し、typedef 指定子で Triangle というエイリアスを与える。
bottom:底辺の長さ(double型)
height:高さ(double型)
② 構造体の配列を使って、5つの三角形の情報を管理する。
③ 初期化時に各三角形の値を以下のように設定する。
{3.0, 4.0}, {5.0, 2.0}, {6.0, 7.0}, {8.0, 3.0}, {4.5, 5.0}
④ 各三角形の面積を計算し、一番面積の大きい三角形の情報を表示する。
解答例
ファイル名:15_4_3.c
#include <stdio.h>
// 三角形の情報を表す型
typedef struct {
double bottom; // 底辺
double height; // 高さ
} Triangle;
int main(void)
{
Triangle triangles[5] = {
{3.0, 4.0},
{5.0, 2.0},
{6.0, 7.0},
{8.0, 3.0},
{4.5, 5.0}
};
int max_index = 0;
double max_area = triangles[0].bottom * triangles[0].height / 2.0;
for (int i = 1; i < 5; i++) {
double area = triangles[i].bottom * triangles[i].height / 2.0;
if (area > max_area) {
max_area = area;
max_index = i;
}
}
printf("一番面積の大きい三角形\n");
printf("底辺:%.1f\n", triangles[max_index].bottom);
printf("高さ:%.1f\n", triangles[max_index].height);
printf("面積:%.1f\n", max_area);
return 0;
}実行結果例
一番面積の大きい三角形
底辺:6.0
高さ:7.0
面積:21.0解説
この問題では、typedef を使って Triangle という型名を作っています。
そのおかげで Triangle triangles[5]; のように、自然な書き方で構造体配列を宣言できます。
また、最大面積を求める処理では、配列を順番に調べながら、現在の最大値より大きいものが見つかったら更新しています。
このように、構造体配列と繰り返し処理はとても相性がよいです。
実践問題
次の表で管理された商品の情報から、平均価格と平均在庫数を求めるプログラムを作成してください。
| 番号 | 商品名 | 価格 | 在庫数 |
|---|---|---|---|
| 1 | ノート | 120.0 | 50 |
| 2 | ペン | 80.0 | 120 |
| 3 | 消しゴム | 60.0 | 70 |
| 4 | 定規 | 150.0 | 40 |
| 5 | ファイル | 200.0 | 30 |
| 6 | はさみ | 350.0 | 20 |
解答例
ファイル名:15_4_4.c
#include <stdio.h>
// 商品情報を表す構造体
typedef struct {
int no; // 番号
char name[20]; // 商品名
double price; // 価格
int stock; // 在庫数
} Product;
int main(void)
{
Product products[6] = {
{1, "ノート", 120.0, 50},
{2, "ペン", 80.0, 120},
{3, "消しゴム", 60.0, 70},
{4, "定規", 150.0, 40},
{5, "ファイル", 200.0, 30},
{6, "はさみ", 350.0, 20}
};
double total_price = 0.0;
int total_stock = 0;
for (int i = 0; i < 6; i++) {
total_price += products[i].price;
total_stock += products[i].stock;
}
printf("平均価格:%.1f円\n", total_price / 6);
printf("平均在庫数:%.1f個\n", (double)total_stock / 6);
return 0;
}実行結果例
平均価格:160.0円
平均在庫数:55.0個解説
この問題では、構造体配列に複数の商品の情報を入れ、for文で合計を求めています。
price は double 型なのでそのまま加算し、stock は int 型ですが、平均を小数で表示したいので最後に double に変換して計算しています。
構造体を使うことで、価格と在庫数という異なる型のデータを1件ずつまとまりとして扱えるのが大きな利点です。
実践問題
次の要件を満たすプログラムを作成してください。
① 構造体を用いて、1つの基準点と複数の点との距離を求めるプログラムを作成する。
② 各点は int 型の x 座標と y 座標で定義されている。
③ 基準点は変数で、比較用の複数の点は配列で管理する。
④ プログラムは、基準点と各点との距離を求めて表示する。
⑤ 距離の計算には次の式を使う。
⑥ 平方根を求めるには math.h の sqrt 関数を使用する。
解答例
ファイル名:15_4_5.c
#include <stdio.h>
#include <math.h>
// 点の情報を表す型
typedef struct {
int x; // x座標
int y; // y座標
} Point;
int main(void)
{
Point origin = {0, 0};
Point points[5] = {
{3, 4},
{5, 12},
{-2, 8},
{0, 7},
{-6, -8}
};
printf("基準点:(%d, %d)\n\n", origin.x, origin.y);
for (int i = 0; i < 5; i++) {
double dx = points[i].x - origin.x;
double dy = points[i].y - origin.y;
double distance = sqrt(dx * dx + dy * dy);
printf("点[%d]:(%d, %d)\n", i + 1, points[i].x, points[i].y);
printf("→ 距離:%.3f\n\n", distance);
}
return 0;
}実行結果例
基準点:(0, 0)
点[1]:(3, 4)
→ 距離:5.000
点[2]:(5, 12)
→ 距離:13.000
点[3]:(-2, 8)
→ 距離:8.246
点[4]:(0, 7)
→ 距離:7.000
点[5]:(-6, -8)
→ 距離:10.000解説
この問題では、基準点を1つの構造体変数で表し、比較対象を構造体配列で表しています。
距離の計算式では平方根が必要なので sqrt 関数を使っています。
構造体を使うことで、x座標とy座標を「点」というひとつの単位で扱えるため、計算式の意味もはっきり見えます。
また、配列にしているので、複数の点を for文でまとめて処理できます。
宣言方法ごとの使い分けを整理する
ここまで学んだ内容を、最後に表で整理しておくと理解が安定します。
| 方法 | 例 | 使いどころ |
|---|---|---|
| 基本の構造体宣言 | struct point { ... }; | 基本を理解するとき |
| 型と変数を同時定義 | struct point { ... } p1; | 1か所でまとめたいとき |
| 同時初期化 | struct point { ... } p1 = {10, 20}; | 定義と初期化を一度に済ませたいとき |
| 無名構造体 | struct { ... } p1; | 一度きりの用途 |
| typedef | typedef struct { ... } Point; | 読みやすく簡潔にしたいとき |
どの方法も文法としては使えますが、長く使うコードや、学習を進めていく中では typedef の便利さを実感する場面が多くなります。
構造体を「使える」だけでなく、「読みやすく書ける」ようになることが、この単元の大きなポイントです。
