Cプログラミング
 Computer >> コンピューター >  >> プログラミング >> Cプログラミング

C言語の構造体(struct)入門|宣言・初期化・アクセス方法をサンプルコードで解説

構造体(structure)とは、異なるデータ型を持つ複数の変数を、ひとつの名前でまとめて扱えるようにしたデータ構造です。関連する情報(たとえば本のページ数・著者名・価格など)をひとまとめに管理できるため、C言語プログラミングにおいて非常に重要な概念となっています。

構造体の宣言と初期化

構造体を宣言する一般的な書式は以下の通りです。

struct tagname {
    datatype member1;
    datatype member2;
    ...
    datatype member n;
};

各要素の意味は次の通りです。

  • struct … 構造体を定義するためのキーワード
  • tagname … 構造体につける名前(タグ名)
  • member1, member2, ... … 構造体を構成する各データ項目(メンバ)

記述例

struct book {
    int pages;        /* ページ数 */
    char author[30];  /* 著者名 */
    float price;      /* 価格 */
};

この例では、「book」という名前の構造体に、int型・char型・float型という異なる型のメンバが含まれています。

構造体変数の宣言方法

構造体変数を宣言するには、主に以下の3つの方法があります。

方法1:宣言時に変数を同時に定義する

struct book {
    int pages;
    char author[30];
    float price;
} b;

方法2:タグ名なしで変数を定義する

struct {
    int pages;
    char author[30];
    float price;
} b;

※この場合、同じ構造を後から再利用できない点に注意してください。

方法3:先に型を定義し、後から変数を宣言する

struct book {
    int pages;
    char author[30];
    float price;
};

struct book b;

実務では、再利用性が高い「方法3」が最も一般的に使われています。

構造体の初期化とメンバへのアクセス

  • 構造体変数とメンバをつなぐには、メンバ演算子(ドット演算子「.」)を使用します。
  • 初期化の方法は複数あり、目的に応じて使い分けられます。

方法1:宣言と同時に初期化する

struct book {
    int pages;
    char author[30];
    float price;
} b = {100, "balu", 325.75};

方法2:タグ名を使って別途初期化する

struct book {
    int pages;
    char author[30];
    float price;
};

struct book b = {100, "balu", 325.75};

方法3:メンバ演算子を使って個別に代入する

#include <string.h>

struct book {
    int pages;
    char author[30];
    float price;
};

struct book b;
b.pages = 100;
strcpy(b.author, "balu");   /* 文字列配列にはstrcpyを使用 */
b.price = 325.75;

※文字配列への代入は「=」では行えないため、string.h の strcpy 関数を使います。

方法4:scanf関数を使ってユーザー入力から設定する

struct book {
    int pages;
    char author[30];
    float price;
};

struct book b;
scanf("%d", &b.pages);
scanf("%s", b.author);
scanf("%f", &b.price);

これらの構造体の中身は、main関数内で次のように出力できます。

main() {
    struct book b;
    clrscr();
    printf("ページ数・著者名・価格を入力してください\n");
    scanf("%d%s%f", &b.pages, b.author, &b.price);
    printf("本の詳細\n");
    printf("pages = %d, author = %s, price = %f",
            b.pages, b.author, b.price);
    getch();
}

※ scanf の文字列読み込み時のみ、配列名自体が先頭アドレスを表すため「&」は不要です。

応用例:ポインタを使った自己参照構造体

次の例は、自分自身と同じ型へのポインタ(prev / next)を持つ自己参照構造体のサンプルです。双方向リンクリストなどの基礎となる重要なテクニックです。

#include <stdio.h>

struct aaa {
    struct aaa *prev;
    int i;
    struct aaa *next;
};

main() {
    struct aaa abc, def, ghi, jkl;
    int x = 100;

    abc.i = 0;  abc.prev = &jkl;  abc.next = &def;
    def.i = 1;  def.prev = &abc;  def.next = &ghi;
    ghi.i = 2;  ghi.prev = &def;  ghi.next = &jkl;
    jkl.i = 3;  jkl.prev = &ghi;  jkl.next = &abc;

    x = abc.next->next->prev->next->i;
    printf("%d", x);
}

実行結果

2

このプログラムでは、4つの構造体変数が prev と next ポインタで循環的に連結されています。abc.next->next->prev->next->i を順にたどると、abc → def → ghi → def → ghi となり、最終的に ghi のメンバ i の値である 2 が出力されます。ポインタとドット演算子(.)、アロー演算子(->)の使い分けを理解する良い練習例といえるでしょう。

  1. 構造体の概念で理解するC言語のビットフィールド|定義方法と範囲の計算を徹底解説

    ビットフィールドとはビットフィールドとは、変数が占めるメモリのサイズをビット単位で指定できるC言語の機能です。通常は構造体(struct)の中で定義されます。ビットフィールドの基本:1バイト=8ビット記述例struct info { int x : 2; };この例では、メンバxは2ビットを占有します。ビットフィールド使用時の注意点ビットフィールドの範囲外の値を代入することはできません(その場合の動作は保証されません)。sizeof演算子やアドレス演算子(&)をビットフィールドに適用できないため、scanf関数で値を入力することもできません。ビットフィールドに指定できるデータ型

  2. C言語でfwrite()/fread()を使って構造体をファイルに読み書きする方法

    C言語では、標準ライブラリのfwrite()関数とfread()関数を使うことで、構造体(struct)のデータをそのままファイルへ書き込んだり、ファイルから読み込んだりできます。これらの関数はデータをバイナリ形式で扱うため、構造体内の値を効率よく入出力できるのが大きな特徴です。 fwrite() の構文 fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) 各引数の意味は次の通りです。 ptr … 書き込む要素が格納された配列へのポインタ size … 書き込む各要素のサイズ(バイト単位) nmemb … 書き込