Namespaces
Variants

Struct declaration

From cppreference.net

structは、メンバーのストレージが順序付けられたシーケンスで割り当てられる型です(これに対し、unionはメンバーのストレージがオーバーラップする型です)。

型指定子 のstructは、 union 型指定子と同一ですが、使用されるキーワードが異なります:

目次

翻訳の説明: - 「Contents」を「目次」に翻訳しました - C++関連の専門用語(Syntax、Explanation、Forward declaration、Keywords、Notes、Example、Defect reports、References、See also)は原文のまま保持しました - HTMLタグ、属性、クラス名、IDなどは一切変更していません - 番号部分もそのまま保持しています - フォーマットと構造は完全に元のまま維持しています

構文

struct attr-spec-seq  (オプション) name  (オプション) { struct-declaration-list } (1)
struct attr-spec-seq  (オプション) name (2)
1) 構造体定義: 新しい型 struct name を導入し、その意味を定義する
2) 単独の行で使用される場合、例えば struct name ; のように記述すると、構造体 name 宣言 しますが定義はしません(後述の前方宣言を参照)。他の文脈では、以前に宣言された構造体を参照し、この場合 attr-spec-seq は許可されません。
name - 定義されている構造体の名前
struct-declaration-list - 任意の数の変数宣言、 bit-field 宣言、および static assert 宣言。不完全型のメンバーおよび関数型のメンバーは許可されない(後述するフレキシブル配列メンバーを除く)
attr-spec-seq - (C23) オプションの attributes リスト、構造体型に適用される

説明

構造体オブジェクト内では、その要素のアドレス(およびビットフィールド割り当て単位のアドレス)は、メンバが定義された順序で増加します。構造体へのポインタは、その最初のメンバへのポインタにキャストできます(または、メンバがビットフィールドの場合、その割り当て単位へのポインタにキャストできます)。同様に、構造体の最初のメンバへのポインタは、それを包含する構造体へのポインタにキャストできます。構造体の任意の2つのメンバの間、または最後のメンバの後に無名のパディングが存在する可能性がありますが、最初のメンバの前には存在しません。構造体のサイズは、少なくともそのメンバのサイズの合計と同じ大きさです。

構造体が少なくとも1つの名前付きメンバを定義する場合、その最後のメンバを不完全な配列型で追加的に宣言することが許可される。フレキシブル配列メンバの要素がアクセスされるとき(演算子 . または -> をフレキシブル配列メンバの名前を右辺オペランドとして使用する式において)、その構造体は、そのオブジェクトに割り当てられたメモリに収まる最大のサイズで配列メンバが存在するかのように振る舞う。追加のストレージが割り当てられていない場合、1要素の配列であるかのように振る舞うが、その要素がアクセスされたり、その要素の1つ後ろのポインタが生成された場合の動作は未定義である。初期化と代入演算子はフレキシブル配列メンバを無視する。 sizeof はこれを省略するが、省略によって示唆される以上のトレーリングパディングが存在する可能性がある。フレキシブル配列メンバを持つ構造体(またはフレキシブル配列メンバを持つ再帰的可能性のある構造体メンバを持つ共用体)は、配列要素や他の構造体のメンバとして現れることはできない。

struct s { int n; double d[]; }; // s.d is a flexible array member
struct s t1 = { 0 };          // OK, d is as if double d[1], but UB to access
struct s t2 = { 1, { 4.2 } }; // error: initialization ignores flexible array
// if sizeof (double) == 8
struct s *s1 = malloc(sizeof (struct s) + 64); // as if d was double d[8]
struct s *s2 = malloc(sizeof (struct s) + 40); // as if d was double d[5]
s1 = malloc(sizeof (struct s) + 10); // now as if d was double d[1]. Two bytes excess.
double *dp = &(s1->d[0]);    // OK
*dp = 42;                    // OK
s1->d[1]++;                  // Undefined behavior. 2 excess bytes can't be accessed
                             // as double.
s2 = malloc(sizeof (struct s) + 6);  // same, but UB to access because 2 bytes are
                                     // missing to complete 1 double
dp = &(s2->d[0]);            // OK, can take address just fine
*dp = 42;                    // undefined behavior
*s1 = *s2; // only copies s.n, not any element of s.d
           // except those caught in sizeof (struct s)
(C99以降)

unionと同様に、名前のない構造体型の構造体の無名メンバは、 無名構造体 として知られています。無名構造体のすべてのメンバは、その構造レイアウトを維持したまま、外側の構造体またはunionのメンバと見なされます。これは、外側の構造体またはunionも無名である場合、再帰的に適用されます。

struct v
{
   union // anonymous union
   {
      struct { int i, j; }; // anonymous structure
      struct { long k, l; } w;
   };
   int m;
} v1;
v1.i = 2;   // valid
v1.k = 3;   // invalid: inner structure is not anonymous
v1.w.k = 5; // valid

unionと同様に、名前付きメンバ(無名のネストされた構造体またはunionを通じて取得されたものを含む)なしで構造体が定義された場合、プログラムの動作は未定義です。

(C11以降)

前方宣言

以下の形式の宣言

struct attr-spec-seq  (オプション) name ;

以前に宣言されたタグ名前空間における名前 name の意味を隠蔽し、後で定義される新しい構造体名として現在のスコープで name を宣言します。定義が現れるまで、この構造体名は incomplete type を持ちます。

これにより、相互に参照する構造体が可能になります:

struct y;
struct x { struct y *p; /* ... */ };
struct y { struct x *q; /* ... */ };

新しい構造体名は、他の宣言内で構造体タグを使用するだけで導入される場合もあるが、同じ名前の構造体が以前にタグ name space で宣言されている場合、そのタグはその名前を参照することになる

struct s* p = NULL; // 未知の構造体を参照するタグはそれを宣言する
struct s { int a; }; // pが指す構造体の定義
void g(void)
{
    struct s; // 新しいローカル構造体sの前方宣言
              // これはこのブロックの終わりまでグローバルな構造体sを隠蔽する
    struct s *p;  // ローカル構造体sへのポインタ
                  // 上記の前方宣言がなければ、
                  // これはファイルスコープのsを指すことになる
    struct s { char* p; }; // ローカル構造体sの定義
}

キーワード

struct

注記

構造体の初期化子に関する規則については、 struct initialization を参照してください。

不完全型のメンバを持つことは許可されていないため、構造体型は定義の終端まで不完全型となります。したがって、構造体は自身の型のメンバを持つことはできません。自身の型へのポインタは許可されており、リンクリストやツリーのノードを実装する際に一般的に使用されます。

struct宣言は scope を確立しないため、 struct-declaration-list 内の宣言によって導入されたネストされた型、列挙型および列挙子は、structが定義されている周囲のscopeで可視となります。

#include <stddef.h>
#include <stdio.h>
int main(void)
{
    // 構造体型を宣言
    struct car
    {
        char* make;
        int year;
    };
    // 事前に宣言された構造体型のオブジェクトを宣言・初期化
    struct car c = {.year = 1923, .make = "Nash"};
    printf("1) Car: %d %s\n", c.year, c.make);
    // 構造体型、その型のオブジェクト、およびそのポインタを宣言
    struct spaceship
    {
        char* model;
        int max_speed;
    } ship = {"T-65 X-wing starfighter", 1050},
    *pship = &ship;
    printf("2) Spaceship: %s. Max speed: %d km/h\n\n", ship.model, ship.max_speed);
    // アドレスは定義順に増加。パディングが挿入される可能性あり
    struct A { char a; double b; char c; };
    printf(
        "3) Offset of char a = %zu\n"
        "4) Offset of double b = %zu\n"
        "5) Offset of char c = %zu\n"
        "6) Size of struct A = %zu\n\n",
        offsetof(struct A, a),
        offsetof(struct A, b),
        offsetof(struct A, c),
        sizeof(struct A)
    );
    struct B { char a; char b; double c; };
    printf(
        "7) Offset of char a = %zu\n"
        "8) Offset of char b = %zu\n"
        "9) Offset of double c = %zu\n"
        "A) Size of struct B = %zu\n\n",
        offsetof(struct B, a),
        offsetof(struct B, b),
        offsetof(struct B, c),
        sizeof(struct B)
    );
    // 構造体へのポインタは最初のメンバへのポインタにキャスト可能(逆も同様)
    char** pmodel = (char **)pship;
    printf("B) %s\n", *pmodel);
    pship = (struct spaceship *)pmodel;
}

出力例:

1) Car: 1923 Nash
2) Spaceship: T-65 X-wing starfighter. Max speed: 1050 km/h
3) Offset of char a = 0
4) Offset of double b = 8
5) Offset of char c = 16
6) Size of struct A = 24
7) Offset of char a = 0
8) Offset of char b = 1
9) Offset of double c = 8
A) Size of struct B = 16
B) T-65 X-wing starfighter

不具合報告

以下の動作変更に関する不具合報告は、以前に公開されたC規格に対して遡及的に適用されました。

DR 適用対象 公開時の動作 正しい動作
DR 499 C11 無名構造体/共用体のメンバーは、外側の構造体/共用体のメンバーと見なされていた それらはメモリレイアウトを保持する

参考文献

  • C23規格 (ISO/IEC 9899:2024):
  • 6.7.2.1 構造体及び共用体指定子 (p: 未定)
  • C17規格 (ISO/IEC 9899:2018):
  • 6.7.2.1 構造体および共用体指定子 (p: 81-84)
  • C11規格 (ISO/IEC 9899:2011):
  • 6.7.2.1 構造体および共用体指定子 (p: 112-117)
  • C99規格 (ISO/IEC 9899:1999):
  • 6.7.2.1 構造体および共用体指定子 (p: 101-104)
  • C89/C90標準 (ISO/IEC 9899:1990):
  • 3.5.2.1 構造体および共用体指定子

関連項目

C++ ドキュメント for クラス宣言