The advantages of using union when the same thing can be done using struct-C

I find it difficult to understand the use of union in C. I read a lot of posts here about SO about the subject. But not one of them explains why union is preferred when the same can be achieved with structure.

Quote from K & R

As an example, for example, you can find in the compiler symbol table manager, suppose that the constant can be int, a float or a pointer to a symbol. the value of a particular constant should be stored in a variable of the appropriate type, but it is most convenient for managing a table if the value takes up the same amount of storage and is stored in the same place regardless of its type. This unification goal is the only variable that can legally hold any of several species. The syntax is based on the structure:

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

Use will be

 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); 

The same thing can be done with struct. Something like,

 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); 

Is that not so? What advantage does union give?

Any thoughts?

+6
c struct data-structures unions
source share
11 answers

In the given example, the size of the union will be the size of a float (if it is the largest), as indicated in the comments, it can vary in a 64-bit compiler), and the size of the structure is the sum of the values ​​of float, int, char * and utype_t (and indentation, if they are).

The results of my compiler:

 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; } 
+8
source share

Unions can be used when no more than one member can apply at a time. This way you can save some memory instead of using structure.

There is a neat cheat that can be possible with associations: writing one field and reading from another, checking bit patterns or interpreting them differently.

+8
source share

The union uses less memory and allows you to do more dangerous things. It is a single contiguous block of memory that can be interpreted as an integer, a floating point value, or a pointer to a character.

+4
source share

Unions are used to save only one type of data at a time. If the value is reassigned, the old value is overwritten and cannot be accessed. In your example, the members int, float and char can have different values ​​at any time when used as a structure. It is not a matter of union. So it depends on your software requirements and design. Check out the article on when to use union. Google can give even greater results.

+4
source share

The language offers the programmer many possibilities for applying high-level abstractions to machine data and lower-level operations.

However, the mere presence of something does not mean that its use is the best practice. Their presence makes the language powerful and flexible. But industry needs have led to the development of programming techniques that have contributed to clarity and maintainability with respect to maximum code efficiency or storage efficiency.

So, if the set of solutions to the problem contains both unions and structures, the responsibility of the programmer is to decide whether the need for compact storage exceeds the cost.

Recently, the cost of memory has been extremely low. The introduction of the bool type (and even before that, int variables) allowed the 32-bit system programmer to use 32 bits to represent the binary state. You often see this in programming, although a programmer can use masks and get 32 ​​true / false values ​​in a variable.

So, to answer your question, the union offers a more compact storage for one object of value from several possible types than the traditional structure, but at the expense of clarity and possible subtle defects of the program.

+2
source share

The use of unions to save memory in most cases is not performed on modern systems, since the code for accessing a member of the union will quickly take up more space (and be slower) than just adding another word size variable to the memory. However, when your code needs to support multiple architectures with different principles (like a word), unions can be convenient. I prefer to use the final utility library (for functions), but some people like alliances.

Memory-mapped hardware registers also typically access connections. Bit fields in C (do not use them, they are average) can be transmitted as words using unions.

+1
source share
unions

have two dominant uses:

First you need to specify the option, as you have outlined. In contrast to the structural approach, there is one unit of memory shared between all members of the union. If memory is not a problem, the structure will also perform this function.

I usually embed a union in a structure - the structure ensures that the type and data are stored together, and the union means that exactly the same value is stored.

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

Secondly, unification is very useful for low-level access to raw data - re-interpretation of one type as another. The purpose for which I used this is to read and write binary encoded 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; } 

Here you can write individual bytes, depending on the platform finiteness, and then interpret these 4 raw char bytes as an IEEE float. Casting that a char array for float would not have the same result.

+1
source share

As often mentioned above: associations preserve memory. But this is not the only difference. Chips are made to save ALL given subtypes, while unions are made to save EXACTLY ONE of these subtypes. Therefore, if you want to store an integer or a float, then you probably need a union (but you need to remember somewhere else which number you saved). If you want to keep both, you will need a structure.

0
source share

borrowing from the quote you quote "... any of one of several types ..." of union members at a time. This is precisely the union; while members of the structure can be appointed and available at the same time.

union makes more sense in executing some system (os) programs, such as process processing / concurrency.

0
source share

Unions are complex. For many years I could not understand them, then I started to deal with network protocols, and someone showed me the light. Say you have a header, and then after the header there are different types of packages, for example:

| type (4 bytes) | uid (8 bytes) | payload length (2 bytes) | Payload (variable length) |

And then there will be different types of payload packets ... For the argument, there may be greetings, goodbye and message packets ...

Well, you can build a nested set of structures / unions that can accurately represent a package in this protocol, for example ...

 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]; }; }; }; 

Inevitably, you get this protocol from the operating system through a call to read (), and it's just a mess of bytes. But if you are careful about defining your structure, and all types are of the right size, you can simply make a pointer to the structure, point it to your buffer filled with random data, and ...

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

and reading your packages is as simple as ...

 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; } 

Do not cheat, count bytes, etc. You can nest it as deep as you want (create a union for ip addresses that gives you all this as an unsigned int or separate bytes, so it’s easier to type 192.168.0.1).

Unions do not slow down your code, because all this simply translates into offsets into machine code.

0
source share

An example will make sense here. See the example below:

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

uint is a typedef of an unsigned int.

Here, this union is a 32-bit register. You can read the register using allX and then manipulate it with struct.

This facilitates unnecessary bit shifts if we use allX to manipulate the bits.

0
source share

All Articles