名前空間
変種
操作

構造体宣言

From cppreference.com
< c‎ | language

構造体(struct)は、メンバーが順序付けられたシーケンスで格納される型です(メンバーの格納領域が重複するunion型とは対照的です)。

型指定子としての構造体は、使用されるキーワードを除き、union 型指定子と同一です。

目次

[編集] 構文

struct attr-spec-seq (任意) name (任意) { struct-declaration-list } (1)
struct attr-spec-seq (任意) name (2)
1) 構造体の定義: 新しい型である構造体 name を導入し、その意味を定義します。
2) struct name ; のように、それ自体の行で使われた場合、構造体 `name` を*宣言*しますが、定義はしません(後述の前方宣言を参照)。他のコンテキストでは、以前に宣言された構造体の名前を指定し、attr-spec-seq は許可されません。
name - 定義されている構造体の名前
struct-declaration-list - 任意の数の変数宣言、ビットフィールド宣言、およびstatic assert宣言。不完全型(incomplete type)のメンバーや関数型のメンバーは許可されません(後述の柔軟な配列メンバーを除く)。
attr-spec-seq - (C23)属性の任意のリスト。構造体型に適用されます。

[編集] 説明

構造体オブジェクト内では、その要素のアドレス(およびビットフィールドの割り当てユニットのアドレス)は、メンバーが定義された順序で増加します。構造体へのポインタは、その最初のメンバー(または、メンバーがビットフィールドの場合、その割り当てユニット)へのポインタにキャストできます。同様に、構造体の最初のメンバーへのポインタは、囲んでいる構造体へのポインタにキャストできます。構造体の2つのメンバー間、または最後のメンバーの後には、名前のないパディングが存在する場合がありますが、最初のメンバーの前には存在しません。構造体のサイズは、そのメンバーのサイズの合計以上です。

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

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と同様に、name を持たない構造体の型である名前のないメンバーは、*匿名構造体*として知られています。匿名構造体のすべてのメンバーは、囲んでいる構造体または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 を宣言します。これは後で定義されます。定義が現れるまで、この構造体名は不完全型となります。

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

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

別の宣言内で構造体タグを使用するだけで新しい構造体名が導入される場合があることに注意してください。しかし、タグ名前空間に同じ名前の以前に宣言された構造体が存在する場合、タグはその名前に参照します。

struct s* p = NULL; // tag naming an unknown struct declares it
struct s { int a; }; // definition for the struct pointed to by p
void g(void)
{
    struct s; // forward declaration of a new, local struct s
              // this hides global struct s until the end of this block
    struct s *p;  // pointer to local struct s
                  // without the forward declaration above,
                  // this would point at the file-scope s
    struct s { char* p; }; // definitions of the local struct s
}

[編集] キーワード

struct

[編集] 注記

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

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

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

[編集]

#include <stddef.h>
#include <stdio.h>
 
int main(void)
{
    // Declare the struct type.
    struct car
    {
        char* make;
        int year;
    };
    // Declare and initialize an object of a previously-declared struct type.
    struct car c = {.year = 1923, .make = "Nash"};
    printf("1) Car: %d %s\n", c.year, c.make);
 
    // Declare a struct type, an object of that type, and a pointer to it.
    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);
 
    // Address increase in order of definition. Padding may be inserted.
    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)
    );
 
    // A pointer to a struct can be cast to a pointer
    // to its first member and vice versa.
    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 匿名構造体/union のメンバーは、囲んでいる構造体/union のメンバーとみなされていました。 それらはメモリレイアウトを保持します。

[編集] 参照

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

[編集] 関連項目

C++ドキュメントクラス宣言
English 日本語 中文(简体) 中文(繁體)