struct
aとaの違いを示す良い例はありますunion
か?struct
基本的に、それはそのメンバーのすべてのメモリをunion
使用し、最大のメンバーのメモリスペースを使用することを知っています。他にOSレベルの違いはありますか?
16 に答える
ユニオンでは、要素がすべて同じ場所に格納されているため、要素の1つのみを使用することになっています。これは、いくつかのタイプの1つである可能性があるものを保存する場合に役立ちます。一方、構造体には、その要素ごとに個別のメモリ位置があり、それらすべてを一度に使用できます。
それらの使用の具体例を示すために、私は少し前にSchemeインタープリターに取り組んでおり、基本的にSchemeデータ型をCデータ型にオーバーレイしていました。これには、値の型を示す列挙型とその値を格納するためのユニオンを構造体に格納することが含まれていました。
union foo {
int a; // can't use both a and b at once
char b;
} foo;
struct bar {
int a; // can use both a and b simultaneously
char b;
} bar;
union foo x;
x.a = 3; // OK
x.b = 'c'; // NO! this affects the value of x.a!
struct bar y;
y.a = 3; // OK
y.b = 'c'; // OK
編集: xbを「c」に設定するとxaの値がどのように変更されるのか疑問に思っている場合、技術的には未定義です。最近のほとんどのマシンでは、charは1バイト、intは4バイトであるため、xbに値'c'を与えると、xaの最初のバイトにも同じ値が与えられます。
union foo x;
x.a = 3;
x.b = 'c';
printf("%i, %i\n", x.a, x.b);
プリント
99, 99
2つの値が同じなのはなぜですか?int 3の最後の3バイトはすべてゼロであるため、99としても読み取られます。xaに大きな数値を入力すると、常にそうであるとは限らないことがわかります。
union foo x;
x.a = 387439;
x.b = 'c';
printf("%i, %i\n", x.a, x.b);
プリント
387427, 99
実際のメモリ値を詳しく調べるために、値を16進数で設定して出力してみましょう。
union foo x;
x.a = 0xDEADBEEF;
x.b = 0x22;
printf("%x, %x\n", x.a, x.b);
プリント
deadbe22, 22
0x22が0xEFを上書きした場所を明確に確認できます。
しかし
Cでは、intのバイト順序は定義されていません。このプログラムは私のMacで0xEFを0x22で上書きしましたが、intを構成するバイトの順序が逆になっているため、代わりに0xDEを上書きするプラットフォームが他にもあります。したがって、プログラムを作成するときは、移植性がないため、ユニオン内の特定のデータを上書きする動作に依存しないでください。
バイトの順序の詳細については、エンディアンを確認してください。
簡単な答えは次のとおりです。構造体はレコード構造体です。構造体の各要素は新しいスペースを割り当てます。したがって、次のような構造体
struct foobarbazquux_t {
int foo;
long bar;
double baz;
long double quux;
}
各インスタンスに少なくとも(sizeof(int)+sizeof(long)+sizeof(double)+sizeof(long double))
バイトのメモリを割り当てます。(「少なくとも」は、アーキテクチャのアラインメント制約により、コンパイラが構造体をパディングするように強制される可能性があるためです。)
一方で、
union foobarbazquux_u {
int foo;
long bar;
double baz;
long double quux;
}
メモリのチャンクを 1 つ割り当て、それに 4 つのエイリアスを割り当てます。そのsizeof(union foobarbazquux_u) ≥ max((sizeof(int),sizeof(long),sizeof(double),sizeof(long double))
ため、再びアラインメントの追加の可能性があります。
「構造体」と「ユニオン」の違いを示す良い例はありますか?
架空の通信プロトコル
struct packetheader {
int sourceaddress;
int destaddress;
int messagetype;
union request {
char fourcc[4];
int requestnumber;
};
};
この架空のプロトコルでは、「メッセージ タイプ」に基づいて、ヘッダー内の次の場所が要求番号または 4 文字のコードのいずれかであるが、両方ではないことが特定されています。つまり、共用体を使用すると、同じ格納場所で複数のデータ型を表すことができ、一度に 1 つの型のデータのみを格納することが保証されます。
共用体は主に、システム プログラミング言語としての C の遺産に基づく低レベルの詳細であり、「重複する」記憶域の場所がこのように使用されることがあります。一度に複数の型のうちの 1 つだけが保存されるデータ構造がある場合、共用体を使用してメモリを保存することができます。
一般に、OS は構造体と共用体を気にしたり認識したりしません。これらはどちらも単にメモリのブロックにすぎません。構造体は、複数のデータ オブジェクトを格納するメモリ ブロックであり、それらのオブジェクトは重複しません。ユニオンは、複数のデータ オブジェクトを格納するメモリのブロックですが、最大のデータ オブジェクトしか格納できないため、一度に 1 つのデータ オブジェクトしか格納できません。
union
すでに質問で述べているように、との主な違いstruct
は、union
メンバーが互いにメモリをオーバーレイするため、ユニオンのサイズが1になり、struct
メンバーが次々に配置されることです(間にオプションのパディングがあります)。また、ユニオンはそのすべてのメンバーを含むのに十分な大きさであり、そのすべてのメンバーに適合する配置を持っています。たとえばint
、2バイトのアドレスにのみ格納でき、幅は2バイトであり、longは4バイトのアドレスにのみ格納でき、長さは4バイトであるとします。次の組合
union test {
int a;
long b;
};
はsizeof
4で、配置要件は4である可能性があります。ユニオンと構造体の両方で、先頭にパディングを付けることはできますが、先頭にパディングを付けることはできません。構造体に書き込むと、書き込まれたメンバーの値のみが変更されます。ユニオンのメンバーに書き込むと、他のすべてのメンバーの値が無効になります。以前に書き込みを行ったことがない場合はアクセスできません。そうでない場合、動作は定義されていません。GCCは、組合員に最近手紙を書いていなくても、組合員から実際に読むことができる拡張機能を提供します。オペレーティングシステムの場合、ユーザープログラムがユニオンに書き込むか構造に書き込むかは重要ではありません。これは実際にはコンパイラの問題にすぎません。
共用体と構造体のもう1つの重要な特性は、それらへのポインターがそのメンバーの任意の型を指すことができることです。したがって、以下が有効です。
struct test {
int a;
double b;
} * some_test_pointer;
some_test_pointerはint*
またはを指すことができdouble*
ます。test
タイプのアドレスをにキャストすると、実際にはint*
最初のメンバーである、を指します。a
組合についても同じことが言えます。したがって、ユニオンは常に正しい配置になるため、ユニオンを使用して、あるタイプを指すようにすることができます。
union a {
int a;
double b;
};
そのユニオンは実際にはintとdoubleを指すことができます:
union a * v = (union a*)some_int_pointer;
*some_int_pointer = 5;
v->a = 10;
return *some_int_pointer;
C99標準で述べられているように、実際には有効です。
オブジェクトは、次のいずれかのタイプの左辺値式によってのみ、格納された値にアクセスする必要があります。
- オブジェクトの有効なタイプと互換性のあるタイプ
- ..。
- メンバーの中に前述のタイプの1つを含む集合体または共用体タイプ
v->a = 10;
の値に影響を与える可能性があるため、コンパイラは最適化を行いません*some_int_pointer
(そして、関数は10
の代わりに戻ります5
)。
Aunion
はいくつかのシナリオで役立ちます。
union
カーネル用のデバイス ドライバーの作成など、非常に低レベルの操作を行うためのツールとなる可能性があります。
その例は、ビットフィールドを持つ とをfloat
使用して数値を分析することです。に番号を保存し、後でそれを介しての特定の部分にアクセスできます。この例は、データをさまざまな角度から見るために を使用する方法を示しています。union
struct
float
float
float
struct
union
#include <stdio.h>
union foo {
struct float_guts {
unsigned int fraction : 23;
unsigned int exponent : 8;
unsigned int sign : 1;
} fg;
float f;
};
void print_float(float f) {
union foo ff;
ff.f = f;
printf("%f: %d 0x%X 0x%X\n", f, ff.fg.sign, ff.fg.exponent, ff.fg.fraction);
}
int main(){
print_float(0.15625);
return 0;
}
ウィキペディアの単精度の説明を見てください。そこから例とマジックナンバー 0.15625 を使用しました。
union
複数の選択肢を持つ代数データ型を実装するためにも使用できます。O'Sullivan、Stewart、および Goerzen による「Real World Haskell」の本でその例を見つけました。差別された組合のセクションでそれをチェックしてください。
乾杯!
はい、構造体と共用体の主な違いは、あなたが述べたものと同じです。構造体はそのメンバーのすべてのメモリを使用し、共用体は最大のメンバーのメモリ空間を使用します。
しかし、すべての違いは、メモリの使用の必要性にあります。ユニオンの最適な使用法は、シグナルを利用する unix のプロセスで見ることができます。プロセスが一度に 1 つのシグナルにしか作用できないように。したがって、一般的な宣言は次のようになります。
union SIGSELECT
{
SIGNAL_1 signal1;
SIGNAL_2 signal2;
.....
};
この場合、プロセスはすべてのシグナルの最も高いメモリのみを使用します。ただし、この場合に struct を使用すると、メモリ使用量はすべてのシグナルの合計になります。多くの違いがあります。
要約すると、一度にいずれかのメンバーにアクセスすることがわかっている場合は、Union を選択する必要があります。
「union」と「struct」はC言語の構造体です。1つまたは別のキーワードを使用すると異なるコードを生成するのはコンパイラであるため、それらの間の「OSレベル」の違いについて話すことは不適切です。
あなたはそれを持っています、それだけです。しかし、基本的に、組合のポイントは何ですか?
異なるタイプのコンテンツを同じ場所に配置できます。ユニオンに格納したものの型を知っておく必要があります (多くの場合struct
、type タグを使用して格納します...)。
何でこれが大切ですか?スペースを増やすためではありません。はい、いくつかのビットを取得したり、パディングを行ったりできますが、それはもはや重要なポイントではありません。
これは型の安全性のためであり、ある種の「動的型付け」を行うことができます。コンパイラは、コンテンツが異なる意味を持つ可能性があることと、実行時に解釈する方法の正確な意味を認識しています。異なる型を指すことができるポインターがある場合は、共用体を使用する必要があります。そうしないと、エイリアシングの問題によりコードが正しくない可能性があります (コンパイラーは、「ああ、このポインターのみがこの型を指すことができるので、最適化できます。それらのアクセスを...」、そして悪いことが起こる可能性があります)。
技術的に言えば、次のことを意味します。
仮定: chair = メモリ ブロック、 people = 変数
構造: 3 人がいる場合は、それぞれのサイズの椅子に座ることができます。
Union : 3 人がいる場合、座る椅子は1 つだけです。全員が座りたいときに同じ椅子を使用する必要があります。
技術的に言えば、次のことを意味します。
下記のプログラムは、構造と結合を一緒に深く掘り下げます。
struct MAIN_STRUCT
{
UINT64 bufferaddr;
union {
UINT32 data;
struct INNER_STRUCT{
UINT16 length;
UINT8 cso;
UINT8 cmd;
} flags;
} data1;
};
MAIN_STRUCT の合計サイズ = bufferaddr の sizeof(UINT64) + ユニオンの sizeof(UNIT32) + パディングの 32 ビット (プロセッサのアーキテクチャに依存) = 128 ビット . 構造体の場合、すべてのメンバーがメモリ ブロックを連続して取得します。
Union は、最大サイズのメンバー (ここでは 32 ビット) の 1 つのメモリ ブロックを取得します。ユニオン内には、もう 1 つの構造体 (INNER_STRUCT) があり、そのメンバーは合計サイズ 32 ビット (16+8+8) のメモリ ブロックを取得します。ユニオンでは、INNER_STRUCT(32 ビット) メンバーまたはデータ(32 ビット) にアクセスできます。
構造体は、その中のすべての要素の合計サイズを割り当てます。
ユニオンは、最大のメンバーが必要とするだけのメモリを割り当てます。
ユニオンの使用 ユニオンは、特殊なタイプの会話が必要な場合に頻繁に使用されます。ユニオンの有用性を理解するため。c/c 標準ライブラリは、短い整数をファイルに書き込むために特別に設計された関数を定義していません。fwrite() を使用すると、単純な操作に対して過度のオーバーヘッドが発生します。ただし、ユニオンを使用すると、短い整数のバイナリを一度に 1 バイトずつファイルに書き込む関数を簡単に作成できます。短い整数は2バイトの長さであると想定しています
例:
#include<stdio.h>
union pw {
short int i;
char ch[2];
};
int putw(short int num, FILE *fp);
int main (void)
{
FILE *fp;
fp fopen("test.tmp", "wb ");
putw(1000, fp); /* write the value 1000 as an integer*/
fclose(fp);
return 0;
}
int putw(short int num, FILE *fp)
{
pw word;
word.i = num;
putc(word.c[0] , fp);
return putc(word.c[1] , fp);
}
putw() は short integer で呼び出しましたが、putc() と fwrite() を使用することは可能でした。しかし、ユニオンの使用方法を支配する例を示したかった
共用体は、以下に示すバイト順序関数を作成するときに便利です。構造体では不可能です。
int main(int argc, char **argv) {
union {
short s;
char c[sizeof(short)];
} un;
un.s = 0x0102;
if (sizeof(short) == 2) {
if (un.c[0] == 1 && un.c[1] == 2)
printf("big-endian\n");
else if (un.c[0] == 2 && un.c[1] == 1)
printf("little-endian\n");
else
printf("unknown\n");
} else
printf("sizeof(short) = %d\n", sizeof(short));
exit(0);
}
// Program from Unix Network Programming Vol. 1 by Stevens.