To share this page click on the buttons below;
Unions
The syntax to define a union is very similar to the one used for structures, although the meaning of these 2 constructions is very different. Unions allow the programmer to see the same location of memory with different shapes and this is something that might sound a little strange for a beginner.
Besides the concept behind unions is a low level programming which is used on embedded programming and that makes somehow unions something a little bit exotic.
Defining a union
You define a union by using the reserved word union following by a tag (just the name of the union) followed by a pair of braces: inside the braces you define all the different shapes you want to access the union: those shapes are nothing more than variables declaration. As we know every type of variable is stored in memory and has an address and a dimension (and the dimension depend on the type of the variable), but, at it end, this is just a space in memory and the same space can contain different variables, here it comes the union: it maps different type of variables on the same memory area, so that you can access the same variable in different ways.
That is the important point: although unions have a very similar syntax of structures they behave very differently. When you declare different variables inside the union you are not declaring different variables: you are simply mapping the same variable on different shapes so that you can access the variable in different ways. Why you would do that? Well because one shape is suitable for certain operations, while another one is suitable for other ones.
It is probably worth to note that (since they are different shapes of the same variable) the variables declared inside the union should all have the same size in memory, not only, they share the exact same space in memory.
Here it is a very simple example: when you use your PC to surf the Internet, your PC has an address, to be more precise it has an IP (Internet Protocol) address. There are 2 different kind of IP addresses but for our example we just use the first v4 version. So an IP address is a set of 4 numbers, each one goes from 0 to 255. So to store an IP address you can use an array of 4 unsigned char. Now suppose that you want to compare 2 different IP addresses to establish if they are the same of not: well you need to compare all the 4 bytes and that is boring. Alternatively you can use a union for the IP address, one of the shapes of the IP address will be an array of four unsigned char and an other possible shape would be (for example) a single unsigned int which is 4 bytes long. Now when you need to use the IP address for networking purposes you will use the array form, but when you want to compare two addresses you can now use the shape of the unsigned int which is handy because you can make just one comparison.
Here it is how you can define the IP address union:
union tagIPAddress {
unsigned char ip[4];
unsigned int val;
};
You will find a complete example at the end of this note. Here I used just 2 different shapes, but this is not a limit: you can add as many different shapes you need (and those are just different types of variable). Remember (because this is very important) that with union you are defining always just a variable and you access it through different shapes: any changes using a particular shape will of course affect the other ones (because at the end the variable is always the same, or, which is the same, different shapes always rely on the same memory space).
It is not necessary (although it is strongly suggested) that all the shapes of the a union have the same size: the union will size itself based on biggest of the sizes you choose.
Access different shapes
You can access the different shapes the union is made of in the same way you access the fields of a structure: using the "dot" notation. Name of a union variable "dot" shape will allow you to access that union variable using that particular shape.
A complete example
The following example summarizes all the previous concepts.
#include <stdio.h>
/* program to demonstrate unions */
/* a union to model an IP address */
union tagIPAddress {
unsigned char ip[4];
unsigned int val;
};
void printIP(union tagIPAddress u, char *name) {
if(name != NULL) {
printf("IP address %s is: %i.%i.%i.%i (or %i as a unique integer)\n",
name,
u.ip[0],
u.ip[1],
u.ip[2],
u.ip[3],
u.val);
}
else {
printf("IP address is: %i.%i.%i.%i (or %i as a unique integer)\n",
u.ip[0],
u.ip[1],
u.ip[2],
u.ip[3],
u.val);
}
}
void main(void) {
printf("PROGRAM to demonstrate some stuff on UNIONS\n");
/* declaration of a variable add "of type" union tagIPAddress */
union tagIPAddress add;
/* declaration and definition of a second variable add1 "of type" union tagIPAddress */
union tagIPAddress add1 = {12,34,56,8};
add.ip[0] = 34;
add.ip[1] = 12;
add.ip[2] = 56;
add.ip[3] = 8;
printIP(add, "add");
printIP(add1, "add1");
if(add.val == add1.val) {
printf("The 2 addresses are the same\n");
}
else {
printf("The 2 addresses are different\n");
}
/* when you change a shape of the union of course you change also all the other ones */
add1.val = add.val;
printIP(add1, "add1");
if(add.val == add1.val) {
printf("The 2 addresses are the same\n");
}
else {
printf("The 2 addresses are different\n");
}
add1.ip[2] = 4;
printIP(add1, "add1");
}
Just a little additional note on this example: the function printIP allows you to print an IP address and it takes the argument name to specify the name of the variable you are printing. That will be an array of char which (I hope you remember) is a pointer (that is the meaning of the syntax used in the function definition. Please note that the function is not so much safe because it rely on the fact that the string you pass is NULL terminated (as it has to be), but what happen if that is not true? The program will probably crash… So we will need to improve our code in this kind of situations.
To share this page click on the buttons below;