The document discusses self-referential structures in C. It begins by showing how a struct cannot directly contain a member of its own type, as this would cause infinite recursion. It then demonstrates how to make a struct self-referential by using a pointer to the struct as a member, rather than the struct itself. This allows the compiler to correctly allocate memory for the pointer. Linked lists are provided as an example application of self-referential structures. The document also covers dynamic memory allocation functions like malloc(), calloc(), free() and realloc() in C.
1. SELF-REFERENTIAL STRUCTURES
Consider the structure declaration below,
struct NODE {
struct NODE new; /* 'new' declared variable */
int value;
};
As we know that structure template tells compiler how to allocate storage to its members and
makes computer not to allocate memory to them. When compiler reaches the line
struct NODE new;
template struct NODE is not fully defined. Further, its member ‘new’ of type struct NODE
contains a member ‘new’ of type struct NODE which in turn contains a member ‘new’ again of
type struct NODE and so on indefinitely. In such an instance, compiler can’t evaluate correctly
how much storage to allocate to ‘new’ member of template struct NODE. So we observed here
that a member of struct NODE can’t be a variable of type struct NODE. Then, how can we
make structure struct NODE self-referential? Let’s take one more try, this time we declare a
‘pointer-to-struct NODE’ as a member of template struct NODE,
struct NODE {
struct NODE *new; /* 'new' a pointer-to-struct NODE */
int value;
};
As compiler starts compiling the template struct NODE and reaches line
struct NODE *new;
it finds ‘new’, a ‘pointer-to-struct NODE’, and also member of struct NODE template, it
evaluates correctly how much bytes of storage to be allocated to ‘new’. On linux system, any
pointer type takes 8 bytes of storage. There’s no problem in using ‘pointer-to-struct NODE’ as a
member of struct NODE. Because ‘new’ is a ‘pointer-to-struct NODE’, structure struct NODE is
called self-referential structure.
typedef struct NODE {
struct NODE *new;
int value;
}Node;
int main(void)
{
Node previous, current;
/* accessing members of 'previous' */
previous.new = ¤t;
2. /* previous.new is a 'pointer-to-struct NODE' */
previous.value = 100;
}
In above fragment of code, ‘previous.new’ is pointing to ‘current’ Node.
Self-referential structures have their applications in Advanced Data Structures like, Linked
Lists, Binary Trees etc..
C Dynamic Memory Allocation
In C, the exact size of array is unknown until compile time, i.e., the time when a compiler
compiles your code into a computer understandable language. So, sometimes the size of the
array can be insufficient or more than required.
Dynamic memory allocation allows your program to obtain more memory space while running,
or to release it if it's not required.
In simple terms, Dynamic memory allocation allows you to manually handle memory space for
your program.
Although, C language inherently does not have any technique to allocate memory dynamically,
there are 4 library functions under "stdlib.h" for dynamic memory allocation.
Function Use of Function
malloc() Allocates requested size of bytes and returns a pointer first byte of allocated space
calloc()
Allocates space for an array elements, initializes to zero and then returns a pointer
to memory
free() deallocate the previously allocated space
realloc() Change the size of previously allocated space
C malloc()
The name malloc stands for "memory allocation".
The function malloc() reserves a block of memory of specified size and return a pointer of
type void which can be casted into pointer of any form.
3. Syntax of malloc()
ptr = (cast-type*) malloc(byte-size)
Here, ptr is pointer of cast-type. The malloc() function returns a pointer to an area of
memory with size of byte size. If the space is insufficient, allocation fails and returns NULL
pointer.
ptr = (int*) malloc(100 * sizeof(int));
This statement will allocate either 200 or 400 according to size of int 2 or 4 bytes respectively
and the pointer points to the address of first byte of memory.
C calloc()
The name calloc stands for "contiguous allocation".
The only difference between malloc() and calloc() is that, malloc() allocates single block of
memory whereas calloc() allocates multiple blocks of memory each of same size and sets all
bytes to zero.
Syntax of calloc()
ptr = (cast-type*)calloc(n, element-size);
This statement will allocate contiguous space in memory for an array of n elements. For
example:
ptr = (float*) calloc(25, sizeof(float));
This statement allocates contiguous space in memory for an array of 25 elements each of size of
float, i.e, 4 bytes.
4. C free()
Dynamically allocated memory created with either calloc() or malloc() doesn't get freed on its
own. You must explicitly use free() to release the space.
syntax of free()
free(ptr);
This statement frees the space allocated in the memory pointed by ptr.
Example #1: Using C malloc() and free()
Write a C program to find sum of n elements entered by user. To perform this program, allocate
memory dynamically using malloc() function.
#include <stdio.h>
#include <stdlib.h>
int main()
{
int num, i, *ptr, sum = 0;
printf("Enter number of elements: ");
scanf("%d", &num);
ptr = (int*) malloc(num * sizeof(int)); //memory allocated using malloc
if(ptr == NULL)
{
printf("Error! memory not allocated.");
exit(0);
}
printf("Enter elements of array: ");
5. for(i = 0; i < num; ++i)
{
scanf("%d", ptr + i);
sum += *(ptr + i);
}
printf("Sum = %d", sum);
free(ptr);
return 0;
}
Example #2: Using C calloc() and free()
Write a C program to find sum of n elements entered by user. To perform this program, allocate
memory dynamically using calloc() function.
#include <stdio.h>
#include <stdlib.h>
int main()
{
int num, i, *ptr, sum = 0;
printf("Enter number of elements: ");
scanf("%d", &num);
ptr = (int*) calloc(num, sizeof(int));
if(ptr == NULL)
{
printf("Error! memory not allocated.");
exit(0);
}
printf("Enter elements of array: ");
for(i = 0; i < num; ++i)
6. {
scanf("%d", ptr + i);
sum += *(ptr + i);
}
printf("Sum = %d", sum);
free(ptr);
return 0;
}
C realloc()
If the previously allocated memory is insufficient or more than required, you can change the
previously allocated memory size using realloc().
Syntax of realloc()
ptr = realloc(ptr, newsize);
Here, ptr is reallocated with size of newsize.
Example #3: Using realloc()
#include <stdio.h>
#include <stdlib.h>
int main()
{
int *ptr, i , n1, n2;
printf("Enter size of array: ");
scanf("%d", &n1);
ptr = (int*) malloc(n1 * sizeof(int));
7. printf("Address of previously allocated memory: ");
for(i = 0; i < n1; ++i)
printf("%ut",ptr + i);
printf("nEnter new size of array: ");
scanf("%d", &n2);
ptr = realloc(ptr, n2);
for(i = 0; i < n2; ++i)
printf("%ut", ptr + i);
return 0;
}
Linked lists in C (Singly linked list)
Linked list is one of the most important data structures. We often
face situations, where the data is dynamic in nature and number of
data can’t be predicted or the number of data keeps changing during
program execution. Linked lists are very useful in this type of
situations.
Dynamic Memory Allocation
SELF-REFERENTIAL STRUCTURES
Linked lists
8. A linked list is made up of many nodes which are connected in
nature. Every node is mainly divided into two parts, one part holds
the data and the other part is connected to a different node. It is
similar to the picture given below.
Here, each node contains a data member (the upper part of the
picture) and link to another node(lower part of the picture).
Notice that the last node doesn’t point to any other node and just
stores NULL.
In C, we achieve this functionality by using structures and pointers.
Each structure represents a node having some data and also a pointer
to another structure of the same kind. This pointer holds the address
of the next node and creates the link between two nodes. So, the
structure is something like:
struct node
{
int data;
struct node *next;
};
9. The first data member of the structure (named node) is an integer to
hold an integer and the second data member is the pointer to a node
(same structure). This means that the second data member holds the
address of the next node and in this way, every node is connected as
represented in the picture above.
The picture representing the above structure is given below.
And the picture representing the linked list is:
So, if we have access to the first node then we can access any node of
the linked list. For example, if ‘a’ is a node then a->next is the node
next to the ‘a’ (the pointer storing the address of the next node is
named ‘next’).
One thing you should notice here is that we can easily access the next
node but there is no way of accessing the previous node and this is the
limitation of singly linked list.
10. Coding up a linked list
The first part is to create a node (structure).
struct node
{
int data;
struct node *next;
};
The second and the most important part of a linked list is to always
keep the track of the first node because access to the first node means
access to the entire list. So, let’s call our first node as ‘ head’.
int main()
{
struct node *prev,*head,*p;
return 0;
}
We have made three nodes – head, prev and p. You will see the
function of prev in the explanation of the next block of code.
Now, let’s create a node ‘p’.
int main()
{
struct node *p;
p=malloc(sizeof(struct node));
scanf("%d",&p->data);
p->next=NULL;
return 0;
}
11. p=malloc(sizeof(struct node)) – We are allocating the space required
for a node by the malloc function. Now, ‘p’ points to a node (or space
allocated for the node).
scanf("%d",&p->data) – We are giving a value to the ‘data’ of ‘p’ after
taking the input from the user.
p->next=NULL – We have given the value to ‘data’ in the previous line
and a value of the pointer ‘next’ (NULL) in this line and thus making
our node ‘p’ complete.
Let’s create our linked list by joining the nodes.
int main()
{
struct node *prev,*head,*p;
int n,i;
printf ("number of elements:");
scanf("%d",&n);
head=NULL;
for(i=0;i<n;i++)
{
p=malloc(sizeof(struct node));
scanf("%d",&p->data);
p->next=NULL;
if(head==NULL) head=p;
else prev->next=p;
prev=p;
}
return 0; }
We are storing n number of elements in our linked list.
12. if(head==NULL) – If the ‘head’ is NULL, then our linked list is not created.
head=p – We have given the value to the ‘head’ and thus made the first
node of our linked list.
else – The linked list is already there and we just have to add a node in
this linked list.
prev->next=p – We have used the prev to store the record of the previous
node to the current node (the last node from the previous iteration)
(you will see in the next line). The ‘next’ of this prev is holding NULL
till now. We pointed it to the node ‘p’ and hence added a node to our
linked list.
prev=p – We made the last node ‘prev’ for the next iteration.
Code with all operations:
#include <stdio.h>
#include <stdlib.h>
struct node
{
int data;
struct node *next;
};
display(struct node *head)
{
if(head == NULL)
{
printf("NULLn");
13. }
else
{
printf("|%d||Link|->", head -> data);
display(head->next);
}
}
struct node* front(struct node *head)
{
struct node *p;
p=malloc(sizeof(struct node));
printf("Enter the value to be inserted n");
scanf("%d",&p->data);
p->next=head;
return (p);
}
struct node* deletf(struct node *a)
{
struct node *temp;
if(a==NULL)
printf("NO elements in list");
else
{
temp=a;
a=a->next;
free(temp);
}
return a;
17. //
free(p);
}
temp=temp->next;
}
return a;
}
int main()
{
struct node *prev,*head, *p;
int n,i,key;
head=NULL;
for(;;)
{
printf("Enter the option n1.displayn2.insertn3.delete");
scanf("%d",&key);
switch (key)
{
case 2: printf("Enter the option n1.Ifrontn2.Iend n3.Ibetween");
scanf("%d",&key);
switch(key)
{
case 1: head = front(head);break;
case 2: head=end(head);break;
case 3:printf("Enter the value to be searched");
scanf("%d",&key);
after(head,key);break;
}
break;
18. case 1:display(head);break;
case 3:
printf("Enter the option n1.Dfrontn2.Dend n3.Dbetween");
scanf("%d",&key);
switch(key)
{
case 1:head=deletf(head);break;
case 2:head=deletr(head);break;
case 3:printf("Enter the value to be searched");
scanf("%d",&key);
deletat(head,key);break;
}
break;
default:return 0;
}
}
}