Struct declaration
structは、メンバーのストレージが順序付けられたシーケンスで割り当てられる型です(これに対し、unionはメンバーのストレージがオーバーラップする型です)。
型指定子
のstructは、
union
型指定子と同一ですが、使用されるキーワードが異なります:
目次 |
構文
struct
attr-spec-seq
(オプション)
name
(オプション)
{
struct-declaration-list
}
|
(1) | ||||||||
struct
attr-spec-seq
(オプション)
name
|
(2) | ||||||||
struct
name
;
のように記述すると、構造体
name
を
宣言
しますが定義はしません(後述の前方宣言を参照)。他の文脈では、以前に宣言された構造体を参照し、この場合
attr-spec-seq
は許可されません。
| name | - | 定義されている構造体の名前 |
| struct-declaration-list | - | 任意の数の変数宣言、 bit-field 宣言、および static assert 宣言。不完全型のメンバーおよび関数型のメンバーは許可されない(後述するフレキシブル配列メンバーを除く) |
| attr-spec-seq | - | (C23) オプションの attributes リスト、構造体型に適用される |
説明
構造体オブジェクト内では、その要素のアドレス(およびビットフィールド割り当て単位のアドレス)は、メンバが定義された順序で増加します。構造体へのポインタは、その最初のメンバへのポインタにキャストできます(または、メンバがビットフィールドの場合、その割り当て単位へのポインタにキャストできます)。同様に、構造体の最初のメンバへのポインタは、それを包含する構造体へのポインタにキャストできます。構造体の任意の2つのメンバの間、または最後のメンバの後に無名のパディングが存在する可能性がありますが、最初のメンバの前には存在しません。構造体のサイズは、少なくともそのメンバのサイズの合計と同じ大きさです。
|
構造体が少なくとも1つの名前付きメンバを定義する場合、その最後のメンバを不完全な配列型で追加的に宣言することが許可される。フレキシブル配列メンバの要素がアクセスされるとき(演算子
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 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
クラス宣言
|