いつユニオンを使うのですか?Cのみの時代の名残ですか?


133

私は学びましたが、実際には組合を取得しません。私が通過するすべてのCまたはC ++テキストはそれらを紹介します(時々)が、それらを使用する理由または場所の実用的な例はほとんどありません。現代の(またはレガシーの)場合に組合はいつ役立つでしょうか?使用するスペースが非常に限られている場合、またはAPI(または同様の何か)を開発していて、エンドユーザーに複数のオブジェクト/タイプのインスタンスを1つだけに強制したい場合、私の2つの推測はマイクロプロセッサのプログラミングです。一度。これらの2つの推測は右に近いですか?


31
C / C ++は言語ではありません。共用体はCでは適度に役立ち、C ++ではほとんど役に立ちません。C ++で、彼らは「レムナントC ++からCに基づいている」だと言うことは正しいことだろうが、ないと言って、彼らしている「Cからレムナントは唯一の日」C ++代わるC.かのように
R .. GitHubのICEのヘルプの停止

12
ユニオンの代わりにc ++が何であるか、またはなぜそれらがc ++で役に立たないのかについて詳しく説明できますか?
ラッセル'25年

3
C ++の共用体の代替は、クラスと継承です。Cの共用体は、ほとんどの場合、タイプセーフなポリモーフィズムにのみ使用されます。何かのクラスははるかに優れています。(Cスタイルのポリモーフィズムについてはvz0の回答を参照)
tobyodavies

6
@R ..:共用体は、C ++ではまだ適度に有用です。以下の回答をご覧ください。
マイケル

2
ユニオンは、オペレーティングシステムの内部で、または、たとえば、サウンドファイルをアセンブル/逆アセンブルするパッケージで、非常に価値があります。このようなコンテキストでは、データ/エンディアン変換、低レベルのポリモーフィズムなど、複数の異なる方法で使用されます。はい、同じ問題に対する他の解決策(主にポインター型間のキャスト)がありますが、共用体は多くの場合、よりクリーンで自己文書化されています。
ホットリックス14

回答:


105

共用体は通常、弁別子と一緒に使用されます。これは、共用体のどのフィールドが有効かを示す変数です。たとえば、独自のバリアント型を作成するとします。

struct my_variant_t {
    int type;
    union {
        char char_value;
        short short_value;
        int int_value;
        long long_value;
        float float_value;
        double double_value;
        void* ptr_value;
    };
};

次に、次のように使用します。

/* construct a new float variant instance */
void init_float(struct my_variant_t* v, float initial_value) {
    v->type = VAR_FLOAT;
    v->float_value = initial_value;
}

/* Increments the value of the variant by the given int */
void inc_variant_by_int(struct my_variant_t* v, int n) {
    switch (v->type) {
    case VAR_FLOAT:
        v->float_value += n;
        break;

    case VAR_INT:
        v->int_value += n;
        break;
    ...
    }
}

これは実際にはかなり一般的なイディオムであり、特にVisual Basicの内部ではそうです。

実際の例については、SDLのSDL_Event共用体を参照してください。(実際のソースコードはこちら)。あるtype労働組合の上部のフィールドは、同じフィールドがすべてのSDL_ *イベントの構造体の上に繰り返されます。次に、正しいイベントを処理するために、typeフィールドのます。

利点は単純です。不要なメモリを使用せずにすべてのイベントタイプを処理できる単一のデータタイプがあります。


2
すごい!その場合、なぜSdl関数がクラス階層として実装されなかったのか不思議に思っています。それはC ++だけでなくC互換にするためですか?
ラッセル

12
@Russel C ++クラスはCプログラムからは使用できませんが、C構造体/共用体は、 'extern "C"ブロックを使用してC ++から簡単にアクセスできます。
vz0 '25年

1
このバリアントパターンは、プログラミング言語インタープリターでもよく使用されます。たとえば、github.com / petermichaux / bootstrapstruct object
Adam Rosenfield

1
素晴らしい説明。私は常に労働組合が何であるかを知っていましたが、誰もがそれらを使用するのに十分狂っている理由の実際の理由を見たことはありません:)例をありがとう。
riwalk 2011

Stargazer712、GoogleのCodeSearchを@:google.com/...
kagaliさん

87

C ++のunionはかなりクールだと思います。人々は通常、unionインスタンスの値を「その場で」変更したいというユースケースのみを考えているようです(これは、メモリを節約するか、疑わしい変換を実行するためにのみ役立つようです)。

実際、ユニオンインスタンスの値を変更しない場合でも、ユニオンはソフトウェアエンジニアリングツールとして非常に役立ちます

ユースケース1:カメレオン

ユニオンを使用すると、1つの金種の下で多数の任意のクラスを再グループ化できます。これは、基本クラスとその派生クラスの場合との類似点がないわけではありません。ただし、変更点は、特定のユニオンインスタンスで実行できることと実行できないことです。

struct Batman;
struct BaseballBat;

union Bat
{
    Batman brucewayne;
    BaseballBat club;
};

ReturnType1 f(void)
{
    BaseballBat bb = {/* */};
    Bat b;
    b.club = bb;
    // do something with b.club
}

ReturnType2 g(Bat& b)
{
    // do something with b, but how do we know what's inside?
}

Bat returnsBat(void);
ReturnType3 h(void)
{
    Bat b = returnsBat();
    // do something with b, but how do we know what's inside?
}

プログラマーは、特定の共用体インスタンスを使用する場合、そのコンテンツのタイプを特定する必要があるようです。f上記の機能の場合です。ただし、g上記の場合のように、渡された引数として関数が共用体インスタンスを受け取る場合、関数はそれをどうするかわかりません。同じことがunionインスタンスを返す関数にも当てはまります。参照h:呼び出し元は内部が何であるかをどのようにして知るのですか?

unionインスタンスが引数または戻り値として渡されない場合、プログラマーがコンテンツを変更することを選択したときに興奮のスパイクが発生し、非常に単調な人生を送ることになります。

Batman bm = {/* */};
Baseball bb = {/* */};
Bat b;
b.brucewayne = bm;
// stuff
b.club = bb;

そしてそれが最も人気のある(非)ユニオンの使用例です。もう1つの使用例は、共用体インスタンスがその型を通知する何かを伴う場合です。

使用例2:「はじめまして」objectからClass

プログラマがunionインスタンスと型記述子を常にペアにすることを選択したとします(そのようなオブジェクトの実装を想像するのは読者の裁量に任せます)。プログラマがメモリを節約することを望んでおり、型記述子のサイズが共用体のサイズに関して無視できない場合、これは共用体自体の目的を無効にします。しかし、unionのインスタンスが引数または戻り値として渡され、呼び出し先または呼び出し元が何が入っているかを知らないことが重要であるとしましょう。

次に、プログラマは switch制御フローステートメントを記述して、ブルースウェインに木製の棒またはそれと同等のものを区別させる必要があります。ユニオンにコンテンツが2種類しかない場合でもそれほど悪くはありませんが、明らかに、ユニオンはもうスケーリングしません。

ユースケース3:

ISO C ++標準の勧告の作成者が2008年にそれを戻したように、

多くの重要な問題ドメインには、多数のオブジェクトまたは限られたメモリリソースのいずれかが必要です。これらの状況では、スペースを節約することが非常に重要であり、多くの場合、ユニオンはそのための完璧な方法です。実際、一般的なユースケースは、組合がその有効期間中にアクティブメンバーを変更しない状況です。メンバーを1つだけ含む構造体であるかのように、構築、コピー、破棄できます。これの典型的なアプリケーションは、動的に割り当てられない無関係なタイプの異種のコレクションを作成することです(おそらく、それらはマップ内でインプレースで構築されるか、配列のメンバーです)。

そして今、UMLクラス図の例:

クラスAの多くの楽曲

平易な英語の状況:クラスAのオブジェクトは、B1、...、Bn、および各タイプの多くても1つのクラスのオブジェクトを持つことできます。nはかなり大きな数で、たとえば少なくとも10です。

次のようにフィールド(データメンバー)をAに追加したくありません。

private:
    B1 b1;
    .
    .
    .
    Bn bn;

nが変化する可能性があるため(Bxクラスをミックスに追加する場合がある)、これによりコンストラクターが混乱し、Aオブジェクトが多くのスペースを占有するためです。

キャストを使用したオブジェクトvoid*へのポインターの奇抜なコンテナーを使用してBxそれらを取得することもできますが、それはあいまいでCスタイルですが、さらに重要なことに、動的に割り当てられた多くのオブジェクトのライフタイムを管理することができます。

代わりに、できることは次のとおりです。

union Bee
{
    B1 b1;
    .
    .
    .
    Bn bn;
};

enum BeesTypes { TYPE_B1, ..., TYPE_BN };

class A
{
private:
    std::unordered_map<int, Bee> data; // C++11, otherwise use std::map

public:
    Bee get(int); // the implementation is obvious: get from the unordered map
};

次に、からユニオンインスタンスのコンテンツを取得するには、dataなどを使用a.get(TYPE_B2).b2します。ここaで、はクラスAインスタンスです。

ユニオンはC ++ 11では制限がないため、これはさらに強力です。詳細については、上記のリンク先のドキュメントまたはこの記事を参照してください。


これは非常に役に立ち、その2番目の記事のシリーズは非常に有益でした。ありがとう。
Andrew

38

1つの例は組み込みレルムで、レジスターの各ビットが異なる何かを意味する場合があります。たとえば、8ビット整数と8つの1ビットビットフィールドを持つ構造体の和集合では、1ビットまたはバイト全体を変更できます。


7
これは、デバイスドライバーでも非常に一般的です。数年前、私はプロジェクトにこのような共用体を使用して多くのコードを書きました。これは通常は推奨されず、場合によってはコンパイラ固有の場合もありますが、機能します。
thkala 2011年

11
私はそれを「非推奨」とは呼びません。埋め込まれた空間では、多くの場合、多くの明示的なキャストとvoid*sまたはマスクとシフトを伴う代替案よりもはるかにクリーンでエラーが発生しにくくなります。
bta

え?明示的なキャストがたくさんありますか?以下のような単純な文私には思えるREG |= MASKREG &= ~MASK。エラーが発生しやすい場合は、#define SETBITS(reg, mask)とに入れます#define CLRBITS(reg, mask)。特定の順序(のビットを取得するために、コンパイラに依存しないでくださいstackoverflow.com/questions/1490092/...
マイケル

26

Herb SutterGOTWで約6年前に次の点を強調して書いています

「しかし、ユニオンは以前からのホールドオーバーに過ぎないとは考えないでください。ユニオンはおそらくデータのオーバーラップを可能にすることでスペースを節約するために最も有用であり、これはC ++や今日の現代の世界では依然として望ましいです。たとえば、高度なC ++世界の標準ライブラリの実装では、この手法だけを使用して「小さな文字列の最適化」を実装しています。これは、文字列オブジェクト自体の内部のストレージを再利用する優れた最適化の代替手段です。大きな文字列の場合、文字列オブジェクトの内部のスペースには、割り当てられたバッファおよびバッファのサイズなどのハウスキーピング情報。小さな文字列の場合は、同じスペースを代わりに再利用して文字列の内容を直接格納し、動的なメモリ割り当てを完全に回避します。小さな文字列の最適化(およびその他の文字列の最適化とかなりの深さでの悲観化)の詳細については、...を参照してください。 "

そして、あまり役に立たない例については、長いが決定的ではない質問gcc、厳密なエイリアス、およびunionを介したキャストを参照してください。


23

まあ、私が考えることができる1つの使用例はこれです:

typedef union
{
    struct
    {
        uint8_t a;
        uint8_t b;
        uint8_t c;
        uint8_t d;
    };
    uint32_t x;
} some32bittype;

その後、その32ビットデータブロックの8ビットの個別の部分にアクセスできます。ただし、エンディアンによってかまれる可能性を準備してください。

これは架空の例の1つにすぎませんが、フィールド内のデータをこのようにコンポーネントパーツに分割したい場合はいつでも、共用体を使用できます。

とはいえ、エンディアンセーフなメソッドもあります:

uint32_t x;
uint8_t a = (x & 0xFF000000) >> 24;

たとえば、そのバイナリ演算はコンパイラによって正しいエンディアンに変換されるためです。


労働組合をいつ使うべきかという質問が一番いいと思います。あなたは組合が正しいツールではない場所の答えを提供しました、私はこの答えでより明確にすべきだと思います。
Michael

15

労働組合のいくつかの用途:

  • 未知の外部ホストへの一般的なエンディアンインターフェイスを提供します。
  • ネットワークリンクからVAX G_FLOATSを受け入れ、IEEE 754 long realに変換して処理するなど、外部CPUアーキテクチャの浮動小数点データを操作します。
  • 上位レベルの型への簡単なビットいじりアクセスを提供します。
union {
      unsigned char   byte_v[16];
      long double     ld_v;
 }

この宣言を使用すると、の16進バイト値を表示しlong doubleたり、指数の符号を変更したり、それが非正規値であるかどうかを判断したり、それをサポートしないCPUにlong double演算を実装したりすることが簡単になります。

  • フィールドが特定の値に依存している場合のストレージスペースの節約:

    class person {  
        string name;  
    
        char gender;   // M = male, F = female, O = other  
        union {  
            date  vasectomized;  // for males  
            int   pregnancies;   // for females  
        } gender_specific_data;
    }
  • コンパイラで使用するインクルードファイルをGrepします。数十から数百の用途がありunionます。

    [wally@zenetfedora ~]$ cd /usr/include
    [wally@zenetfedora include]$ grep -w union *
    a.out.h:  union
    argp.h:   parsing options, getopt is called with the union of all the argp
    bfd.h:  union
    bfd.h:  union
    bfd.h:union internal_auxent;
    bfd.h:  (bfd *, struct bfd_symbol *, int, union internal_auxent *);
    bfd.h:  union {
    bfd.h:  /* The value of the symbol.  This really should be a union of a
    bfd.h:  union
    bfd.h:  union
    bfdlink.h:  /* A union of information depending upon the type.  */
    bfdlink.h:  union
    bfdlink.h:       this field.  This field is present in all of the union element
    bfdlink.h:       the union; this structure is a major space user in the
    bfdlink.h:  union
    bfdlink.h:  union
    curses.h:    union
    db_cxx.h:// 4201: nameless struct/union
    elf.h:  union
    elf.h:  union
    elf.h:  union
    elf.h:  union
    elf.h:typedef union
    _G_config.h:typedef union
    gcrypt.h:  union
    gcrypt.h:    union
    gcrypt.h:    union
    gmp-i386.h:  union {
    ieee754.h:union ieee754_float
    ieee754.h:union ieee754_double
    ieee754.h:union ieee854_long_double
    ifaddrs.h:  union
    jpeglib.h:  union {
    ldap.h: union mod_vals_u {
    ncurses.h:    union
    newt.h:    union {
    obstack.h:  union
    pi-file.h:  union {
    resolv.h:   union {
    signal.h:extern int sigqueue (__pid_t __pid, int __sig, __const union sigval __val)
    stdlib.h:/* Lots of hair to allow traditional BSD use of `union wait'
    stdlib.h:  (__extension__ (((union { __typeof(status) __in; int __i; }) \
    stdlib.h:/* This is the type of the argument to `wait'.  The funky union
    stdlib.h:   causes redeclarations with either `int *' or `union wait *' to be
    stdlib.h:typedef union
    stdlib.h:    union wait *__uptr;
    stdlib.h:  } __WAIT_STATUS __attribute__ ((__transparent_union__));
    thread_db.h:  union
    thread_db.h:  union
    tiffio.h:   union {
    wchar.h:  union
    xf86drm.h:typedef union _drmVBlank {

5
Tsk tsk!2つの反対投票と説明なし。それは残念です。
wallyk 2013

男と女を抱くことができる人の例は、私の目には非常に悪いデザインです。人の基本クラスと男性と女性がクラスを派生させないのはなぜですか?申し訳ありませんが、データフィールドに格納されている型を特定するために手動で変数を探すことは、まったくお勧めできません。これは、長年見られなかった手作りのcコードです。しかし、反対票はありません。それは私の視点に過ぎません:-)
クラウス

4
「去勢」または「妊娠」組合の反対票を得たと思います。それは少し病気です。
akaltar

2
ええ、それは暗い日だったと思います。
wallyk

14

ユニオンは、バイトレベル(低レベル)のデータを処理するときに役立ちます。

私の最近の使用法の1つは、以下のようなIPアドレスモデリングでした。

// Composite structure for IP address storage
union
{
    // IPv4 @ 32-bit identifier
    // Padded 12-bytes for IPv6 compatibility
    union
    {
        struct
        {
            unsigned char _reserved[12];
            unsigned char _IpBytes[4];
        } _Raw;

        struct
        {
            unsigned char _reserved[12];
            unsigned char _o1;
            unsigned char _o2;
            unsigned char _o3;
            unsigned char _o4;    
        } _Octet;    
    } _IPv4;

    // IPv6 @ 128-bit identifier
    // Next generation internet addressing
    union
    {
        struct
        {
            unsigned char _IpBytes[16];
        } _Raw;

        struct
        {
            unsigned short _w1;
            unsigned short _w2;
            unsigned short _w3;
            unsigned short _w4;
            unsigned short _w5;
            unsigned short _w6;
            unsigned short _w7;
            unsigned short _w8;   
        } _Word;
    } _IPv6;
} _IP;

7
ただし、そのような生のものへのアクセスは標準ではなく、すべてのコンパイラで期待どおりに機能しない可能性があることに注意してください。
nos

3
また、これが未定義の動作である整列を保証しない方法で使用されるのを見るのは非常に一般的です。
Mooing Duck 2013

10

ユニオンを使用した例:

class Vector
{
        union 
        {
            double _coord[3];
            struct 
            {
                double _x;
                double _y; 
                double _z;
            };

        };
...
}

これにより、配列または要素としてデータにアクセスできます。

ユニオンを使用して、異なる用語が同じ値を指すようにしました。画像処理では、列、幅、X方向のサイズのいずれで作業していても、混乱する可能性があります。この問題を緩和するために、私は共用体を使用して、どの説明が一緒に行くかを知っています。

   union {   // dimension from left to right   // union for the left to right dimension
        uint32_t            m_width;
        uint32_t            m_sizeX;
        uint32_t            m_columns;
    };

    union {   // dimension from top to bottom   // union for the top to bottom dimension
        uint32_t            m_height;
        uint32_t            m_sizeY;
        uint32_t            m_rows;
    };

12
このソリューションはほとんどの監視可能なプラットフォームで機能しますが、_x、_y、_zに値を設定し、_coordにアクセスすることは未定義の動作です。労働組合の主な目的は、スペースの節約です。以前に設定したのと同じunion要素にアクセスする必要があります。
anxieux 2013年

1
これは、私もstd :: array forr coordsといくつかのstatic_assertsを使用する方法でも使用しています
Viktor Sehr

1
このコードは厳密なエイリアシングルールに違反しているため、推奨できません。
ウォルター

これを行うことが信頼できるように組合を改善する方法はおそらくありますか?
Andrew

8

共用体はCで多態性を提供します


18
私は思ったvoid*^^ということでした

2
@ user166390ポリモーフィズムは、同じインターフェースを使用して複数のタイプを操作します。void *にはインターフェースがありません。
アリス

2
Cでは、ポリモーフィズムは一般に、不透明な型や関数ポインタを通じて実装されます。あなたがそれを達成するためにどうやって、なぜ組合を使うのか私にはわかりません。それは本当に悪い考えのように思えます。
ランディン2017年

7

unionの優れた使用法は、PCL(Point Cloud Library)のソースコードで見つけたメモリアライメントです。APIの単一のデータ構造は、SSEをサポートするCPUとSSEをサポートしないCPUの2つのアーキテクチャをターゲットにすることができます。たとえば、PointXYZのデータ構造は次のとおりです。

typedef union
{
  float data[4];
  struct
  {
    float x;
    float y;
    float z;
  };
} PointXYZ;

3つのフロートには、SSEアラインメント用の追加のフロートが埋め込まれています。だから

PointXYZ point;

ユーザーは、たとえばX座標にアクセスするために、point.data [0]またはpoint.x(SSEサポートに依存)にアクセスできます。よりよく似た使用法の詳細は、次のリンクにあります:PCLドキュメントPointTタイプ


7

このunionキーワードは、C ++ 03 1で引き続き使用されていますが、ほとんどの場合、C日の名残りです。最も重大な問題は、POD 1でのみ機能することです。

ただし、ユニオンの概念はまだ存在しており、実際にBoostライブラリはユニオンのようなクラスを備えています。

boost::variant<std::string, Foo, Bar>

union(すべてではないにしても)のほとんどの利点があり、以下が追加されます。

  • 非PODタイプを正しく使用する機能
  • 静的型安全性

実際には、union+の組み合わせと同等であることが実証されておりenum、同じくらい高速であることがベンチマークされています(RTTIを使用しているためboost::any、のレルムの方が多くdynamic_castなります)。

1ユニオンはC ++ 11(無制限のユニオン)でアップグレードされ、ユーザーはデストラクタを手動で(現在アクティブなユニオンメンバで)呼び出す必要がありますが、デストラクタを含むオブジェクトを含めることができます。バリアントを使用する方がはるかに簡単です。


これは、最近のバージョンのc ++では当てはまりません。たとえば、jrsalaの回答を参照してください。
Andrew

@Andrew:無制限の共用体を使用するC ++ 11では、デストラクタを含む型を共用体に格納できることを説明するように、回答を更新しました。私はまだあなたが本当にはるかに優れて使用してオフになっているというのが私のスタンスに立つタグ付けされた労働組合をのようなboost::variant自分で労働組合を使用しようとするよりも。ユニオンを取り巻く未定義の動作が多すぎて、正しく機能する可能性が非常に低いです。
Matthieu M.

3

組合に関するウィキペディアの記事から:

unionの主な有用性は、多くの異なるタイプを同じスペースに格納できるようにするため、スペース節約することです。組合はまた 、粗雑な多型を提供します。ただし、タイプのチェックは行われないため、適切なフィールドが異なるコンテキストでアクセスされることを確認するのはプログラマの責任です。共用体変数の関連フィールドは、通常、おそらく囲んでいる構造体の中の他の変数の状態によって決定されます。

共通のCプログラミングイディオムの1つは、共用体を使用して、C ++がreinterpret_castを呼び出すものを実行します。これは、値の生の表現に依存するコードで行われるように、共用体の1つのフィールドに割り当て、別のフィールドから読み取ることによって行います。


2

Cの初期の頃(たとえば1974年に文書化されたとおり)、すべての構造体はメンバーの共通の名前空間を共有していました。各メンバー名はタイプとオフセットに関連付けられていました。「wd_woozle」がオフセット12の「int」の場合、ポインタが与えられますpある場合、任意の構造体型のをp->wd_woozleと、と同等になり*(int*)(((char*)p)+12)ます。言語では、すべての構造タイプのすべてのメンバーに一意の名前が必要ですをただし、メンバーが使用されているすべての構造体が共通の初期シーケンスとして処理する場合に、メンバー名の再利用を明示的に許可していました。

構造タイプを無差別に使用できるという事実により、構造が重複フィールドを含んでいるかのように動作させることが可能になりました。たとえば、与えられた定義:

struct float1 { float f0;};
struct byte4  { char b0,b1,b2,b3; }; /* Unsigned didn't exist yet */

コードは「float1」型の構造を宣言し、「メンバー」b0 ... b3を使用してその中の個々のバイトにアクセスできます。言語が変更されて各構造がそのメンバーの個別の名前空間を受け取るようになると、複数の方法で物事にアクセスする機能に依存するコードが機能しなくなります。異なる構造タイプの名前空間を分離することの価値は、そのようなコードをそれに対応するように変更することを要求するには十分でしたが、そのような手法の価値は、言語を拡張してサポートし続けることを正当化するのに十分でした。

内のストレージにアクセスする機能を利用するために書かれたコードstruct float1、それはあたかもstruct byte4:宣言を追加することによって、新しい言語で動作させることができunion f1b4 { struct float1 ff; struct byte4 bb; };、種類などのオブジェクトを宣言するunion f1b4;のではなくstruct float1、およびへのアクセスを置き換えf0b0b1など、 。でff.f0bb.b0bb.b1そのようなコードがサポートされている可能性がより良い方法があるが、等、unionアプローチは、少なくとも折り返しルールのC89時代の解釈と、少なくともいくらか実行可能でした。


1

n個の異なるタイプの構成があるとしましょう(パラメーターを定義する変数のセットにすぎません)。構成タイプの列挙を使用することにより、構成タイプのIDを持つ構造と、すべての異なる構成タイプの結合を定義できます。

このように、構成を渡す場所はどこでもIDを使用して構成データを解釈する方法を決定できますが、構成が巨大な場合でも、潜在的なタイプの無駄なスペースごとに並列構造を持つ必要はありません。


1

すでに上昇しているユニオンの重要性に対する最近の1つのブーストは、C標準の最近のバージョンで導入されたStrict Aliasing Ruleによって与えられました。

C規格に違反することなく、共用体を使用して型パンニングを行うことができます。
このプログラムには不特定の動作がありますされていません(私は同じ長さfloatと仮定してunsigned intいるため)が、未定義の動作ではありません(ここを参照)。

#include <stdio.h> 

union float_uint
{
    float f;
    unsigned int ui;
};

int main()
{
    float v = 241;
    union float_uint fui = {.f = v};

    //May trigger UNSPECIFIED BEHAVIOR but not UNDEFINED BEHAVIOR 
    printf("Your IEEE 754 float sir: %08x\n", fui.ui);

    //This is UNDEFINED BEHAVIOR as it violates the Strict Aliasing Rule
    unsigned int* pp = (unsigned int*) &v;

    printf("Your IEEE 754 float, again, sir: %08x\n", *pp);

    return 0;
}

タイプアクセスルールは、標準の「最近の」バージョンだけではありません。Cのすべてのバージョンには、基本的に同じルールが含まれています。変更されたのは、脚注「このリストの目的は、オブジェクトがエイリアス化される場合とされない場合がある状況を指定することです」というコンパイラの使用です。として書かれたエイリアス含まない場合にルールが適用されることを意図していないことを示していたとして、彼らは今それが何もなかったエイリアスを作成するためにコードを書き直すための招待として扱います。
スーパーキャット2018年

1

ユニオンを使用するための1つの優れた実用的な例を追加したいと思います-数式計算/インタープリターの実装またはある種の計算での使用(たとえば、計算式のランタイム部分で変更可能を使用したい-数式を数値で解く-ちょうど例えば)。したがって、次のようにさまざまなタイプ(整数、浮動小数点、さらには複素数)の数値/定数を定義することができます。

struct Number{
enum NumType{int32, float, double, complex}; NumType num_t;
union{int ival; float fval; double dval; ComplexNumber cmplx_val}
}

したがって、メモリとより重要なものを節約することになります-(クラス定義継承/ポリモーフィズムによる実装と比較して)小さなオブジェクトの(極端に大量のランタイム定義数を使用する場合)おそらく動的割り当てを回避します。しかし、さらに興味深いのは、このタイプの構造体でC ++ポリモーフィズムのパワーを使用できることです(たとえば、ダブルディスパッチのファンであれば、;)。この構造体のフィールドとして、すべての数値タイプの親クラスに「ダミー」インターフェースポインターを追加し、追加するか、生の型の代わりに/に加えてこのインスタンスをか、古き良きC関数ポインターを使用します。

struct NumberBase
{
virtual Add(NumberBase n);
...
}
struct NumberInt: Number
{
//implement methods assuming Number's union contains int
NumberBase Add(NumberBase n);
...
}
struct NumberDouble: Number
{
 //implement methods assuming Number's union contains double
 NumberBase Add(NumberBase n);
 ...
}
//e.t.c. for all number types/or use templates
struct Number: NumberBase{
 union{int ival; float fval; double dval; ComplexNumber cmplx_val;}
 NumberBase* num_t;
 Set(int a)
 {
 ival=a;
  //still kind of hack, hope it works because derived classes of   Number    dont add any fields
 num_t = static_cast<NumberInt>(this);
 }
}

そのため、必要に応じて、switch(type)で型チェックの代わりにポリモーフィズムを使用できます-メモリ効率の高い実装(小さなオブジェクトの動的割り当てなし)。


これは、動的言語を作成するときに役立ちます。私がそれで解決すると思う問題は、その変更をN回実装することなく、未知の型の変数を大量に変更することです。マクロはこれに対して恐ろしいものであり、テンプレート化は事実上不可能です。
Andrew

0

http://cplus.about.com/od/learningc/ss/lowlevel_9.htmから:

unionの使用はほとんどありません。ほとんどのコンピューターでは、ポインターとintのサイズは通常同じです。これは、どちらも通常CPUのレジスターに収まるためです。そのため、intへのポインターのキャストをすばやくダーティにしたい場合は、unionを宣言します。

union intptr {   int i;   int * p; }; 
union intptr x; x.i = 1000; 
/* puts 90 at location 1000 */ 
*(x.p)=90; 

ユニオンのもう1つの用途は、コマンドまたはメッセージプロトコルであり、さまざまなサイズのメッセージが送受信されます。各メッセージタイプは異なる情報を保持しますが、それぞれに固定部分(おそらく構造体)と可変部分ビットがあります。これは、実装方法です。

struct head {   int id;   int response;   int size; }; struct msgstring50 {    struct head fixed;    char message[50]; } struct

struct msgstring80 {構造体のヘッドが修正されました。char message [80]; }
struct msgint10 {struct head fixed; int message [10]; } struct msgack {struct head fixed; int ok; } union messagetype {
struct msgstring50 m50; struct msgstring80 m80; struct msgint10 i10; struct msgack ack; }

実際には、共用体は同じサイズですが、意味のあるデータのみを送信し、スペースを無駄にしないのは理にかなっています。msgack80のサイズは92バイトですが、msgackのサイズはちょうど16バイトです。そのため、messagetype変数が初期化されると、その型に応じてサイズフィールドが設定されます。これは、他の関数が正しいバイト数を転送するために使用できます。


0

ユニオンは、プログラムにマシンに依存しない情報を埋め込むことなく、単一のストレージ領域でさまざまな種類のデータを操作する方法を提供します。これらはパスカルのバリアントレコードに類似しています。

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

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

変数uは、3つのタイプのうち最大のものを保持するのに十分な大きさになります。特定のサイズは実装に依存します。これらのタイプのいずれかがuに割り当てられ、使用法が一貫している限り、式で使用されます。

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