For example,

union { float fl, unsigned int uinteg, char ch, int integ } foo; 

All this is stored alternately in one area of ​​memory. What's the point, because once you set the values

 foo.fl = 3.14f; foo.uinteg = 666; foo.ch = 'a'; foo.int = -25; 

it won't be possible to get them back - will everything get mixed up? A way to save a couple of bytes or a couple of clocks and at the same time save readability? Not to write 4 different functions, but to write one that takes the union and already decide what to do? In this case, isn't it easier to accept void * and then cast into the type you need? As an example, "Just cast" I will give this code:

Classic example:

 typedef enum { STR, INT } tType; typedef struct { tType typ; // typ is separate. union { int ival; // ival and sval occupy same memory. char *sval; }; } tVal; void printer(tVal uni) { if (uni.type == INTEGER) // blah-blah uni.ival // Используем integer else ini.sval // В противном случае } 

The function of the printer can be rewritten something like this:

 void printer(void* data, tType typ) { if (tType == INTEGER) { (int*)data // Чего-то делаем } } 

Another example:

 union { int a; int b; int c; } bar; bar.a = 20; bar.b = 50; // Значение a потеряли :( 

Again, what's the point if I can first get a separate variable int a = 20; and then change its value a = 50; and the effect is exactly the same? Looks like a strong witchcraft.

    4 answers 4

    Union s are used in two cases:

    1. To create a "universal" data type that can store not only one, but one of the predefined types. To do this, an integer field is added to the union, indicating the type of data currently stored:

       struct variant { union tag_value { int intValue; float floatValue } value; unsigned storedType; }; 

      One example of such a real-life application is the Windows API VARIANT structure.

      In other words, this is the predecessor of the modern boost::variant , QVariant , etc. However, the above classes can store non-primitive types (with constructors, destructors, and copy operators), but the union does not.

    2. To convert between incompatible types. Traditionally, the conversion operator (T) or reinterpret_cast<> used for these purposes. However, these methods are dangerous by violating the strict aliasing rule and, as a result, by causing indefinite (that is, unpredictable) behavior.

      The correct conversion methods are either memcpy (a similar call to which is thrown by the compiler), or using a union .

      UPD: Attention! Conversion via union is valid only in C , but not in C ++. In the answer to the question “Accessing inactive union member and undefined behavior?”, References to the following items of standards are given:

      • 6.5.2.3 Structures and members of associations

        95) If the field used to read the contents of the union object is not the field used previously to write the value to this object, the required part of the internal representation of the object is interpreted according to the presentation of the requested type according to 6.2.6 (this process is also known as type punning ). This view may lead to undefined behavior.


        6.5.2.3 Structure and union members

        95) It’s not. type as described in 6.2.6 (a process sometimes called “type punning”). This might be a trap representation.

      • (no explicit permission for type punning)

        9.5 Unions [class.union]

        In a union, only one non-static field can be active at a time; as a result, at any time there can be at most one value in the union.


        9.5 Unions [class.union]

        If you are not a member of a group of people, you can’t be able to work at all times.

    • 2
      Doesn't union result in the same ub? It seemed to me that you can only read in the field in which he wrote. - pavel
    • This raises the question - why a universal type? For comfort? VARIANT from WinAPI does not look convenient - as much blood has flowed from the eyes. Than void* + the second parameter with type did not please as universal? printf can print anything here and no tricky structures need to be sent there. Or is it a matter of taste? - m9_psy
    • @pavel, added the answer. - ߊߚߤߘ
    • @ m9_psy, void* requires additional memory allocation on the heap (for the pointer). Union keeps everything in itself. - ߊߚߤߘ
    • one
      @Arhad: trap representation is, as I understand it, not an “incorrect value”, it is “a value that reads when UB occurs”. So the difference between C and C ++ is not that big. - VladD

    Here is what Bjarne Straustrup answered me in due time to a similar question:

    For each one at a time. I don't use them much.

    In other words, in our time they are, in general, not needed.

    • Not needed, however, used. I stumbled upon them (and unknowingly took it for a structure and was perplexed) in the Vulkan API - fresh, the year it came out. The most that neither is "our time". - m9_psy
    • @ m9_psy, used, yes. But, as you said, now it is more like witchcraft. - wakwist
    • one
      If you don’t think about memory (processor cache, passing arguments to functions in registers, etc. things that are not significant for modern programmers), then you probably don’t need them (you can always replace them with just a structure) - avp

    One practical way to use union is to access the bits of information transmitted. Suppose information is transmitted bit by bit at a time 32 bits.

     struct _info_s { uint32_t info_a:2; uint32_t info_b:10; uint32_t info_c:8; uint32_t info_d:5; uint32_t info_e:5; uint32_t info_f:3; }__atributte__((packed)); typedef struct _info_s info_s; union _total_u { uint32_t array; info_s info; }; 

    When reading / writing, we can immediately operate uint32_t , but access to the bits appears immediately.

    • one
      why do you have 33 bits in total? ;-) - Akubik

    If I understood correctly, this is a combination of variables in memory. Sometimes Pts is convenient if you want to see a variable in a different light or you only need to access part of a variable ...

    For example, a String can be interpreted as a byte [] array or a rectangular byte [] [2] array, a integer can be decomposed into byte_lo and byte_hi

    • one
      What you are talking about is undefined behavior in C ++ (and most likely in C). Take a look back @Arhad. - VladD