web-dev-qa-db-ja.com

構造と組合の違い

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

381
gagneet

ユニオンでは、要素はすべて同じ場所に格納されているため、要素の1つを使用することになります。これは、いくつかのタイプのうちの1つになる可能性があるものを保管したいときに役立ちます。一方、構造体は、その要素ごとに別々のメモリ位置を持ち、それらすべてを一度に使用できます。

具体的な使用例を示すために、私は少し前にSchemeインタプリタに取り組んでいましたが、基本的にSchemeデータ型をCデータ型にオーバーレイしていました。これは、値のタイプを示すenumとその値を格納するための共用体を構造体に格納することを含みます。

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

edit:x.bを 'c'に設定するとx.aの値がどのように変更されるのか疑問に思う場合は、厳密には定義されていません。最近のほとんどのマシンでは、charは1バイト、intは4バイトなので、x.bに値 'c'を指定すると、x.aの最初のバイトも同じ値になります。

union foo x;
x.a = 3;
x.b = 'c';
printf("%i, %i\n", x.a, x.b);

版画

99, 99

2つの値が同じなのはなぜですか? int 3の最後の3バイトはすべて0なので、99とも読み取られます。x.aにもっと大きな数を入れると、必ずしもそうとは限りません。

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を上書きした場所を明確に確認できます。

BUT

Cでは、int内のバイトの順序は定義されていません。このプログラムは、0xEFを0x22で上書きしましたが、代わりに0xDEを上書きするプラットフォームがあります。 intを構成するバイトの順序が逆になりました。したがって、プログラムを書くときは、移植性がないため、特定のデータを共用体で上書きするという動作に頼るべきではありません。

バイト順の詳細については、 エンディアン をご覧ください。

639
Kyle Cronin

簡単な答えは次のとおりです。構造体はレコード構造体です。構造体内の各要素は新しい領域を割り当てます。だから、のような構造体

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))、ここでもアラインメントを追加する可能性があります。

76
Charlie Martin

'struct'と 'union'の違いを説明する良い例はありますか?

架空の通信プロトコル

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

この架空のプロトコルでは、「メッセージタイプ」に基づいて、ヘッダー内の次の位置は要求番号か4文字のコードのどちらかになりますが、両方にはなりません。つまり、共用体では、同じ格納場所で複数のデータタイプを表すことができます。この場合、一度に格納できるデータタイプは1つだけです。

共用体は大部分がCプログラミング言語としてのCの遺産に基づく低レベルの詳細で、「重複する」保管場所がこのように使用されることがあります。いくつかのタイプのうち1つだけが一度に保存されるデータ構造がある場合、メモリを節約するために時々共用体を使用することができます。

一般的に、OSは構造体や共用体を気にすることも、知ることもしません - それらはどちらも単にそれに対するメモリのブロックです。構造体は複数のデータオブジェクトを格納するメモリブロックで、それらのオブジェクトはオーバーラップしません。共用体は、いくつかのデータオブジェクトを格納するメモリのブロックですが、これらのうちの最大のもののためのストレージしか持たないため、一度に格納できるデータオブジェクトは1つだけです。

49
cygil

すでに質問で述べたように、unionstructの主な違いは、unionメンバーは互いのメモリをオーバーレイしているため、共用体のsizeofは1であり、structメンバーは1つずつ配置されています。間に)。また、共用体はそのすべてのメンバーを含むのに十分な大きさであり、すべてのメンバーに合うようなアライメントを持っています。 intは2バイトアドレスにのみ格納でき、2バイト幅で、longは4バイトアドレスにのみ格納でき、4バイト長であるとしましょう。次の組合

union test {
    int a;
    long b;
}; 

4のsizeof、および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を返します)。

unionはいくつかのシナリオで役に立ちます。 unionはカーネル用のデバイスドライバを書くような非常に低レベルの操作のためのツールです。

その一例は、ビットフィールドとfloatを持つunionstructを使用して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"の本の中に見つけました。 差別化された共用体 のセクションで調べてください。

乾杯!

18
Krzysztof Voss

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

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

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

この場合、プロセスはすべての信号の最も高いメモリのみを使用します。ただし、この場合にstructを使用すると、メモリー使用量はすべてのシグナルの合計になります。多くの違いがあります。

要約すると、あなたが一度にメンバーのいずれかにアクセスすることを知っていればUnionを選択するべきです。

11
Ravi Kanth

"共用体"と "struct"は )C言語のを構成します。どちらかのキーワードを使用すると異なるコードを生成するのはコンパイラであるため、それらの間の「OSレベル」の違いについて話すことは不適切です。

11

非技術的に言えば、

仮定:椅子=記憶ブロック、人=可変

構造:3人いる場合は、その人数分の椅子に座ることができます。

組合:3人の場合1人だけの椅子がいる座って、座ってみたいときは、すべて同じ椅子を使用する必要があります。

技術的に言えば、

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

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

Bufferaddrの合計MAIN_STRUCT size = sizeof(UINT64)+ unionのsizeof(UNIT32)+パディング用の32ビット(プロセッサアーキテクチャに依存)= 128ビット。構造上、すべてのメンバは連続してメモリブロックを取得します。

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

11
skanda93

あなたはそれを持っています、それがすべてです。しかし、基本的に、労働組合のポイントは何ですか?

あなたは異なる場所の同じ場所にコンテンツを入れることができます。あなたはあなたがあなたが共用体に格納したものの型を知っている必要があります(それであなたはしばしばtypeタグでstructに入れます...).

何でこれが大切ですか?スペースの節約には向いていません。はい、あなたはいくらかのビットを得るか、またはいくらかのパディングをすることができます、しかしそれはもう要点ではありません。

それは型安全のためです、それはあなたがある種の「動的型付け」をすることを可能にします:コンパイラはあなたの内容が異なる意味を持つことを知っています。異なる型を指すことができるポインタがあるなら、あなたは共用体を使わなければなりません、さもなければ、エイリアシング問題のためあなたのコードは正しくないかもしれません(コンパイラはそれ自身に言います。それらのアクセスを排除します... "そして悪いことが起こり得ます)。

10
Piotr Lesnicki

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

共用体は、その最大のメンバーが必要とするだけの量のメモリを割り当てます。

9
CMS

共用体の使用共用体は、特殊なタイプの会話が必要なときに頻繁に使用されます。組合の有用性についての考えを得るため。 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
Ahmed

構造と共用体の違いは何ですか?

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

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のメモリスペースは32ビットオペレーティングシステムでは8です。

そのため、構造体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/

構造体は、さまざまな種類のデータが格納され、それぞれが独自のメモリブロックを取得できる、さまざまな種類のデータの集まりです。

変数の1つのみが一度に使用されることを確認し、現在のメモリを最大限に活用したい場合は、通常、結合を使用します。これは、最大タイプに等しいメモリブロックを1つだけ取得するためです。

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

それが得る総メモリ=> 5バイト

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

合計メモリ= 4バイト

3
Anurag Bhakuni

共用体は他のものに対して繰り返すという点で構造体とは異なります。つまり、構造体は重複や再定義を行わずに次々に定義する一方で、同じメモリを再定義します。

1
Dennis Ng

下記に与えられるバイト順序付け関数を書く間、共用体は便利になります。構造体では不可能です。

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