Let's understand this through an example.
struct abc
{
int a;
char b;
}
The aforementioned code represents a custom structure comprising two elements: 'a' of integer type and 'b' of character type. Upon inspecting the addresses of 'a' and 'b', it became evident that they possess distinct memory locations. Hence, it can be inferred that the elements within the structure do not have a common memory location.
When a union is declared, it is structured similarly to how a structure is defined. The key disparity lies in the usage of the union keyword for establishing the union data type, as opposed to the struct keyword utilized for creating a structure. Within the union, there exist data elements, specifically 'a' and 'b'. Upon comparison of the memory addresses of these variables, it becomes evident that they point to the same location. This indicates that the members of a union share a common memory space.
Let's examine the visual depiction of memory allocation.
The diagram below illustrates the visual depiction of the layout. This arrangement consists of two components; specifically, one is categorized as an integer type, and the other is classified as a character type. Given that 1 block is equivalent to 1 byte; as a result, the 'a' variable will be assigned 4 blocks of storage, whereas the 'b' variable will be assigned 1 block of storage.
The diagram below illustrates the visual representation of union members. Both variables share a common memory location and possess identical initial addresses.
In a union, all members will access the same memory location. Any modifications made to one member will affect the other members too. Let's illustrate this idea with an example.
union abc
{
int a;
char b;
}var;
int main()
{
var.a = 66;
printf("\n a = %d", var.a);
printf("\n b = %d", var.b);
}
In the provided code snippet, the union contains two elements, specifically 'a' and 'b'. 'var' represents a variable of type union abc. Within the main function, we set the value 66 to the 'a' element. Consequently, when accessing var.a, the output will be 66. As 'a' and 'b' share the same memory space, var.b will display 'B' (the ASCII representation of 66).
Deciding the size of the union
The size of the union depends on the size of the largest element within the union.
Let's understand through an example.
union abc{
int a;
char b;
float c;
double d;
};
int main()
{
printf("Size of union abc is %d", sizeof(union abc));
return 0;
}
Given that an integer occupies 4 bytes, a character occupies 1 byte, a float occupies 4 bytes, and a double occupies 8 bytes, the total memory allocation for the double variable is the highest among these data types. Consequently, a total of 8 bytes will be assigned in the memory to accommodate this variable. Thus, the program mentioned above will result in an output of 8 bytes.
Accessing members of union using pointers
We can retrieve the elements of the union via pointers by employing the (->) arrow operator.
Let's understand through an example.
Example
#include <stdio.h>
union abc
{
int a;
char b;
};
int main()
{
union abc *ptr; // pointer variable declaration
union abc var;
var.a= 90;
ptr = &var;
printf("The value of a is : %d", ptr->a);
return 0;
}
In the provided code snippet, a pointer variable named *ptr has been initialized to store the memory address of the var variable. Consequently, ptr is capable of retrieving the value of the variable 'a' through the (->) operator. As a result, the expected result of the aforementioned code is 90.
Why do we need C unions?
Consider a scenario to grasp the necessity of unions in C. Suppose a store carries two types of products:
- Publications
- Blouses
Store proprietors aim to keep track of the details of the aforementioned items and their corresponding attributes. For instance, Books comprise Title, Author, number of pages, price, while Shirts consist of Color, design, size, and price. Notably, the 'price' attribute is shared between both items. If the store owner intends to preserve these properties, how should they go about storing the records?
Initially, the data was planned to be stored in a format depicted in the following structure:
struct store
{
double price;
char *title;
char *author;
int number_pages;
int color;
int size;
char *design;
};
The provided structure encompasses all the elements that the store proprietor intends to retain. This arrangement is fully functional, with the exception of the price being a shared attribute between both items, while the remaining characteristics are specific to each item type. Attributes such as price, title, author, and number_pages are associated with Books, whereas color, size, and *design are linked to Shirts.
Let's explore how we can retrieve the elements of the data structure.
int main()
{
struct store book;
book.title = "C programming";
book.author = "Paulo Cohelo";
book.number_pages = 190;
book.price = 205;
printf("Size is : %ld bytes", sizeof(book));
return 0;
}
In the provided code snippet, a store type variable has been declared. The variables title, author, number_pages, and price have been initialized with values. However, the book variable lacks attributes like size, color, and design, leading to inefficient memory usage. Consequently, the total size of this structure is calculated to be 44 bytes.
We can save lots of space if we use unions.
Example
#include <stdio.h>
struct store
{
double price;
union
{
struct{
char *title;
char *author;
int number_pages;
} book;
struct {
int color;
int size;
char *design;
} shirt;
}item;
};
int main()
{
struct store s;
s.item.book.title = "C programming";
s.item.book.author = "John";
s.item.book.number_pages = 189;
printf("Size is %ld", sizeof(s));
return 0;
}
In the provided code snippet, a store-type variable has been instantiated. By employing unions in the aforementioned code, the memory allocation for the variable is determined by the largest memory consumption among the union members. The program's resultant memory usage is 32 bytes. In contrast, when using structures, the memory allocation amounts to 44 bytes, whereas with unions, it remains at 44 bytes. Consequently, opting for unions results in a significant memory saving, as 44 bytes surpasses 32 bytes in memory efficiency.