構造とユニオン


TOC
  • Struct vs Union

  • Components
  • Type-specifier
  • Identifier
  • Declaration-list or Members
  • Tags
  • Alias
  • Struct, Union, Members and Semicolon
  • Declaring and Defining a Struct and Union
  • Declaring Variable and Accessing the Content
  • Size of a Struct and Union
  • Anonymous Struct and Union [C11]
  • Bit-field

  • 構造vsユニオン

    a structure is a type consisting of a sequence of members, whose storage is allocated in an ordered sequence, and a union is a type consisting of a sequence of members whose storage overlap.

    N1570 - 6.7.2.1

    The word structure refers to struct , union is not a structure because, during runtime, it only holds a value of one member at a time.


    コンポーネント

    Type-specifier Identifier { Declaration-list } Tags ;

    typedef Type-specifier Identifier { Declaration-list } Alias ;


    型指定子

    Keywords struct or union .


    識別子

    The name given to a struct or union , this is not a variable, its just a name of the model:

    struct s { int x; }; // 's' is the identifier
    union u { int x; };  // 'u' is the identifier
    

    The identifier is optional if the struct or union have 1 or more tags:

    // No identifier
    struct { int x; } z; // 'z' is a tag
    union { int x; } w; // 'w' is a tag
    

    No identifier for anonymous struct [C11] or anonymous union [C11].

    struct a { // Regular struct, has identifier 'a'
      struct { int x; }; // Anonymous struct, no identifier and no tag
      union { int y; }; // Anonymous union, no identifier and no tag
    };
    

    宣言リスト Can be one or more, same or different types, plus bit-fields .
    メンバーは巻き毛括弧で区切られています:左の巻き毛ブラケット{ 初めに、右のカーリーブラケット} 最後に.
    同じ型の複数のメンバーを一緒に宣言することができます.
    struct s { int x, y, z; };
    union u { int x, y, z; };
    
    エーstruct or union 自分自身のインスタンスを持つことはできませんが、ポインタ自体が有効です.例、データ構造:リスト、リンクされたリストとより多く:
    // 💩
    struct s { struct s z; };  // instance of itself
    
    // 👍
    struct s { struct s *z; }; // pointer to itself
    
    // 👍
    struct Node {
      int value;
      struct Node *next;       // pointer to itself
      struct Node *prev;       // pointer to itself
    };
    
    For struct のみ.メンバーとして有効な唯一の不完全型は、柔軟な配列型であり、最後のメンバーでなければなりません.それは、最後の、そして、唯一のメンバーでありえませんstruct 柔軟な配列の前に少なくとも1つの完全な型を持たなければなりません.すべてのコストでVLAのを避ける.
    // 👍
    struct s {
      int x;
      int z[];
    };
    
    // 💩
    struct s {
      int z[]; // Flexible array is not the last member
      int x;
    };
    
    // 💩
    struct s {
      int z[]; // Flexible array is the last member, but is the only member
    };
    

    タグ

    Optional. None, one or many tags separated by comma , . But if you have a typedef the tag part is not a tag anymore, it is an alias for that type.

    struct { int x; } x, y; // 'x' and 'y' are tags
    union { int x; } z, w;  // 'z' and 'w' are tags
    

    ⚠️ [C11] An anonymous struct or anonymous union does not have tag.


    別名

    Exist only if a typedef is used, means that a new name for this boring_name is an awesome_name . Using typedef in the same line if you have tags, the tags part are not tags anymore, instead they are alias an behave different than a tag.

    struct s { inx x; } y;         // 'y' is a tag
    typedef struct s { inx x; } y; // 'y' is an alias
    union u { inx x; } y;          // 'y' is a tag
    typedef union u { inx x; } y;  // 'y' is an alias
    

    Using Tag and Alias together:

    #include <stdio.h>
    
    struct s { int x; } S; // 'S' is a tag
    typedef struct s SS;   // 'SS' is an alias
    
    int main(void) {
      SS myStruct;
      myStruct.x = 10;
    
      S.x = 15;
    
      printf("Tag: %d\n", S.x);
      printf("Alias: %d\n", myStruct.x);
    
      return 0;
    }
    

    構造体、組合、メンバー、セミコロン

    The struct and union and all members shall be terminated by semicolon character ; .

    // 💩
    struct s { int x }
    union u { int x }
    struct s { int x; }
    union u { int x; }
    
    // 💩
    struct s {
      int x;
      char z
    };
    
    union u {
      int x;
      char z
    };
    
    // Some compilers accept this style if you have only 1 member
    struct s { int x };
    union u { int x };
    
    // 👍
    struct s { int x; };
    union u { int x; };
    

    structとunionの宣言と定義

    Forward declaration in form of struct x; or union x; , at this point, x is considered an incomplete type. A struct or union is only considered complete type after } .

    struct s1;          // Incomplete type
    union u1;           // Incomplete type
    typedef struct s1 S;    // Incomplete type, 'S' is only an alias to incomplete struct 's1'
    typedef union u1 U;     // Incomplete type, 'U' is only an alias to incomplete struct 'u1'
    struct s2 { int x; };   // Complete
    union u2 { int x; };    // Complete
    struct { int x; } s3;   // Also complete, 's3' is a tag
    struct { int x; } u3;   // Also complete, 'u3' is a tag
    

    At the same file and scope, a tag and an identifier with the same name. They are two different struct and two different union , yes, C allow this behavior. Avoid this style.

    // Valid, but 👎 💩 😠
    struct s { int x; };
    typedef struct { int x; } s;
    union u { int x; };
    typedef union { int x; } u;
    

    At the same file and scope the example bellow is invalid, because they have the same identifier and typedef is trying to redefine the struct s and union u that is already defined.

    // 💩
    struct s { int x; };
    typedef struct s { int x; };
    union u { int x; };
    typedef union u { int x; };
    

    May be compiled, but the keyword typedef is useless, because there is no alias. If you have a typedef is because you want an alias. To get an error on this, and not only warning, for clang you need the flag -Werror .

    // 💩
    typedef struct s { int x; };
    typedef union u { int x; };
    

    変数の宣言と内容へのアクセス

    struct example:

    #include <stdio.h>
    
    struct a { int x; };
    typedef struct a aa; // Alias 'aa' for the struct 'a'
    struct { int x; } b; // Tag 'b'
    typedef struct b bb; // Compile, but unusable.
    struct c { int x; } C; // struct 'c' and tag 'C'
    typedef struct { int x; } d; // Alias 'd'
    typedef struct e { int x; } ee; // struct 'e' and alias 'ee'
    
    int main(void) {
      struct a a1; // 'a1' is a variable of struct type 'a'
      a1.x = 1;
    
      aa a2; // 'a2' is a variable of struct type 'a'. 'aa' is an alias of 'struct a'
      a2.x = (++a1.x);
    
      b.x = (++a2.x); // You can't do 'b b1', because 'b' is not a type, but has a type.
    
      struct c c1;
      c1.x = (++b.x);
    
      C.x = (++c1.x);
    
      d d1;
      d1.x = (++C.x);
    
      struct e e1;
      e1.x = (++d1.x);
    
      ee e2;
      e2.x = (++e1.x);
    
      printf("%d\n", e2.x);    
      return 0;
    }
    

    union example, because union members overlap the memory every time it is assigned something, you need attention on the usage sequence:

    #include <stdio.h>
    
    union e {
      int x;
      char y;
    };
    
    int main(void) {    
      union e u;
    
      u.x = 30200; // Remember not to cause signed integer overflow
      printf("%d\n", u.x);
      u.y = 'a'; // After this point, you will get garbage trying to read u.x
      printf("%c\n", u.y);
      printf("%d\n", u.x);
      u.x = 11111; // After this point, you will get garbage trying to read u.y
      printf("%d\n", u.x);
      printf("%c\n", u.y);
      return 0;
    }
    

    構造体と組合の大きさ

    struct size is defined by all members, plus compiler optimization in the form of hidden padding bits for performance reasons.

    struct s {
      int x;
      char y;
    };
    

    In the example above the size will be the size of an int plus char plus hidden padding bits that a compiler judges its needed.

    union size is defined by his largest member, plus compiler optimization in the form of hidden padding bits for performance reasons.

    union u {
      int x;
      char y;
    };
    

    In the example above the size of the union u will be the size of an int , and not int plus char .


    アノニマス・ストラクチャ

    It is a anonymous struct or anonymous union if it have no identifier and no tag, and exist only inside another struct or union .

    struct s { // Regular struct
      struct { int x; };     // Anonymous struct, no identifier and no tag
      union { int y; };      // Anonymous union, no identifier and no tag
    
      struct a { int x; };   // NOT Anonymous struct, has an identifier 'a'
      union b { int x; };    // NOT Anonymous union, has an identifier 'b'
      struct { int x; } c;   // NOT Anonymous struct, has a tag 'c'
      union { int x; } d;    // NOT Anonymous union, has a tag 'd'
      struct e { int x; } E; // NOT Anonymous struct, has an identifier and a tag
      union f { int x; } F;  // NOT Anonymous union, has an identifier and a tag
    };
    
    union u { // Regular union
      struct { int x; };    // Anonymous struct, no identifier and no tag
      union { int y; };     // Anonymous union, no identifier and no tag
    };
    

    Members of a anonymous struct or union can't have the same identifier name of the members of the parent struct or union , because their scope is the parent struct or union scope. A regular struct or union have its own scope. And this also define the way you access the members.

    struct {
      int x;
      struct { int x; };   // 💩, same name of parent member identifier
      struct { int z; };   // 👍
      struct { int x; } y; // 👍, regular struct
    } s;
    

    Accessing values:

    #include <stdio.h>
    
    struct {
      int x;
      struct { int z; };
      struct { int x; } y;
    } s;
    
    int main(void) {
      s.x = 5;
      s.z = 10; // Access the same way as any parent member
      s.y.x = 15;
    
      printf("%d, %d, %d\n", s.x, s.z, s.y.x);
    
      return 0;
    }
    

    Until now can't find any useful situation for anonymous struct inside another struct , if you find let me know. But an anonymous struct inside an union is useful:

    enum conType { USB, SERIAL, WIFI, };
    
    struct usbConnection { int x; };
    struct serialConnection { int x; };
    struct wifiConnection { int x; };
    
    struct connection {
      enum conType type;
      union {
        struct usbConnection usb;
        struct serialConnection serial;
        struct wifiConnection wifi;
      };
    };
    
    int main(void) {
      struct connection con;    
      con.usb.x = 10; // Anonymous union allow easy access
      con.type = USB;
    
      return 0;
    }
    

    Another example, trying to achieve some kind of polymorphism:

    #include <stdio.h>
    
    #define LENGHT 10
    enum NumberType { INT, FLOAT, DOUBLE };
    
    struct saveData {
      enum NumberType type;
      union {
        int     iNumbers[LENGHT];
        float   fNumbers[LENGHT];
        double  dNumbers[LENGHT];
      };
    };
    
    struct wasteData {
      enum    NumberType type;
      int     iNumbers[LENGHT];
      float   fNumbers[LENGHT];
      double  dNumbers[LENGHT];
    };
    
    void ProcessData( struct saveData *d ){
      switch(d->type) {
        case INT:
          printf("Int\n");
          break;
        case FLOAT:
          printf("Float\n");
          break;
        case DOUBLE:
          printf("Double\n");
          break;
      };
    }
    
    int main(void) {
      struct saveData sData;    
      sData.type = DOUBLE;    
      ProcessData(&sData);
    
      struct saveData sData1;
      struct wasteData sData2;    
      printf("sData1: %ld\nsData2: %ld\n", sizeof(sData1), sizeof(sData2));
    
      return 0;
    }
    

    In a situation like this, if you have millions of requests processing some kind of data, using union you can reduce the amount of resources (space, memory, processing times, money) needed to achieve an objective. Also not forget padding bits concept. In the example above you can switch the usage of enum to a mix of #define and char to save some bytes if needed.


    ビットフィールド

    It is a member of struct or union , with explicit width in bits. Before you be able to correctly handle bit-fields you need understand the concepts of allocation unit, padding bits and memory alignment.

    ⚠️ Bit-field is NOT portability friendly. The behaviors and results of the usage of a bit-field are tied to implementation defined rules.

    ⚠️ The use of bit-fields as member of union is not recommended, because there is no valid reason to do that. But if you want, the same rules for bit-field as member of struct apply to union , except the fact that only 1 member can exist during runtime. Because of this bit-field examples only use struct .


    ビットフィールドのコンポーネント
    型識別子:;
    種類
    有効なビットフィールド型:
  • int . 平野int かもしれないsigned int or unsigned int , 実装定義です.
  • signed int
  • unsigned int
  • _Bool . 単一のビットフィールドは0 or 1 . それでも_BoolCHAR_BIT .
  • 実装定義型
  • 原子タイプ[C 11].実装定義.

  • 識別子
    変数名.


    ビットフィールドの幅は動的ではなく、宣言中に設定する必要があり、非負の整数定数でなければならず、指定された型のビットを超えてはならない.
    // 💩
    struct s { _Bool x: 5; };  // _Bool is a single bit-field, only 0 or 1 is accepted
    struct s { int x: 1.2; };  // Not an integer
    struct s { int x: -1; };   // Negative width
    struct s { int x: 256; };  // 256 bits exceed the max bits of a int that is 32 bits
    
    const unsigned int A = 10;
    struct s { unsigned int x: A; }; // A is not integer constant
    
    // 👍
    struct s { _Bool x: 1; };
    
    #define A 10;
    struct s { unsigned int x: A; }; // A is translated to a integer constant 10 before compillation of the source code.
    
    // 👍, enum
    enum { A = 10 };
    struct s { unsigned int x: A; }; // A is an integer constant, also works well.
    
    enum { A = 10 } z;
    struct s { unsigned int x: A; }; // A is an integer constant, also works well.
    
    // 💩, enum
    enum { A = 10 } z;
    struct s { unsigned int x: z.A; }; // Tag z is NOT an integer constant.
    

    記憶挙動
    ビット単位の割り当ては、アロケーションユニットのために、バイトの第1の位置で常に行われない.十分なスペースがあるならば、シーケンスの複数のビットフィールドは一緒に詰まっています.それは、ビットがパックされた左または右から左への権利、endiannessに関係するかどうか定義される実装です.
    次の例は、前提ですunsigned int 4バイト、合計32ビットで混乱します.
    struct s { int x : 20, y : 3; }; //Bit field
    struct s { int x, y; };          // Any other type
    
    上からの説明.
    ビットフィールド
    欲しいものx and y タイプint . 割り当ては1つだけ割り当てられるint , それは必要なスペースを保持するのに十分なのでx and y .
    他のタイプ
    割り当てはスペースを確保することによって行われるx int and y int , 合計2int 64ビットを使う.この場合、ビットフィールドよりメモリを2倍にする.
    あなたが変わるならばx30 , 64ビットしか割り当てられませんint 十分ではありません:
    struct s { int x: 30, y: 3; };
    
    いくつかの理由で、いくつかのビットフィールドの間に空きスペースが必要です: + ビット数:
    struct {
      unsigned int x: 4, :5, y: 1;
    } s;
    
  • x 4ビットを使用します.
  • :5 識別子なしでは、5ビットが空で、使用されないが、予約されます.
  • y 1ビットを使用します.
  • 値を持つ任意の型のビットフィールド0 識別子なし:0 , 同じタイプの他のビットフィールドの間で、次のアロケーションユニットの先頭にビットを詰めて、次のビットフィールドを開始します
    // 👍, sizeof is 8 for both styles.
    struct { unsigned int x: 3, :0, y: 4; } s; // 
    
    struct {
      unsigned int x: 3;
      unsigned int :0;
      unsigned int y: 4;
    } s;
    
    // 💩 
    struct {
      unsigned int x: 3;
      :0; // Not compile because no type was specified.
      unsigned int y: 4;
    } s;
    
    struct {
      unsigned int x: 3;
      _Bool :0; // Normal compilation, but the desired behavior (break) does not work, because the type is different.
      unsigned int y: 4;
    } s;
    
    コンパイラ干渉を使用しないパケットのビット量を制御する#pragma pack .

    ビットフィールド値の保存とアクセス
    単項演算子& (アドレスの)ビットフィールドでは使用できません.これは、ビットフィールドのポインタや配列が存在しない理由です.Also sizeof or alignas [ C 11 ]ビットフィールドメンバーでは動作しませんが、sizeof エーstruct それは1つ以上のビットフィールドを含んでいます.
    #include <stdio.h>
    
    struct { unsigned int x: 3; } s;
    
    int main(void) {
      s.x = 7; // Assign a decimal 7
      printf("%d", s.x); // Output decimal 7
      return 0;
    }
    
    置換するなら7 to 8 または10進数の7より大きい任意の数は、この場合は動作しません.ビットフィールドが3ビットの幅を持つので、それは数のバイナリ表現を持つことができます0 - 7 . 数7 ビットでは111 そして、数を保持するのに十分です7 , しかし、数8 4ビット必要です.
    整数オーバーフローが起こりやすいので、これは非常にエラーがちな操作です.いくつかの実装ではunsigned int 常にオーバーフローする0 , でもsigned int オーバーフローは未定義の動作を引き起こす可能性があります.
    参考文献
  • N1256
  • N1570
  • N2596