C言語の構造体とは?基本から使い方まで初心者向けに徹底解説

2025年4月14日月曜日

C言語

C言語の構造体、なんだか難しそう…って思っていませんか? 実は、プログラム作りをグッと楽にしてくれる、とっても便利な機能なんです。

この記事では、C言語の構造体のキホンから、実際の使い方、ちょっとした注意点まで、初心者の方でも「なるほど!」と思えるように、分かりやすく解説を進めていきます。

この記事を読むと、こんなことができるようになりますよ。

  • 構造体が何なのか、なぜ使うと便利なのかが分かる
  • 構造体の基本的な書き方を覚えられる
  • 構造体のメンバにアクセスする方法が身につく
  • 構造体を初期化する方法が分かる
  • 簡単なサンプルプログラムで構造体の使い方を体験できる
  • 構造体を使う上でのちょっとしたコツを知れる

さあ、一緒に構造体の世界を探検しましょう! きっと、あなたのプログラミングの世界が広がりますよ。

C言語の構造体とは?~複雑なデータをまとめる基本機能~

プログラムを作っていると、例えば人の「名前」と「年齢」と「身長」みたいに、いくつか種類の違うデータをセットで扱いたい場面が出てきますよね。

今までの知識だと、

char name[50];
int age;
double height;

みたいに、一つ一つのデータを別々の変数として用意する必要がありました。これでも動きますが、データが増えてくると「どれとどれがセットだっけ?」と、ごちゃごちゃして管理が大変になりがちなんです。

そこで登場するのが構造体(こうぞうたい)です!

構造体を使うと、違う種類のデータ型(例えば文字配列、整数、小数など)を、ひとつのオリジナルな「まとまり」として定義できます。さっきの例なら、「名前・年齢・身長」をまとめて「個人データ型」みたいに作れるイメージですね。

例えるなら、バラバラだった文房具(変数)を、ひとつの筆箱(構造体)にまとめて整理するようなものです。必要なものをサッと取り出せるし、持ち運びも管理も楽になりますよね。

このように、関連するデータをグループ化することで、

  • プログラムの見た目がスッキリする
  • データの管理がしやすくなる
  • コードが読みやすく、間違いにくくなる

といったメリットがあります。特に、扱うデータが複雑になってくるほど、構造体のありがたみが分かってくるはずです!

【C言語の構造体】基本的な書き方(宣言方法)

では、さっそく構造体の作り方、つまり「宣言」の方法を見ていきましょう。構造体を使うには、まず「こういうデータまとまりを作りますよー」という設計図を作る必要があります。

基本の形はこんな感じです。

struct 構造体タグ名 {
    データ型1 メンバ名1;
    データ型2 メンバ名2;
    // ... 必要なだけメンバを追加 ...
}; // ← セミコロン忘れずに!

いくつかポイントがあります。

  • struct
    • 「これから構造体を定義しますよ」という合図のキーワードです。
  • 構造体タグ名
    • あなたが作るオリジナルのデータ型の「名前」です。分かりやすい名前を付けましょう(例: `Person`, `Point`, `Student` など)。
  • { } (波括弧)
    • この中に、まとめたいデータ(メンバ)を書いていきます。
  • メンバ
    • 構造体の中に含める個々のデータのことです。通常の変数宣言と同じように「データ型 メンバ名;」の形で書きます。
  • ; (セミコロン)
    • 構造体定義の最後には、忘れずにセミコロンを付けましょう。

例えば、「人の名前(文字列)と年齢(整数)をまとめる構造体」を作るとしたら、こんな風に書けます。

struct Person {
    char name[50]; // 名前を入れるための文字配列メンバ
    int age;       // 年齢を入れるための整数メンバ
}; // ← セミコロン!

これで、「Person」という名前の、名前と年齢をセットで保持できる新しいデータ型(の設計図)が完成しました!

構造体変数の宣言方法

さっき作ったのは、あくまで「設計図」です。実際にデータを入れるためには、その設計図に基づいて「実体」、つまり構造体の変数を作る必要があります。

変数の作り方は、普通の変数宣言と似ています。

struct 構造体タグ名 変数名;

さっき定義した `Person` 構造体を使って、実際にデータを格納するための変数 `person1` を作ってみましょう。

// まずは構造体型を定義 (設計図を作る)
struct Person {
    char name[50];
    int age;
};

// 次に、Person型の変数を作る (設計図から実体を作る)
struct Person person1; // person1 という名前の変数を作成
struct Person person2; // 別の変数 person2 も作れる

これで、`person1` や `person2` という名前の「箱」が用意されました。それぞれの箱には、名前(`name`)と年齢(`age`)を入れるスペースがあります。

「`struct Person`」で一つのデータ型だと考えると、`int number;` と同じような感覚で変数を宣言できるのが分かりますね。

typedefを使った構造体の宣言

毎回 `struct 構造体タグ名` って書くの、ちょっと面倒くさいな…と感じるかもしれません。そんな時に便利なのが `typedef` (タイプデフ) です。

`typedef` を使うと、既存のデータ型に別名(エイリアス)を付けることができます。構造体の定義と組み合わせると、もっとスッキリ書けるようになります。

書き方はいくつかありますが、よく使われるのはこちら。

typedef struct {
    データ型1 メンバ名1;
    データ型2 メンバ名2;
    // ...
} 新しい型名; // ← ここで別名を付ける

さっきの `Person` 構造体を `typedef` を使って定義してみましょう。

typedef struct {
    char name[50];
    int age;
} PersonData; // ← PersonData という新しい型名を付けた

こうすると、変数を作るときに `struct` キーワードが不要になります!

// typedef を使って定義した場合...
PersonData person1; // struct がいらない!スッキリ!
PersonData person2;

どっちの書き方でも間違いではありませんが、`typedef` を使うとコードが短くなって読みやすくなることが多いので、よく使われるテクニックです。覚えておくと便利ですよ!

【C言語の構造体】メンバへのアクセス

構造体の変数を作ったら、次はその中にあるメンバにデータを入れたり、データを取り出したりしたくなりますよね。

構造体のメンバにアクセスするには、ドット演算子 `.` を使います。

変数名.メンバ名

という形で書きます。簡単でしょう?

実際にやってみましょう。さっき作った `PersonData` 型の変数 `person1` の名前と年齢に値を入れてみます。

#include <stdio.h>
#include <string.h> // 文字列を扱う strcpy 関数を使うために必要

// typedef で構造体型 PersonData を定義
typedef struct {
    char name[50];
    int age;
} PersonData;

int main(void) {
    // PersonData 型の変数 person1 を宣言
    PersonData person1;

    // person1 の name メンバに値を代入 (文字列の代入は strcpy を使う)
    strcpy(person1.name, "山田 太郎");

    // person1 の age メンバに値を代入
    person1.age = 30;

    // person1 のメンバの値を取り出して表示
    printf("名前: %s\n", person1.name); // .name で name メンバにアクセス
    printf("年齢: %d 歳\n", person1.age); // .age で age メンバにアクセス

    return 0;
}

これを実行すると、ちゃんと代入した値が表示されます。

名前: 山田 太郎
年齢: 30 歳

このように、ドット `.` を使えば、普通の変数と同じような感覚で構造体のメンバに値を読み書きできますね!

【C言語の構造体】初期化の方法

変数を宣言するときに、一緒に最初の値(初期値)を入れておくことを「初期化」といいますよね。構造体も、宣言する時に初期化できます。

初期化するには、波括弧 `{}` とカンマ `,` を使います。

構造体型 変数名 = {メンバ1の値, メンバ2の値, ...};

波括弧の中に、構造体を定義したときのメンバの順番通りに、初期値をカンマで区切って書いていきます。

さっきの `PersonData` 型でやってみましょう。

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

typedef struct {
    char name[50]; // 1番目のメンバ
    int age;       // 2番目のメンバ
} PersonData;

int main(void) {
    // 宣言と同時に初期化!
    PersonData person1 = {"鈴木 一郎", 25}; // name に "鈴木 一郎", age に 25 が入る

    // 初期化された値を表示して確認
    printf("名前: %s\n", person1.name);
    printf("年齢: %d 歳\n", person1.age);

    return 0;
}

実行結果:

名前: 鈴木 一郎
年齢: 25 歳

どうでしょう? 宣言と初期化を同時に行うと、コードが短く、分かりやすくなる場合があります。特に、決まった値で始まる構造体を作りたいときに便利です。

ただし、値を書く順番は、構造体を定義したときのメンバの順番としっかり合わせる必要があります。順番を間違えると、意図しないメンバに値が入ってしまうので注意しましょう。

【C言語の構造体】サンプルプログラムで実践

ここまでの内容を使って、簡単なプログラムを実際に作ってみましょう! 構造体の便利さを体験してみてください。

ここでは、「生徒の名前と、国語・数学の点数」を管理する構造体 `Student` を作って、その情報を表示するプログラムを書いてみます。

サンプルプログラム1:基本的な構造体の利用

まずは、`struct` キーワードを使った基本的な定義、変数宣言、そしてドット演算子を使ったメンバへの代入と参照を行うプログラムです。

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

// 生徒情報を格納する構造体 Student を定義
struct Student {
    char name[50]; // 名前
    int kokugo;    // 国語の点数
    int sugaku;    // 数学の点数
};

int main(void) {
    // Student 型の変数 student1 を宣言
    struct Student student1;

    // student1 の各メンバに値を代入
    strcpy(student1.name, "佐藤 次郎");
    student1.kokugo = 85;
    student1.sugaku = 70;

    // 合計点を計算
    int total = student1.kokugo + student1.sugaku;

    // 結果を表示
    printf("--- 生徒情報 ---\n");
    printf("名前: %s\n", student1.name);
    printf("国語: %d 点\n", student1.kokugo);
    printf("数学: %d 点\n", student1.sugaku);
    printf("合計: %d 点\n", total);

    return 0;
}

実行結果:

--- 生徒情報 ---
名前: 佐藤 次郎
国語: 85 点
数学: 70 点
合計: 155 点

解説:
このプログラムでは、まず `struct Student` で生徒の情報をまとめる型を定義しました。

`main` 関数の中で `struct Student student1;` として変数を作成し、`strcpy` や `=` を使って各メンバ(`name`, `kokugo`, `sugaku`)に値を設定しています。最後に、メンバの値を `printf` で表示し、合計点も計算して表示していますね。ドット演算子 `.` でメンバにアクセスするのが基本だと分かります。

サンプルプログラム2:typedefと初期化を活用

次は、`typedef` を使って型定義をスッキリさせ、さらに宣言時の初期化を使った、より実践的なコード例です。

#include <stdio.h>

// typedef を使って生徒情報を格納する構造体型 ScoreData を定義
typedef struct {
    char name[50]; // 名前
    int kokugo;    // 国語の点数
    int sugaku;    // 数学の点数
} ScoreData;

// 生徒情報を表示する関数 (構造体を引数で受け取る例)
void printScore(ScoreData student) {
    int total = student.kokugo + student.sugaku;
    printf("--- 生徒情報 ---\n");
    printf("名前: %s\n", student.name);
    printf("国語: %d 点\n", student.kokugo);
    printf("数学: %d 点\n", student.sugaku);
    printf("合計: %d 点\n", total);
}

int main(void) {
    // ScoreData 型の変数を宣言と同時に初期化
    ScoreData student1 = {"高橋 花子", 92, 78};
    ScoreData student2 = {"田中 健太", 65, 88};

    // 関数を呼び出して情報を表示
    printScore(student1);
    printf("\n"); // 見やすく改行
    printScore(student2);

    return 0;
}

実行結果:

--- 生徒情報 ---
名前: 高橋 花子
国語: 92 点
数学: 78 点
合計: 170 点

--- 生徒情報 ---
名前: 田中 健太
国語: 65 点
数学: 88 点
合計: 153 点

解説:
`typedef` を使ったことで、変数宣言が `ScoreData student1 = ...;` のように短く書けるようになりました。

宣言時に `{}` を使って初期化しているので、代入のコードも不要になっています。関数 `printScore` に構造体変数をそのまま渡せるのもポイントです。データ一式をまとめて関数に渡せるので、コードが整理されやすくなりますね!

【C言語の構造体】応用的な使い方

構造体は便利ですが、いくつか知っておくと良い点もあります。ここでは、C言語の構造体を使う上で知っておくべきことを紹介します。

構造体のコピー(代入)

構造体変数同士は、イコール `=` を使って簡単に中身を丸ごとコピーできます

#include <stdio.h>

typedef struct {
    int x;
    int y;
} Point;

int main(void) {
    Point p1 = {10, 20}; // 初期化
    Point p2;

    p2 = p1; // p1 の中身を p2 に丸ごとコピー!

    printf("p1: x=%d, y=%d\n", p1.x, p1.y);
    printf("p2: x=%d, y=%d\n", p2.x, p2.y);

    // p2 の値を変えても p1 には影響しない
    p2.x = 30;
    printf("--- p2.x を変更後 ---\n");
    printf("p1: x=%d, y=%d\n", p1.x, p1.y);
    printf("p2: x=%d, y=%d\n", p2.x, p2.y);

    return 0;
}

実行結果:

p1: x=10, y=20
p2: x=10, y=20
--- p2.x を変更後 ---
p1: x=10, y=20
p2: x=30, y=20

このように、`p2 = p1;` とするだけで、`p1` のメンバ `x` と `y` の値が、`p2` のメンバ `x` と `y` にそれぞれコピーされます。コピーされた後は、`p1` と `p2` は別々のデータになるので、片方を変更してももう片方には影響しません。これは結構便利ですね!

構造体とポインタ(アロー演算子の紹介)

C言語といえばポインタ!構造体もポインタと組み合わせて使うことがよくあります。構造体の変数そのものではなく、構造体変数が置いてあるメモリ上の「住所」を扱うのがポインタです。

構造体へのポインタ変数を宣言するのは、普通のポインタと同じです。

構造体型 *ポインタ変数名;

例えば、`Point` 構造体へのポインタならこう書きます。

Point pt = {10, 20}; // Point 型の変数
Point *ptr;         // Point 型へのポインタ変数
ptr = &pt;          // pt のアドレスを ptr に代入

ここで問題になるのが、ポインタを使ってメンバにアクセスする方法です。

ドット演算子 `.` は、構造体変数「そのもの」からメンバにアクセスする時に使いました。ポインタ変数からメンバにアクセスする場合は、ドット演算子ではなく、アロー演算子 `->` を使います。

ポインタ変数名->メンバ名

さっきの例の続きで、ポインタ `ptr` を使って `pt` のメンバにアクセスしてみましょう。

#include <stdio.h>

typedef struct {
    int x;
    int y;
} Point;

int main(void) {
    Point pt = {10, 20};
    Point *ptr;
    ptr = &pt; // ptr が pt を指すようにする

    // アロー演算子 -> を使ってメンバにアクセス!
    printf("ポインタ経由: x=%d, y=%d\n", ptr->x, ptr->y);

    // ちなみに、こういう書き方も同じ意味です (ちょっと面倒)
    // printf("(*ptr).x = %d\n", (*ptr).x);

    // アロー演算子で値を変更することもできる
    ptr->x = 50;
    printf("変更後の pt.x = %d\n", pt.x); // 元の変数 pt の値も変わる!

    return 0;
}

実行結果:

ポインタ経由: x=10, y=20
変更後の pt.x = 50

このように、ポインタ変数を使うときは `->` でメンバにアクセスします。`(*ptr).x` という書き方もできますが、`ptr->x` の方が断然スッキリ書けますね。なぜポインタを使うのか、という話はまた別の機会になりますが、構造体のポインタからメンバにアクセスするときはアロー演算子 `->` を使う、と覚えておきましょう!

他にも、構造体のメモリサイズ(パディング)の話などもありますが、まずはここまで紹介した基本をしっかり押さえておけば大丈夫です!

【まとめ】C言語 構造体をマスターしてステップアップ!

お疲れ様でした! C言語の構造体について、基本的なところから使い方、注意点まで見てきました。

今回のポイントを振り返ってみましょう。

  • 構造体は、異なる型のデータをひとまとめにする便利な機能
  • `struct` キーワードで定義し、`typedef` で別名を付けると便利
  • メンバへのアクセスは、変数ならドット `.` 、ポインタならアロー `->` を使う
  • 宣言時に `{}` で初期化できる
  • 構造体変数同士は `=` でコピーできる

構造体を使いこなせるようになると、プログラムで扱えるデータの幅がぐんと広がり、コードも整理しやすくなります。最初はちょっと戸惑うかもしれませんが、実際にコードを書いて動かしてみるのが一番の近道です!

今回学んだ構造体は、C言語のファイル操作や、もっと複雑なデータ構造(リスト構造や木構造など)を学ぶ上でも基礎となります。ぜひ、今回の内容を土台にして、さらにステップアップを目指してくださいね!


【関連記事】

>> C言語とは?特徴・できること・学ぶメリット

このブログを検索

  • ()

自己紹介

自分の写真
リモートワークでエンジニア兼Webディレクターとして活動しています。プログラミングやAIなど、日々の業務や学びの中で得た知識や気づきをわかりやすく発信し、これからITスキルを身につけたい人にも役立つ情報をお届けします。 note → https://note.com/yurufuri X → https://x.com/mnao111

QooQ