6

Cでの使用を理解するのが困難unionです。この件について SO に関する多くの投稿を読みました。unionしかし、構造体を使用して同じことが達成できる場合に、なぜ好ましいのかについては説明していません。

K&Rより引用

コンパイラのシンボル テーブル マネージャーに見られるような例として、定数が int、float、または文字ポインターであるとします。特定の定数の値は、適切な型の変数に格納する必要がありますが、値が同じ量のストレージを占有し、その型に関係なく同じ場所に格納される場合、テーブル管理に最も便利です。これが、複数の型のいずれかを正当に保持できる単一の変数を結合する目的です。構文は構造に基づいています。

union u_tag {
      int ival;
      float fval;
      char *sval;
} u;

使用法は

if (utype == INT)
    printf("%d\n", u.ival);
if (utype == FLOAT)
    printf("%f\n", u.fval);
if (utype == STRING)
    printf("%s\n", u.sval);
else
    printf("bad type %d in utype\n", utype);

構造体を使用して同じことを実装できます。何かのようなもの、

struct u_tag {
    utype_t utype;
    int ival;
    float fval;
    char *sval;
} u;

if (u.utype == INT)
    printf("%d\n", u.ival);
if (u.utype == FLOAT)
    printf("%f\n", u.fval);
if (u.utype == STRING)
    printf("%s\n", u.sval);
else
    printf("bad type %d in utype\n", utype);

これは同じではありませんか?どのような利点unionがありますか?

何かご意見は?

4

11 に答える 11

9

あなたが投稿した例では、unionのサイズはfloatのサイズになります(コメントで指摘されているように、それが最大のものであると仮定すると、64ビットコンパイラでは異なる場合があります)が、structのサイズは合計になりますfloat、int、char* および utype_t (およびパディングがある場合) のサイズ。

私のコンパイラの結果:

union u_tag {
    int ival;
    float fval;
    char *sval;
};
struct s_tag {
    int ival;
    float fval;
    char *sval;
};

int main()
{
    printf("%d\n", sizeof(union u_tag));  //prints 4
    printf("%d\n", sizeof(struct s_tag)); //prints 12
    return 0;
}
于 2010-07-28T11:05:54.380 に答える
8

ユニオンは、一度に複数のメンバーにアクセスする必要がない場合に使用できます。そうすれば、構造体を使用する代わりにメモリを節約できます。

あるフィールドに書き込み、別のフィールドから読み取ることで、ビット パターンを検査したり、それらを別の方法で解釈したりできます。

于 2010-07-28T11:17:18.177 に答える
4

Union はメモリの使用量が少なく、より危険なことを実行できます。これは、整数、浮動小数点値、または文字ポインターのいずれかとして解釈できる、1 つの連続したメモリ ブロックを表します。

于 2010-07-28T11:04:36.277 に答える
4

ユニオンは、一度に 1 種類のデータのみを保存するために使用されます。値が再割り当てされると、古い値が上書きされ、アクセスできなくなります。あなたの例では、 int 、 float 、および char メンバーはすべて、構造体として使用されるときにいつでも異なる値を持つことができます。組合ではそうではありません。したがって、プログラムの要件と設計によって異なります。いつユニオンを使用する かについては、この記事を確認してください。Googleはさらに多くの結果を提供する可能性があります。

于 2010-07-28T11:13:19.827 に答える
2

この言語は、高レベルの抽象化を最低レベルのマシン データと操作に適用するための多数の機能をプログラマに提供します。

ただし、何かが存在するだけで、その使用がベスト プラクティスであると自動的に示唆されるわけではありません。それらの存在により、言語は強力で柔軟になります。しかし、業界のニーズにより、可能な限り最高のコード効率やストレージ効率よりも明快さと保守性を優先するプログラミング手法が開発されました。

したがって、問題のソリューション セットに共用体と構造体の両方が含まれている場合、コンパクトなストレージの必要性がコストを上回るかどうかを判断するのはプログラマの責任です。

最近では、メモリのコストが非常に低くなっています。bool 型 (およびそれ以前の int 変数) の導入により、32 ビット システムのプログラマは 32 ビットを使用してバイナリ状態を表すことができました。プログラマーがマスクを使用して変数に 32 個の true/false 値を取得できたとしても、プログラミングでよく見られます。

したがって、あなたの質問に答えるために、共用体は、従来の構造よりもいくつかの可能なタイプから単一の値エンティティに対してよりコンパクトなストレージを提供しますが、明確さと微妙なプログラムの欠陥の可能性を犠牲にします。

于 2010-07-28T11:16:48.743 に答える
1

組合には2つの主な用途があります。

最初に、概説したように、バリアント型を提供します。構造体アプローチとは対照的に、ユニオン内のすべてのメンバー間で共有される1ユニットのメモリがあります。メモリが問題にならない場合は、構造体もこの機能を果たします。

私は通常、構造体に共用体を埋め込みます。構造体は、型とデータが一緒に格納されることを保証します。共用体は、格納される値が1つだけであることを意味します。

struct any_tag {
    utype_t utype;
    union {
        int ival;
        float fval;
        char *sval;
    } u;
} data;

第二に、ユニオンは生データへの低レベルのアクセスに非常に役立ちます-あるタイプを別のタイプとして再解釈します。私がこれを使用した目的は、バイナリエンコードされたデータの読み取りと書き込みです。

float ConvertByteOrderedBufferTo32bitFloat( char* input ) {
union {
    float f;
    unsigned char buf[4];
} data;

#if WORDS_BIGENDIAN == 1
data.buf[0] = input[0];
data.buf[1] = input[1];
data.buf[2] = input[2];
data.buf[3] = input[3];
#else
data.buf[0] = input[3];
data.buf[1] = input[2];
data.buf[2] = input[1];
data.buf[3] = input[0];
#endif

return dat1.f;
}

ここでは、プラットフォームのエンディアンに応じて個々のバイトに書き込み、それらの4つの生のcharバイトをIEEE浮動小数点として解釈できます。そのchar配列をfloatにキャストしても、同じ結果にはなりません。

于 2010-07-28T15:54:42.437 に答える
1

ユニオンを使用してメモリを節約することは、最新のシステムではほとんど行われていません。これは、ユニオン メンバにアクセスするコードが、別のワード サイズの変数をメモリに追加するよりも、すぐにより多くのスペースを消費する (そして遅くなる) ためです。ただし、エンディアンが異なる複数のアーキテクチャをコードでサポートする必要がある場合 (何ということでしょう)、union は便利です。私はエンディアン ユーティリティ ライブラリ (関数)を使用することを好む傾向がありますが、ユニオンが好きな人もいます。

メモリ マップド ハードウェア レジスタも共用体を使用してアクセスするのが一般的です。C のビット フィールド (使用しないでください。意地悪です) は、共用体を使用して単語として渡すことができます。

于 2010-07-28T12:01:42.070 に答える
0

一度に組合員の「...いくつかのタイプのいずれか...」を投稿した引用から借ります。それがまさに組合です。一方、構造体メンバーは一度にすべて割り当ててアクセスできます。

ユニオンは、プロセス通信/同時実行処理などの一部のシステム レベル (os) プログラムを実行する場合により意味があります。

于 2010-07-28T14:44:36.020 に答える
0

ここでの例は理にかなっています。以下の例を参照してください。

union xReg
{
    uint allX;
    struct
    {
        uint x3      : 9;
        uint x2      : 9;
        uint x1      : 14;
    };
};

uintunsigned int の typedef です。

ここで、この共用体は 32 ビット レジスタを表します。allX を使用してレジスタを読み取り、構造体を使用して操作できます。

ビット操作に allX を使用すると、不要なビット シフトが軽減されます。

于 2011-07-01T20:02:04.310 に答える
0

労働組合は厄介です。何年もの間、私はそれらを理解することができませんでしたが、ネットワーク プロトコルに関する作業を始めたところ、誰かが光を見せてくれました。ヘッダーがあるとします。ヘッダーの後には、次のようなさまざまな種類のパケットがあります。

| | タイプ (4 バイト) | uid (8 バイト) | ペイロード長 (2 バイト) | ペイロード (可変長) |

そして、さまざまなタイプのパケット ペイロードが存在します...議論のために、こんにちは、さようなら、およびメッセージ パケットが存在する可能性があります...

まあ、そのプロトコルのパケットを正確に表すことができるネストされた構造体/共用体のセットを構築できます...

struct packet {
  uint type;
  char unique_id [8];
  ushort payload_length;
  union payload {

    struct hello {
      ushort version;
      uint status;
    };

    struct goodbye {
      char reason[20];
      uint status;
    };

    struct message {
      char message[100];
    };

  };
};

必然的に、オペレーティング システムから read() 呼び出しを介してこのプロトコルを取得しますが、これは単なるバイトの寄せ集めです。しかし、構造体の定義に注意を払い、すべての型が適切なサイズであれば、単純に構造体へのポインターを作成し、ランダム データで満たされたバッファーを指すようにすることができます。

char buf[100];
packet *pkt;
read(outsideworld,&buf,1000);
pkt = (struct packet *)&buf;

パケットの読み取りは簡単です...

switch(pkt->type){

  case PACKET_MESSAGE:
    printf("message = %s\n",
           pkt->payload.message.message);
    break;

  case PACKET_HELLO:
    printf("hello! version = %d status = %d\n",
           pkt->payload.hello.version,
           pkt->payload.hello.status);
    break;
  case PACKET_GOODBYE:
    printf("goodbye! reason = %s status = %d\n",
           pkt->payload.goodbye.reason,
           pkt->payload.goodbye.status);
    break;
}

うろついたり、バイトを数えたりする必要はありません...これを好きなだけ深くネストできます(IPアドレスのユニオンを作成すると、すべてがunsigned intまたは個々のバイトとして得られるため、192.168.うち0.1)。

共用体はすべてマシン コードのオフセットに変換されるだけなので、コードの速度が低下することはありません。

于 2010-07-29T06:34:17.677 に答える
0

前述のとおり、共用体はメモリを節約します。しかし、違いはこれだけではありません。Stucts は指定されたすべてのサブタイプを保存するように作成され、union は指定されたサブタイプの 1 つだけを保存するように作成されます。したがって、整数または浮動小数点数のいずれかを保存する場合は、おそらく共用体が必要です (ただし、保存した数値の種類を別の場所に覚えておく必要があります)。両方を格納する場合は、構造体が必要です。

于 2010-07-28T13:57:36.073 に答える