Data Structure – Importance and Advantages
Data Structure can be defined as the collection of data objects which provides a way of
storing and managing data in the computer so that it can be used. Various Data Structures
types are arrays, Linked List, Stack, Queue, etc. Data Structures are widely used in almost
every aspect of Computer Science for simple as well as complex computations. Data
structures are used in all areas of computer science such as Artificial Intelligence, graphics,
Operating system etc.
Data Structures are the key part of many computer algorithms as they allow the programmers
to do data management in an efficient way. A right selection of data structure can enhance the
efficiency of computer program or algorithm in a better way.
Why Data Structures are needed
With increasing complexities in computer algorithms, the amount of data usage is increasing,
this can affect the performance of the application and can create some areas of concern:
Processing speed: To handle very large data, high-speed processing is required, but with
growing data processor may fail to archive required processing speed.
Data Search: Getting a particular record from database should be quick and with optimum
use of resources.
Multiple requests: To handle simultaneous requests from multiple users
In order to work on concern areas, data structures are used. Data is organized to form a data
structure in such a way that all items are not required to be searched and required data can be
searched instantly.
Data Structure Advantages
Efficient Memory use: With efficient use of data structure memory usage can be optimized,
for e.g we can use linked list vs arrays when we are not sure about the size of data. When
there is no more use of memory, it can be released.
Reusability: Data structures can be reused, i.e. once we have implemented a particular data
structure, we can use it at any other place. Implementation of data structures can be compiled
into libraries which can be used by different clients.
Abstraction: Data structure serves as the basis of abstract data types; the data structure
defines the physical form of ADT (Abstract Data Type). ADT is theoretical and Data
structure gives physical form to them.
Data structures are the basic building block of any programming language, complex
computations.
Types of Data Structures
Data structures are a very important programming concept. They provide us with a means to
store, organize and retrieve data in an efficient manner. The data structures are used to make
working with our data, easier. There are many data structures which help us with this.
Types of Data Structures
Primitive Data Structures
These are the structures which are supported at the machine level, they can be used to make non-
primitive data structures. These are integral and are pure in form. They have predefined behavior
and specifications.
Examples: Integer, float, character, pointers.
The pointers, however don’t hold a data value, instead, they hold memory addresses of the data
values. These are also called the reference data types.
Non-primitive Data Structures
The non-primitive data structures cannot be performed without the primitive data structures.
Although, they too are provided by the system itself yet they are derived data structures and
cannot be formed without using the primitive data structures.
The Non-primitive data structures are further divided into the following categories:
1. Arrays
Arrays are a homogeneous and contiguous collection of same data types. They have a static
memory allocation technique, which means, if memory space is allocated for once, it cannot be
changed during runtime. The arrays are used to implement vectors, matrices and also other data
structures. If we do not know the memory to be allocated in advance then array can lead to
wastage of memory. Also, insertions and deletions are complex in arrays since elements are
stored in consecutive memory allocations.
2. Files
A file is a collection of records. The file data structure is primarily used for managing large
amounts of data which is not in the primary storage of the system. The files help us to process,
manage, access and retrieve or basically work with such data, easily.
3. Lists
The lists support dynamic memory allocation. The memory space allocated, can be changed at
run time also. The lists are of two types:
a) Linear Lists
The linear lists are those which have the elements stored in a sequential order. The insertions and
deletions are easier in the lists. They are divided into two types:
Stacks: The stack follows a “LIFO” technique for storing and retrieving elements. The
element which is stored at the end will be the first one to be retrieved from the stack. The
stack has the following primary functions:
Push(): To insert an element in the stack.
Pop(): To remove an element from the stack.
Queues: The queues follow “FIFO” mechanism for storing and retrieving elements. The
elements which are stored first into the queue will only be the first elements to be
removed out from the queue. The “ENQUEUE” operation is used to insert an element
into the queue whereas the “DEQUEUE” operation is used to remove an element from the
queue.
b) Non Linear Lists
The non linear lists do not have elements stored in a certain manner. These are:
Graphs: The Graph data structure is used to represent a network. It comprises of vertices
and edges (to connect the vertices). The graphs are very useful when it comes to study a
network.
Trees: Tree data structure comprises of nodes connected in a particular arrangement and
they (particularly binary trees) make search operations on the data items easy. The tree
data structures consists of a root node which is further divided into various child nodes
and so on. The number of levels of the tree is also called height of the tree.
Dynamic Memory Allocation in C using malloc(), calloc(),
free() and realloc()
Since C is a structured language, it has some fixed rules for programming. One of it includes
changing the size of an array. An array is collection of items stored at continuous memory
locations.
As it can be seen that the length (size) of the array above made is 9. But what if there is a
requirement to change this length (size). For Example,
If there is a situation where only 5 elements are needed to be entered in this array. In this
case, the remaining 4 indices are just wasting memory in this array. So there is a
requirement to lessen the length (size) of the array from 9 to 5.
Take another situation. In this, there is an array of 9 elements with all 9 indices filled. But
there is a need to enter 3 more elements in this array. In this case 3 indices more are
required. So the length (size) of the array needs to be changed from 9 to 12.
This procedure is referred to as Dynamic Memory Allocation in C.
Therefore, C Dynamic Memory Allocation can be defined as a procedure in which the size
of a data structure (like Array) is changed during the runtime.
C provides some functions to achieve these tasks. There are 4 library functions provided by C
defined under <stdlib.h> header file to facilitate dynamic memory allocation in C
programming. They are:
1. malloc()
2. calloc()
3. free()
4. realloc()
Let’s look at each of them in greater detail.
1. C malloc() method
“malloc” or “memory allocation” method in C is used to dynamically allocate a single
large block of memory with the specified size. It returns a pointer of type void which can
be cast into a pointer of any form. It initializes each block with default garbage value.
Syntax:
ptr = (cast-type*) malloc(byte-size)
For Example:
ptr = (int*) malloc(100 * sizeof(int));
Since the size of int is 4 bytes, this statement will allocate 400 bytes of memory. And, the
pointer ptr holds the address of the first byte in the allocated memory.
If space is insufficient, allocation fails and returns a NULL pointer.
Example:
#include <stdio.h>
#include <stdlib.h>
int main()
{
// This pointer will hold the
// base address of the block created
int* ptr;
int n, i;
// Get the number of elements for the array
n = 5;
printf("Enter number of elements: %d\n", n);
// Dynamically allocate memory using malloc()
ptr = (int*)malloc(n * sizeof(int));
// Check if the memory has been successfully
// allocated by malloc or not
if (ptr == NULL) {
printf("Memory not allocated.\n");
exit(0);
}
else {
// Memory has been successfully allocated
printf("Memory successfully allocated using malloc.\n");
// Get the elements of the array
for (i = 0; i < n; ++i) {
ptr[i] = i + 1;
}
// Print the elements of the array
printf("The elements of the array are: ");
for (i = 0; i < n; ++i) {
printf("%d, ", ptr[i]);
}
}
return 0;
}
Output:
Enter number of elements: 5
Memory successfully allocated using malloc.
The elements of the array are: 1, 2, 3, 4, 5,
2. C calloc() method
“calloc” or “contiguous allocation” method in C is used to dynamically allocate
the specified number of blocks of memory of the specified type. It initializes each
block with a default value ‘0’.
Syntax:
ptr = (cast-type*)calloc(n, element-size);
For Example:
ptr = (float*) calloc(25, sizeof(float));
This statement allocates contiguous space in memory for 25 elements each with
the size of the float.
If space is insufficient, allocation fails and returns a NULL pointer.
Example:
#include <stdio.h>
#include <stdlib.h>
int main()
{
// This pointer will hold the
// base address of the block created
int* ptr;
int n, i;
// Get the number of elements for the array
n = 5;
printf("Enter number of elements: %d\n", n);
// Dynamically allocate memory using calloc()
ptr = (int*)calloc(n, sizeof(int));
// Check if the memory has been successfully
// allocated by calloc or not
if (ptr == NULL) {
printf("Memory not allocated.\n");
exit(0);
}
else {
// Memory has been successfully allocated
printf("Memory successfully allocated using calloc.\n");
// Get the elements of the array
for (i = 0; i < n; ++i) {
ptr[i] = i + 1;
}
// Print the elements of the array
printf("The elements of the array are: ");
for (i = 0; i < n; ++i) {
printf("%d, ", ptr[i]);
}
}
return 0;
}
Output:
Enter number of elements: 5
Memory successfully allocated using calloc.
The elements of the array are: 1, 2, 3, 4, 5,
3. C free() method
“free” method in C is used to dynamically de-allocate the memory. The memory
allocated using functions malloc() and calloc() is not de-allocated on their own.
Hence the free() method is used, whenever the dynamic memory allocation takes
place. It helps to reduce wastage of memory by freeing it.
Syntax:
free(ptr);
Example:
#include <stdio.h>
#include <stdlib.h>
int main()
{
// This pointer will hold the
// base address of the block created
int *ptr, *ptr1;
int n, i;
// Get the number of elements for the array
n = 5;
printf("Enter number of elements: %d\n", n);
// Dynamically allocate memory using malloc()
ptr = (int*)malloc(n * sizeof(int));
// Dynamically allocate memory using calloc()
ptr1 = (int*)calloc(n, sizeof(int));
// Check if the memory has been successfully
// allocated by malloc or not
if (ptr == NULL || ptr1 == NULL) {
printf("Memory not allocated.\n");
exit(0);
}
else {
// Memory has been successfully allocated
printf("Memory successfully allocated using malloc.\n");
// Free the memory
free(ptr);
printf("Malloc Memory successfully freed.\n");
// Memory has been successfully allocated
printf("\nMemory successfully allocated using calloc.\n");
// Free the memory
free(ptr1);
printf("Calloc Memory successfully freed.\n");
}
return 0;
}
Output:
Enter number of elements: 5
Memory successfully allocated using malloc.
Malloc Memory successfully freed.
Memory successfully allocated using calloc.
Calloc Memory successfully freed.
4. C realloc() method
“realloc” or “re-allocation” method in C is used to dynamically change the
memory allocation of a previously allocated memory. In other words, if the
memory previously allocated with the help of malloc or calloc is insufficient,
realloc can be used to dynamically re-allocate memory. re-allocation of memory
maintains the already present value and new blocks will be initialized with default
garbage value.
Syntax:
ptr = realloc(ptr, newSize);
where ptr is reallocated with new size 'newSize'.
If space is insufficient, allocation fails and returns a NULL pointer.
Example:
#include <stdio.h>
#include <stdlib.h>
int main()
{
// This pointer will hold the
// base address of the block created
int* ptr;
int n, i;
// Get the number of elements for the array
n = 5;
printf("Enter number of elements: %d\n", n);
// Dynamically allocate memory using calloc()
ptr = (int*)calloc(n, sizeof(int));
// Check if the memory has been successfully
// allocated by malloc or not
if (ptr == NULL) {
printf("Memory not allocated.\n");
exit(0);
}
else {
// Memory has been successfully allocated
printf("Memory successfully allocated using calloc.\n");
// Get the elements of the array
for (i = 0; i < n; ++i) {
ptr[i] = i + 1;
}
// Print the elements of the array
printf("The elements of the array are: ");
for (i = 0; i < n; ++i) {
printf("%d, ", ptr[i]);
}
// Get the new size for the array
n = 10;
printf("\n\nEnter the new size of the array: %d\n", n);
// Dynamically re-allocate memory using realloc()
ptr = realloc(ptr, n * sizeof(int));
// Memory has been successfully allocated
printf("Memory successfully re-allocated using realloc.\
n");
// Get the new elements of the array
for (i = 5; i < n; ++i) {
ptr[i] = i + 1;
}
// Print the elements of the array
printf("The elements of the array are: ");
for (i = 0; i < n; ++i) {
printf("%d, ", ptr[i]);
}
free(ptr);
}
return 0;
}
Output:
Enter number of elements: 5
Memory successfully allocated using calloc.
The elements of the array are: 1, 2, 3, 4, 5,
Enter the new size of the array: 10
Memory successfully re-allocated using realloc.
The elements of the array are: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,