C言語の構造体とは?使い方の基本から応用まで分かりやすく徹底解説!

プログラミングを行ううちに、同じような情報をひとまとめにできたら便利だと感じることがあると思います。
C言語の構造体は、まさにそんな状況で役立つ仕組み。変数を一つずつ宣言する手間を減らしたり、コードの見通しを良くしたりするのに大いに活躍します。
C言語を学んだばかりでも、使い方を理解しておくと、保守性や可読性が向上するでしょう。
この記事では、構造体の基本的な定義や宣言から、応用的なテクニックまでをサンプルコードとともに解説します。
C言語の構造体とは?
C言語の構造体とは、関連する複数のデータをひとまとめにして扱えるユーザー定義型のことです。
たとえば名前や年齢、座標など異なる型の変数を一つのまとまりとして管理できるため、複雑な情報をシンプルに表現できます。
構造体を理解すると、実世界のオブジェクトをコード上で自然に扱えるようになり、プログラム全体の見通しが良くなるでしょう。
また構造体はメモリ上で連続した領域に配置されるため、ファイル入出力やネットワーク通信でデータを扱いやすくなる点も魅力です。
C言語で構造体を使うメリット
C言語で構造体を使うメリットは、関連するデータを整理してコードの可読性を高められることです。
個別の変数をまとめることで宣言やアクセスが簡潔になり、関数の引数としてまとめて渡せるためインターフェースが明確になります。
構造体単位で扱うので、変更が必要になったとき修正範囲も限定でき、バグの発見や拡張も容易でしょう。
さらに構造体は他の構造体をメンバーに持つネストが可能で、複雑なデータ構造を段階的に設計できます。
結果としてコードの再利用性が高まり、仕様変更時の手間を減らせる点もメリットです。
構造体・クラス・共用体の違い
構造体、クラス、共用体はデータをまとめる点で似ていますが役割が異なります。
構造体はC言語で使い、複数のメンバーを個別に保持する集まりとして機能します。
クラスは主にC++で導入され、構造体の機能に加えてアクセス制御やメソッド、コンストラクタ、継承などのオブジェクト指向機能を備えます。
共用体はメンバーが同じメモリ領域を共有するため、一度に一つの値しか保持できないことから、メモリ効率が重視される場面で有効です。
なお、本記事のここから先は実際のサンプルコードが多く登場します。開発環境の構築から始める方は、ぜひ以下の記事も参考にしてください。
「なんか今の仕事合わないな・・・」
「IT業界に転職してみたいなぁ・・・」
という方、DMMが運営する「WEBCAMP エンジニア転職」をご検討してみてはいかがですか?

「WEBCAMP エンジニア転職」では最短12週間でITエンジニアを目指すことが可能です!
WEBCAMPの卒業生は転職後に年収もUP!(例:年収250万円→500万円)
しかも今なら受講料の最大70%が給付金として支給されます。
DMM WEBCAMPは経済産業省・厚生労働省が認定した専門実践教育訓練給付金制度の対象です
C言語の構造体の使い方【基本】
この章では、C言語の構造体の基本的な使い方を紹介していきます。
手順①:構造体の定義
構造体を定義するには大きく分けて2つの方法があります。
まず
struct 構造体名 { … };
と書いて型だけを定義し、必要に応じて
struct 構造体名 実体名
と宣言して変数を作成します。
もう1つは typedef を使う方法で、
typedef struct { … } 構造体名;
と書けば、以降は「構造体名 実体名;」だけで変数を宣言できます。
どちらも宣言時に {} で全メンバを指定すれば一括初期化が可能ですが、一括代入は宣言時のみ許され、それ以外ではまとめて代入できません。
用途や可読性に合わせて使い分けると良いでしょう。
手順②:構造体変数の宣言
構造体を定義した後は、その型を使って変数を宣言する手順に移ります。
変数宣言では
struct 型名 変数名;
と記述し、同じ構造を持つ複数のインスタンスを簡単に作成できます。
#include <stdio.h>
struct Person {
char name[20];
int age;
};
int main(void) {
struct Person p1, p2;
printf("Size of Person: %zu bytes\n", sizeof(p1));
return 0;
}
実行結果:
Size of Person: 24 bytes
このコードでは
struct Person {...}
で Person という構造体型を定義し、
struct Person p1, p2;
で2つの構造体変数を宣言しています。
各変数は name と age のメンバーを独立して保持し、同じ型のインスタンスを複数作成できる点が便利です。
構造体変数は宣言時に自動で初期化されないため、使用前に各メンバーへ値を代入する必要があります。
手順③:構造体のメンバーへアクセス
構造体のメンバーへアクセスするには、ドット演算子(.)を使います。
構造体変数名.メンバー名を記述することで、個々のフィールドに値を代入したり参照したりできます。
初期化時には中括弧( {} )を用いてまとめて指定可能で、メンバーごとの順序を守る必要があります。
また、構造体を関数の引数に渡す際もドット演算子を使って特定のメンバーだけを渡すか、構造体全体をコピー渡しするか選ぶことになります。
#include <stdio.h>
struct Person {
char name[20];
int age;
};
int main(void) {
struct Person p = {"Alice", 30};
printf("Name: %s\n", p.name);
printf("Age: %d\n", p.age);
return 0;
}
実行結果:
Name: Alice
Age: 30
上記のコードでは、定義済みの構造体 Person の変数 p を宣言と同時に初期化しています。
{"Alice", 30}
によって name フィールドに文字列が、age に数値が設定される仕組みです。
構造体のメンバーにアクセスする際はドット演算子(`.`)を使い、p.nameで文字列、p.ageで整数を参照します。printf` 関数では %s と %d を使ってそれぞれ表示し、改行で見やすく出力しています。
構造体の初期化は宣言時のみ可能で、後からまとめて代入することはできませんが、個別のフィールド更新は可能です。
C言語の構造体の使い方【応用】
ここからは、C言語の構造体の応用的な使い方を紹介します。
それぞれサンプルコードと結果を含めてわかりやすく解説しますので、ぜひ参考にしてください。
①:構造体の初期化
構造体の初期化は、宣言時に {} を使って全メンバーに値を一括設定できます。宣言と同時に初期値を指定することで、コードを簡潔にすることが可能になります。
C99以降はメンバー名を指定する「指定初期化」も可能で、順序を気にせず一部だけ設定できます。ただし宣言後にまとめて代入することはできず、個別のフィールド更新はドット演算子を使って行います。
サンプルコードと実行結果
#include <stdio.h>
struct Point { int x; int y; };
int main(void) {
struct Point p1 = {10, 20};
struct Point p2 = {.y = 50, .x = 30};
printf("p1=(%d, %d)\n", p1.x, p1.y);
printf("p2=(%d, %d)\n", p2.x, p2.y);
return 0;
}
実行結果:
p1=(10, 20)
p2=(30, 50)
上記では
struct Point p1 = {10, 20};
と書くことで、宣言と同時にメンバー x と y に順番どおり値を代入しています。
一方
struct Point p2 = {.y = 50, .x = 30};
は指定初期化と呼ばれるもので、メンバー名を指定することで順序を気にせず初期化できます。
②:構造体のポインタの使用
構造体のポインタを使うと、メモリ効率を高めながら構造体を参照・更新できるので便利です。
ポインタは
struct 型名 *ptr;
のように宣言し、& でアドレスを取得します。
メンバーアクセスにはアロー演算子 (->) を使い、
ptr->member
と記述します。
ポインタ経由なら関数呼び出し時のコピーコストを減らせるため、大きな構造体を扱う場合に有効です。
ポインタについてより理解を深めたいと思う方は、ぜひ以下の記事も参考にしてください。
サンプルコードと実行結果
#include <stdio.h>
struct Point { int x; int y; };
int main(void) {
struct Point p = {5, 10};
struct Point *ptr = &p;
ptr->x = 15;
printf("Point=(%d, %d)\n", ptr->x, ptr->y);
return 0;
}
実行結果:
Point=(15, 10)
上記では
struct Point *ptr = &p;
で構造体変数 p のアドレスをポインタ ptr に格納しています。メンバーへのアクセスには ptr->x のようにアロー演算子を使っています。
また、
ptr->x = 15;
の操作は実際に p.x の値を書き換えているため、関数間で構造体全体をコピーせずにデータを更新できて効率的です。
③:実体のコピー
構造体の実体のコピーとは、ある構造体変数を別の変数に代入し、全メンバーの値を一括で複製する操作のことです。
C言語では = 演算子で構造体同士の代入がサポートされているので、手作業で各フィールドをコピーする必要がありません。
代入後はコピー先と元の変数が独立しているため、一方を変更してももう一方には影響しません。
サンプルコードと実行結果
#include <stdio.h>
struct Person {
char name[20];
int age;
};
int main(void) {
struct Person p1 = {"Alice", 30};
struct Person p2;
p2 = p1; // 実体のコピー
p2.age = 35; // p2 のみ変更
printf("p1: %s, %d\n", p1.name, p1.age);
printf("p2: %s, %d\n", p2.name, p2.age);
return 0;
}
実行結果:
p1: Alice, 30
p2: Alice, 35
上記のサンプルでは、
p2 = p1;
によって name と age の全メンバーがまとめてコピーされます。
続いて p2.age を変更しても p1.age は変わらず、独立したインスタンスとして扱われていることが確認できます。
構造体代入は簡潔にデータ複製を実現しますが、ポインタメンバーはアドレスのみ複製される点を覚えておきましょう。
④:実体の比較演算
C言語では構造体同士を == 演算子で直接比較できません。全メンバーを一つずつチェックする自作関数を用意するのが一般的です。
文字列フィールドは strcmp で比較し、整数や文字は単純な等価演算で判定します。
memcmp を使ったバイト単位比較はパディングの影響で誤判定する可能性があるため、信頼性を重視するならメンバー単位の比較が推奨されます。
サンプルコードと実行結果
#include <stdio.h>
#include <string.h>
typedef struct { int x; int y; } Point;
int main(void) {
Point a = {5, 10}, b = {5, 10};
printf("%s\n", (memcmp(&a, &b, sizeof(Point)) == 0) ? "same" : "diff");
return 0;
}
実行結果:
same
この例では
memcmp(&a, &b, sizeof(Point))
で構造体全体をバイト単位で比較しています。同じ値なら 0 を返し「same」が表示されます。
メンバーが整数型だけの構造体ではパディングの影響が少ないですが、文字列やポインタを含む場合は個別比較を使うほうが安全でしょう。
⑤:構造体を関数の引数として使用する
構造体は関数の引数に「値渡し(コピー)」と「参照渡し(ポインタ)」の両方で渡せます。値渡しでは関数内で構造体全体がコピーされるため、元の変数は変更されません。
一方、ポインタを使う「参照渡し」なら、メモリ上の同じインスタンスを操作でき大きな構造体でもコピーコストを抑えられます。
サンプルコードと実行結果
#include <stdio.h>
typedef struct {
char *name;
int age;
} Person;
void update(Person *dest, Person src) {
*dest = src;
}
int main(void) {
Person a = {"Alice", 20};
Person b = {"Bob", 25};
printf("Before: %s is %d\n", a.name, a.age);
update(&a, b);
printf("After : %s is %d\n", a.name, a.age);
return 0;
}
実行結果:
Before: Alice is 20
After : Bob is 25
update 関数は第1引数にポインタを受け取り、第2引数に構造体を値渡しします。呼び出し元では &a を渡すことで a の実体を直接書き換えています。
一方、b はコピーされるだけなので変更されません。
まとめ
この記事ではC言語の構造体の解説、定義や変数宣言、メンバーアクセスといった基本操作を押さえたうえで、初期化やポインタ、コピー、比較、関数引数での利用といった実践的なテクニックを紹介しました。
構造体を使いこなすと、コードの可読性や保守性が高まり、複雑なデータを整理して扱えるようになります。
一見とっつきにくくても、がんばって覚えてしまえば簡単なので、構造体を味方につけてC言語の活用の幅をもっと広げていきましょう。
とはいえ、C言語は他のモダンなWeb系言語に比べると、難易度が高いことも事実。
「まずモダンなWeb系言語でプログラミングの概要をざっと理解してからC言語に挑戦する」という方もいるくらいです。
DMM WEBCAMPでは、モダンなWeb系言語を初心者向けによりわかりやすく解説し、ゼロからプログラミングの概要をきっちり学べるコースを多数取り扱っています。
>>DMM WEBCAMP フロントエンドコースの詳細はこちら
>>DMM WEBCAMP Pythonコースの詳細はこちら
>>DMM WEBCAMP PHP/Laravelコースの詳細はこちら
プログラミングの概要をひととおり理解するのに最適なコースとなっているので、これからC言語を学ぶ方にもおすすめです。
ぜひこの機会に、DMM WEBCAMPをご検討ください!