構造とユニオン
Components
構造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
. それでも_Bool
はCHAR_BIT
. 識別子
変数名.
幅
ビットフィールドの幅は動的ではなく、宣言中に設定する必要があり、非負の整数定数でなければならず、指定された型のビットを超えてはならない.
// 💩
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倍にする.あなたが変わるならば
x
値30
, 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
オーバーフローは未定義の動作を引き起こす可能性があります.参考文献
Reference
この問題について(構造とユニオン), 我々は、より多くの情報をここで見つけました https://dev.to/bkddev/c-struct-and-union-1cbhテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol