閉じる

【C言語】構造体のメンバーにアクセスする方法 ドット演算子入門

C言語の構造体は、複数の関連する値を1つにまとめて扱える便利な仕組みです。

本記事では構造体のメンバーにアクセスする方法に焦点を当て、ドット演算子(.)の基本から、ネストした構造体、配列との組み合わせまでを丁寧に解説します。

よくある間違いと避け方、読みやすく書くコツも合わせて学び、実用的なコードが書けるようになりましょう。

構造体メンバーアクセスの基本

構造体(struct)とメンバーの関係

構造体(struct)は、異なる型の値をひとまとまりにしたユーザー定義の型です。

たとえば、人を表す構造体には名前、年齢、身長といったメンバー(フィールド)を定義できます。

各メンバーは、構造体変数からメンバー名でアクセスします。

C言語
// 人を表す構造体の定義例
struct Person {
    char name[32];  // 名前(文字配列)
    int age;        // 年齢
    double height;  // 身長(cm)
};

構造体は「設計図」、実際に使うときは変数を宣言して値を入れます。

メンバーにアクセスするための基本が次のドット演算子です。

メンバーアクセスはドット(.)で行う

構造体変数のメンバーにアクセスするときは、ドット(.)を使います

書式は変数.メンバー名です。

ネストしている場合は変数.サブ構造体.メンバー名のようにドットを重ねます。

以下はよく使う書き方の早見表です。

対象記法
構造体変数のメンバー変数.メンバーp.age
配列要素のメンバー配列[i].メンバーpeople[0].height
ネストしたメンバー変数.サブ.メンバーp.address.city
参考: 構造体ポインタ(詳細は別記事)(*ptr).メンバー または ptr->メンバー(*pp).x

ポインタに対してドットは使えません

ポインタの場合は(*ptr).memberのように間接参照してからドット、または->演算子を使います(アロー演算子の詳細は別記事で扱います)。

ドット演算子(.)の使い方

基本の書き方と例

まずはドット演算子でメンバーに値を代入し、読み取る基本例です。

C言語
#include <stdio.h>
#include <string.h> // 文字配列に文字列をコピーするため

// 構造体の定義
struct Person {
    char name[32];
    int age;
    double height;
};

int main(void) {
    struct Person p;             // 構造体変数を宣言
    strcpy(p.name, "Alice");     // 文字配列には文字列コピー関数を使う
    p.age = 20;                  // 数値は代入可能
    p.height = 165.5;

    // ドットで参照して出力
    printf("Name: %s\n", p.name);
    printf("Age : %d\n", p.age);
    printf("Height(cm): %.1f\n", p.height);
    return 0;
}
実行結果
Name: Alice
Age : 20
Height(cm): 165.5

メンバーの値の読み取りも、代入も、すべて変数.メンバーで行います

文字配列(例えばchar name[32])へは文字列リテラルを直接代入できないため、strcpysnprintfなどの関数を使います。

メンバーの読み取りと代入

ドット演算子は「読み」と「書き」のどちらにも使えます。

読み取った値を別の計算に使うことも自然です。

C言語
#include <stdio.h>
#include <string.h>

struct Person {
    char name[32];
    int age;
    double height;
};

int main(void) {
    struct Person p;
    strcpy(p.name, "Bob");
    p.age = 29;
    p.height = 172.4;

    // 読み取り: メンバーの値を他の変数へ
    int next_age = p.age + 1; // 来年の年齢
    double taller = p.height + 2.0; // 2cmヒールを履いた想定

    // 書き込み: メンバーへ新しい値を代入
    p.age = next_age;

    printf("%s is now %d years old.\n", p.name, p.age);
    printf("With heels: %.1f cm\n", taller);
    return 0;
}
実行結果
Bob is now 30 years old.
With heels: 174.4 cm

ドット演算子は式の一部として自由に使えるため、計算や条件分岐の中でも活躍します。

ネストした構造体のメンバーにアクセス

構造体のメンバーに別の構造体を入れることができます。

この場合、変数.サブ構造体.メンバーのようにドットをつなげて参照します。

C言語
#include <stdio.h>
#include <string.h>

// 住所を表す構造体
struct Address {
    char prefecture[16];
    char city[16];
    char street[32];
};

// 人を表す構造体(住所を内包)
struct Person {
    char name[32];
    int age;
    struct Address address; // ネストした構造体
};

int main(void) {
    struct Person p;

    // 各メンバーに代入
    strcpy(p.name, "Carol");
    p.age = 35;

    // ネストした構造体の各メンバーへアクセス
    strcpy(p.address.prefecture, "Tokyo");
    strcpy(p.address.city, "Chiyoda");
    strcpy(p.address.street, "1-1-1");

    // ネストの参照
    printf("%s (%d)\n", p.name, p.age);
    printf("Address: %s, %s, %s\n",
           p.address.prefecture,
           p.address.city,
           p.address.street);
    return 0;
}
実行結果
Carol (35)
Address: Tokyo, Chiyoda, 1-1-1

ネストが深いほどドットが増えますが、基本は同じです

必要に応じて一時変数を使うと読みやすくなります(後述)。

構造体配列からメンバーにアクセス

複数人の情報を扱うなら、構造体の配列が便利です。

配列要素をpeople[i]と書けるので、people[i].memberでアクセスします。

C言語
#include <stdio.h>
#include <string.h>

struct Person {
    char name[32];
    int age;
    double height;
};

int main(void) {
    // 初期化子で配列を用意
    struct Person people[3];

    strcpy(people[0].name, "Dan");
    people[0].age = 18;
    people[0].height = 168.2;

    strcpy(people[1].name, "Eve");
    people[1].age = 22;
    people[1].height = 159.7;

    strcpy(people[2].name, "Frank");
    people[2].age = 27;
    people[2].height = 180.3;

    // ループで順に参照
    for (int i = 0; i < 3; i++) {
        printf("%d: %s, %d, %.1f cm\n",
               i, people[i].name, people[i].age, people[i].height);
    }

    return 0;
}
実行結果
0: Dan, 18, 168.2 cm
1: Eve, 22, 159.7 cm
2: Frank, 27, 180.3 cm

配列のインデックスで要素を選び、その後にドットでメンバーへアクセスするのが基本です。

構造体メンバーアクセスのよくある間違い

ポインタにはドットは使えない

構造体ポインタに対してドット.は使えません

ポインタなら(*ptr).memberのように間接参照してからドットを使うか、->演算子を使います(アロー演算子の詳細は別記事で解説します)。

間違いの例(コンパイルエラー)

C言語
#include <stdio.h>

struct Point { int x; int y; };

int main(void) {
    struct Point p = { 10, 20 };
    struct Point *pp = &p;

    // 間違い: ポインタにドットは使えない
    printf("x=%d\n", pp.x); // ここでコンパイルエラー
    return 0;
}

想定されるエラーメッセージ(GCCの一例)

実行結果
error: request for member 'x' in something not a structure or union

正しい書き方(この章ではドットに統一)

C言語
#include <stdio.h>

struct Point { int x; int y; };

int main(void) {
    struct Point p = { 10, 20 };
    struct Point *pp = &p;

    // ポインタを間接参照してからドットでアクセス
    printf("x=%d, y=%d\n", (*pp).x, (*pp).y);
    return 0;
}
実行結果
x=10, y=20

メンバー名のスペルミスによるコンパイルエラー

メンバー名は定義どおりでなければなりません。

ageと定義したのにagesと書くとエラーです。

C言語
// 定義
struct Person { int age; };

// 間違い: スペルミス
// p.ages のように存在しないメンバーへアクセスするとコンパイルエラー

想定されるエラーメッセージ(GCCの一例)

error: 'struct Person' has no member named 'ages'

定義と使用箇所のスペルを統一し、補完機能のあるエディタでミスを減らすのが有効です。

未初期化のままアクセスしない

初期化していない構造体のメンバー値は不定です。

未初期化のまま読み取ると未定義動作になります。

悪い例(実行しない)

C言語
#include <stdio.h>

struct S { int a; double b; };

int main(void) {
    struct S s; // 未初期化
    // 未定義動作: 値は不定
    printf("%d, %f\n", s.a, s.b);
    return 0;
}

良い例(ゼロ初期化や明示的な代入)

C言語
#include <stdio.h>

struct S { int a; double b; };

int main(void) {
    struct S s = {0}; // すべて0で初期化
    s.a = 100;        // 必要に応じて代入
    s.b = 3.14;

    printf("%d, %.2f\n", s.a, s.b);
    return 0;
}
実行結果
100, 3.14

構造体変数を使う前に、必ず初期化または必要なメンバーへ代入してから読むようにしましょう。

構造体メンバーアクセスのコツ

名前付けで意図を明確にする

読みやすさはバグの予防になります。

構造体名、メンバー名、変数名は役割が伝わる名前にすると、アクセスするコードも自然に理解できます。

  • 構造体名は名詞で上位概念を表す(例: Person, Address)
  • メンバー名は具体的で短く、必要なら下線区切りを使う(例: birth_year, postal_code)
  • 配列には複数形や集合名(例: people, employees)

命名の指針を簡単にまとめます。

項目推奨例説明
構造体名Person対象物を表す名詞を使う
メンバー名age, postal_code短く具体的、下線で可読性を高める
変数名person, people単数と複数で役割を区別する

長い式は一時変数で読みやすくする

ネストや配列が重なると、ドットによるアクセスが長くなります。

一時変数に取り出してから使うと理解しやすく、デバッグもしやすいです。

before: 1行が長い

C言語
double avg = (people[0].height + people[1].height + people[2].height) / 3.0;

after: 一時変数で分解

C言語
double h0 = people[0].height;
double h1 = people[1].height;
double h2 = people[2].height;
double avg = (h0 + h1 + h2) / 3.0;

同様に、ネストした構造体も一段取り出すと分かりやすくなります。

C言語
struct Address addr = person.address; // ネストを一時変数へ
printf("%s, %s, %s\n", addr.prefecture, addr.city, addr.street);

「ドットの回数が増えて読みにくい」と感じたら、一時変数を検討するのがコツです。

まとめ

本記事では、構造体のメンバーにアクセスするにはドット演算子(.)を使うという基本を、配列やネストした構造体まで含めて解説しました。

ポインタにドットは使えない点と、未初期化の読み取りは未定義動作である点は必ず押さえましょう。

命名と一時変数の活用は、読みやすく安全なコードづくりに直結します。

ドット演算子を確実に使いこなし、より大きなプログラムでも迷わずメンバーへアクセスできるように練習してみてください。

アロー演算子(->)や関数と構造体の連携などの発展的な話題は、別記事で詳しく解説します。

この記事を書いた人
エーテリア編集部
エーテリア編集部

プログラミングの基礎をしっかり学びたい方向けに、C言語の基本文法から解説しています。ポインタやメモリ管理も少しずつ理解できるよう工夫しています。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!