構造とユニオンの違い


411

a structとaの違いを示す良い例はありますunionか?基本的に私はそれstructがそのメンバーのすべてのメモリをunion使用し、最大のメンバーのメモリ空間を使用することを知っています。他のOSレベルの違いはありますか?

回答:


677

ユニオンを使用すると、要素の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を上書きするプラットフォームがあります。したがって、プログラムを作成するときは、移植性がないため、ユニオン内の特定のデータを上書きする動作に依存することはできません。

バイトの順序の詳細については、エンディアンを確認してください。


1
この例を使用して、ユニオンで、xb = 'c'の場合、xaに何が格納されますか?charの参照番号ですか?
kylex 2008

1
うまくいけば、xbを設定したときにxaに何が格納されるかがより詳細に説明されています
Kyle Cronin

1
@KyleCroninわかったと思います。あなたの場合、タイプのグループがあり、タイプを使用する必要があるのはわかっていますが、実行時までどちらを使用するかわかりません-ユニオンでそれを行うことができます。ありがとう
user12345613

2
@ user12345613ユニオンは、構造体の一種の基本クラスとして使用できます。構造体の共用体を使用してOO階層をエミュレートできます
Morten Jensen

1
@Lazarマルチバイト型のバイトオーダーはエンディアンに依存します。ウィキペディアの記事を読むことをお勧めします。
カイル・クロニン

83

簡単な答えは次のとおりです。構造体はレコード構造体です。構造体の各要素は新しいスペースを割り当てます。したがって、次のような構造体

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))、再度、アライメントの追加が必要になる可能性があります。


53

「構造体」と「共用体」の違いを示す良い例はありますか?

架空の通信プロトコル

struct packetheader {
   int sourceaddress;
   int destaddress;
   int messagetype;
   union request {
       char fourcc[4];
       int requestnumber;
   };
};

この架空のプロトコルでは、「メッセージタイプ」に基づいて、ヘッダー内の次の場所がリクエスト番号または4文字コードのいずれかであり、両方ではないことが明記されています。つまり、ユニオンを使用すると、同じ格納場所で複数のデータ型を表すことができ、一度に1つのデータ型のみを格納することが保証されます。

ユニオンは、システムプログラミング言語としてのCの伝統に基づく低レベルの詳細であり、「重複する」格納場所がこのように使用される場合があります。ユニオンを使用して、一度に複数のタイプの1つだけが保存されるデータ構造がある場合に、メモリを節約できます。

一般に、OSは構造体と共用体を気にしないか、または認識していません。これらは両方とも単にメモリのブロックです。構造体は、複数のデータオブジェクトを格納するメモリのブロックであり、それらのオブジェクトは重複しません。ユニオンは、いくつかのデータオブジェクトを格納するメモリのブロックですが、これらの最大のものだけのストレージがあるため、一度に1つのデータオブジェクトしか格納できません。


1
うん。これはユースケースをよく説明しています!
ギデオン2013年

1
packetheader ph;requestnumberにどのようにアクセスするかを考えますか?ph.request.requestnumber
justin.m.chase 2015

最高の説明!ありがとう。
84RR1573R 2017年

39

あなたの質問であなたすでに述べているように、との主な相違点unionstructそのありunionながらメンバーは、労働組合のはsizeofが一つであることを互いに程度のメモリをオーバーレイstructメンバーは(間にオプションのパディングで)各次々にレイアウトされています。また、共用体はそのすべてのメンバーを含めるのに十分な大きさであり、そのすべてのメンバーに適合する配置を持っています。したがってint、2バイトのアドレスにのみ格納でき、幅が2バイトであり、longは4バイトのアドレスにのみ格納でき、長さが4バイトであるとしましょう。次の組合

union test {
    int a;
    long b;
}; 

のa sizeofは4、アラインメント要件は4にすることができます。unionとstructはどちらも、末尾にパディングを持つことができますが、先頭にはパディングできません。構造体への書き込みは、書き込まれたメンバーの値のみを変更します。ユニオンのメンバーに書き込むと、他のすべてのメンバーの値が無効になります。以前に書き込んだことがない場合はアクセスできません。そうでない場合、動作は未定義です。GCCは、最後にユニオンのメンバーに書いていない場合でも、実際にユニオンのメンバーから読み取ることができる拡張機能を提供します。オペレーションシステムの場合、ユーザープログラムが共用体または構造体のどちらに書き込むかは問題ではありません。これは実際にはコンパイラの問題のみです。

unionとstructのもう1つの重要な特性は、それらへのポインターがそのメンバーのいずれかの型を指すことができるようにすることです。したがって、以下は有効です。

struct test {
    int a;
    double b;
} * some_test_pointer;

some_test_pointerはint*またはを指すことができdouble*ます。あなたがタイプのアドレスをキャストした場合testint*、それは、その最初のメンバーを指すようになります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)。


18

A unionはいくつかのシナリオで役立ちます。 unionカーネル用のデバイスドライバーの作成など、非常に低レベルの操作用のツールとなることができます。

その例として、ビットフィールド付きのa とfloatを使用して数値を分析することがあります。番号をに保存し、後でそのの特定の部分にアクセスできます。この例は、を使用してデータを見る角度を変える方法を示しています。unionstructfloatfloatfloatstructunion

#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複数の選択肢がある代数的データ型を実装するためにも使用できます。私はその例を、オサリバン、スチュワート、ゲルツェンの「リアルワールドハスケル」の本で見つけました。差別された組合のセクションでそれをチェックしてください。

乾杯!


11

union」と「struct」はC言語の構成要素です。それらの間の「OSレベル」の違いについて話すことは、1つまたは別のキーワードを使用する場合に異なるコードを生成するのはコンパイラーであるため、不適切です。


11

非技術的に言うと、

仮定:椅子=メモリブロック、人=変数

構造:3人の場合、対応するサイズの椅子に座ることができます。

連合:3人の場合、座る椅子は1つだけです。座るときは、全員が同じ椅子を使用する必要があります。

技術的に言えば、

下記のプログラムは、構造と結合を一緒に深く掘り下げます。

struct MAIN_STRUCT
{
UINT64 bufferaddr;   
union {
    UINT32 data;
    struct INNER_STRUCT{
        UINT16 length;  
        UINT8 cso;  
        UINT8 cmd;  
           } flags;
     } data1;
};

合計MAIN_STRUCTサイズ= sizeof(UINT64)はbufferaddr + sizeof(UNIT32)はユニオン+ 32ビットはパディング(プロセッサアーキテクチャによって異なります)= 128ビット。構造体の場合、すべてのメンバーが連続してメモリブロックを取得します。

Unionは、最大サイズのメンバー(ここでは32ビット)のメモリブロックを1つ取得します。union内にもう1つの構造体lies(INNER_STRUCT)があり、そのメンバーは合計サイズ32ビット(16 + 8 + 8)のメモリブロックを取得します。ユニオンでは、INNER_STRUCT(32ビット)メンバーまたは data(32ビット)のいずれかにアクセスできます。


素晴らしい説明。乾杯!
プレミア2017

11

はい、構造体と共用体の主な違いは、あなたが述べたものと同じです。Structはそのメンバーのすべてのメモリを使用し、unionは最大のメンバーのメモリ空間を使用します。

しかし、すべての違いは、メモリの使用量のニーズにあります。ユニオンの最適な使用法は、シグナルを利用するunixのプロセスで見ることができます。プロセスが一度に1つのシグナルのみに作用できるように。したがって、一般的な宣言は次のようになります。

union SIGSELECT
{
  SIGNAL_1 signal1;
  SIGNAL_2 signal2;
  .....
};

この場合、プロセスはすべての信号の最も高いメモリのみを使用します。ただし、この場合にstructを使用すると、メモリ使用量はすべての信号の合計になります。大きな違いが生まれます。

まとめると、一度にいずれかのメンバーにアクセスすることがわかっている場合は、Unionを選択する必要があります。


10

それだけです。しかし、基本的に、組合の要点は何ですか?

異なるタイプのコンテンツを同じ場所に配置できます。ユニオンに格納したもののタイプを知っている必要があります(そのためstruct、タイプタグを付けてに配置することがよくあります...)。

何でこれが大切ですか?スペースを増やすためのものではありません。はい、いくつかのビットを得るか、いくつかのパディングを行うことができますが、それはもはや主要なポイントではありません。

これはタイプセーフのためであり、ある種の「動的型付け」を実行できるようにします。コンパイラーは、コンテンツの意味が異なる可能性があること、および実行時にユーザーが解釈する方法の正確な意味を知っています。異なる型を指すことができるポインターがある場合は、ユニオンを使用する必要があります。それ以外の場合は、エイリアシングの問題が原因でコードが正しくない可能性があります(コンパイラーは「ああ、このポインターだけがこの型を指すことができるので、最適化できます。それらのアクセスを...」、そして悪いことが起こる可能性があります)。


9

構造体は、その中のすべての要素の合計サイズを割り当てます。

ユニオンは、最大のメンバーが必要とするだけのメモリを割り当てます。


2
また、割り当てられたユニオン「構造」の開始アドレスから開始するという点で、ユニオンメンバーを互いに「オーバーレイ」して追加することもできます。
ジムバック

4

構造と労働組合の違いは何ですか?

ショートカットの答えは次のとおりです。違いはメモリ割り当てにあります。説明:構造内で、構造内のすべてのメンバーに対してメモリー・スペースが作成されます。ユニオンでは、最大のメモリ空間を必要とするメンバーに対してのみメモリ空間が作成されます。次のコードを検討してください。

struct s_tag
{
   int a; 
   long int b;
} x;

union u_tag
{
   int a; 
   long int b;
} y;

ここでは、構造体と共用体の内部に2つのメンバーがあります。intとlong intです。intのメモリスペースは4バイトで、long intのメモリスペースは8(32ビットオペレーティングシステム)です。

したがって、構造体の場合は4 + 8 = 12バイトが作成され、ユニオン用に8バイトが作成されます

コード例:

#include<stdio.h>
struct s_tag
{
  int a;
  long int b;
} x;
union u_tag
{
     int a;
     long int b;
} y;
int main()
{
    printf("Memory allocation for structure = %d", sizeof(x));
    printf("\nMemory allocation for union = %d", sizeof(y));
    return 0;
}

参照:http : //www.codingpractise.com/home/c-programming/structure-and-union/


3

ユニオンユニオンの使用は、特殊な型の会話が必要な場合に頻繁に使用されます。労働組合の有用性を理解する。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()は短い整数で呼び出しましたが、putc()およびfwrite()を使用することは可能でした。しかし、私は組合がどのように使用されることができるかを支配するために例を示したかった


3

構造は異なるデータタイプのコレクションであり、異なるタイプのデータがその中に常駐でき、すべての人が独自のメモリブロックを取得します。

最大の型に等しいメモリブロックが1つしか取得されないため、現在のメモリを完全に利用したい場合は、一度に1つの変数のみが使用されることが確実な場合、通常はunionを使用しました。

struct emp
{
    char x;//1 byte
    float y; //4 byte
} e;

取得する合計メモリ=> 5バイト

union emp
{
    char x;//1 byte
    float y; //4 byte
} e;

取得する合計メモリ= 4バイト


2

ユニオンは、以下に示すバイト順序付け関数を記述するときに便利です。構造体では不可能です。

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.

1

Unionは構造体とは異なり、Unionは他のものと同じように繰り返されます。構造体は同じメモリを再定義しますが、構造体は重複や再定義なしに次々に定義します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.