名前空間
変種
操作

構造体宣言

提供: cppreference.com
< c‎ | language

構造体は、記憶域が順番通りに連続的に確保される、一連のメンバから構成される型です (記憶域がオーバーラップする一連のメンバから構成される型である共用体とは対照的です)。

構造体のための型指定子は、使用されるキーワード以外は、共用体の型指定子と同じです。

目次

[編集] 構文

struct name(オプション) { struct-declaration-list } (1)
struct name (2)
1) 構造体の定義。 新しい構造体型 name を導入し、その意味を定義します。
2) struct name ; のように独立した宣言として使用された場合は、構造体 name宣言しますが、定義しません (下の前方宣言を参照してください)。 それ以外の文脈では、以前に宣言された構造体を表します。
name - 定義中の構造体の名前。
struct-declaration-list - 任意個の変数宣言、ビットフィールド宣言、および static assert 宣言。 不完全型のメンバおよび関数型のメンバは使用できません (下で説明するフレキシブル配列メンバの場合は除きます)。

[編集] 説明

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

構造体が名前付きのメンバを少なくともひとつ定義する場合は、その最後のメンバを不完全配列型で宣言することができます (フレキシブル配列メンバと言います)。 フレキシブル配列メンバの要素がアクセスされたとき (フレキシブル配列メンバの名前を右側の被演算子として . または -> 演算子を使用したとき)、その構造体は、その配列メンバがそのオブジェクトのために確保されたメモリに収まる最も大きなサイズを持つかのように振る舞います。 追加の記憶域が確保されなかった場合は、1個の要素を持つ配列であるかのように振る舞いますが、ただし、その要素にアクセスした場合、またはその要素の次の要素へのポインタを生成した場合、動作は未定義です。 初期化、 sizeof、および代入演算子は、フレキシブル配列メンバを無視します。 フレキシブル配列メンバを持つ構造体 (またはフレキシブル配列メンバを持つ構造体メンバを持つ共用体 (再帰的に持つ場合も含みます)) は、配列要素として、または他の構造体の要素として使用することはできません。

struct s { int n; double d[]; }; // s.d はフレキシブル配列メンバです。 
 
    struct s t1 = { 0 };         // OK、 t1.d は double[1] であるかのように振る舞います (ただし要素にアクセスすると未定義動作です)。
    struct s t2 = { 1, { 4.2 } }; // エラー、初期化はフレキシブル配列メンバを無視します。
 
    // sizeof (double) == 8 の場合
    struct s *s1 = malloc(sizeof (struct s) + 64); // s1->d は double[8] であるかのように振る舞います。
    struct s *s2 = malloc(sizeof (struct s) + 40); // s2->d は double[5] であるかのように振る舞います。
 
    s1 = malloc(sizeof (struct s) + 10); // 以降 s1->d は double[1] であるかのように振る舞います。 2バイト余っています。
    double *dp = &(s1->d[0]);    // OK。
    *dp = 42;                    // OK。
    s1->d[1]++;                  // 未定義動作。 余りの2バイトは
                                 // double としてアクセスできません。
 
    s2 = malloc(sizeof (struct s) + 6);  // 以降 s2->d は double[1] であるかのように振る舞いますが、
                                         // 2バイトは完全な double 1個には足りないため、アクセスは未定義動作です。
    dp = &(s2->d[0]);            // OK、アドレスを取ることは問題ありません。
    *dp = 42;                    // 未定義動作。
 
    *s1 = *s2; // s.n のみがコピーされます。 s.d の要素はコピーされません。
               // ただし sizeof (struct s) には s.d の分が含まれます。
(C99以上)

共用体と同様に、 name を持たない構造体型のメンバは、無名構造体と言います。 無名構造体のすべてのメンバは、その囲っている構造体または共用体のメンバとみなされます。 囲っている構造体または共用体も無名であれば、これは再帰的に適用されます。

struct v {
   union { // 無名共用体
      struct { int i, j; }; // 無名構造体
      struct { long k, l; } w;
   };
   int m;
} v1;
 
v1.i = 2;   // 有効。
v1.k = 3;   // 無効。 内側の構造体が無名ではありません。
v1.w.k = 5; // 有効。

共用体と同様に、名前付きのメンバ (ネストした無名構造体または無名共用体から取得したものを含みます) を一切持たずに構造体が定義された場合、プログラムの動作は未定義です。

(C11以上)

[編集] 前方宣言

以下の形式の宣言は、

struct name ;

タグ名前空間内の名前 name に対するそれまでに宣言されたあらゆる意味を隠蔽し、後に定義されるであろう新しい構造体の名前として、 name を現在のスコープに宣言します。 その定義が現れるまでは、その構造体の名前は不完全型です。

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

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

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

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-declaration-list 内の宣言によって導入されたネストした型、列挙、および列挙子は、その構造体が定義された周りのスコープでも可視です。

[編集]

#include <stddef.h>
#include <stdio.h>
 
int main(void)
{
    struct car { char *make; char *model; int year; }; // 構造体型を宣言します。
    // 上で宣言した構造体型のオブジェクトを宣言し、初期化します。
    struct car c = {.year=1923, .make="Nash", .model="48 Sports Touring Car"};
    printf("car: %d %s %s\n", c.year, c.make, c.model);
 
    // 構造体型、その型のオブジェクト、およびそれへのポインタを宣言します。
    struct spaceship { char *make; char *model; char *year; }
        ship = {"Incom Corporation", "T-65 X-wing starfighter", "128 ABY"},
        *pship = &ship;
    printf("spaceship: %s %s %s\n", ship.year, ship.make, ship.model);
 
    // アドレスは定義の順序で増加します。
    // パディングが挿入されることがあります。
    struct A { char a; double b; char c;};
    printf("offset of char a = %zu\noffset of double b = %zu\noffset of char c = %zu\n"
           "sizeof(struct A)=%zu\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("offset of char a = %zu\noffset of char b = %zu\noffset of double c = %zu\n"
           "sizeof(struct B)=%zu\n", offsetof(struct B, a), offsetof(struct B, b),
           offsetof(struct B, c), sizeof(struct B));
 
    // 構造体へのポインタはその最初のメンバへのポインタにキャストできます (逆もできます)。
    char* pmake = (char*)&ship;
    pship = (struct spaceship *)pmake;
}

出力例:

car: 1923 Nash 48 Sports Touring Car
spaceship: 128 ABY Incom Corporation T-65 X-wing starfighter
offset of char a = 0
offset of double b = 8
offset of char c = 16
sizeof(struct A)=24
offset of char a = 0
offset of char b = 1
offset of double c = 8
sizeof(struct B)=16

[編集] 参考文献

  • C11 standard (ISO/IEC 9899:2011):
  • 6.7.2.1 Structure and union specifiers (p: 112-117)
  • C99 standard (ISO/IEC 9899:1999):
  • 6.7.2.1 Structure and union specifiers (p: 101-104)
  • C89/C90 standard (ISO/IEC 9899:1990):
  • 3.5.2.1 Structure and union specifiers

[編集] 関連項目

クラス宣言C++リファレンス