Imagine a linked list like a treasure hunt: each clue (node) holds a prize (data) and directions to the next clue (pointer). Unlike arrays that need contiguous memory, linked lists dynamically chain nodes anywhere in memory. Perfect for unpredictable data growth! To understand more we will begin our learning how to create and traverse linked list?
💡 Key Insight: Use linked lists when you need frequent insertions/deletions or don’t know data size upfront.

Step 1: Define the Node Structure
typedef struct node
{
int data; // Stores integer value, can be char, struct etc.
struct node* next; // Pointer to next node
} node;🤔 Why use typedef in C? It simplifies code (use node instead of struct node).
Step 2: Create Node Dynamically
- Allocate new memory using
malloc(), pass size in bytes to allocate as argument. Get the size of our node usingsizeof(node). - Typecast the
malloc()result to the pointer tonode. malloc(), returnsNULLon failure. Verify for successful memory allocation.- Assign
dataand pointer tonextnode.
node* createNode(int data)
{
// Allocate memory for new node
node* newNode = (node*) malloc(sizeof(node));
if (newNode == NULL)
{
// Memory allocation failed, exit program
printf("Memory allocation failed!");
exit(1);
}
// Initialise node data and pointer
newNode->data = data;
newNode->next = NULL;
return newNode;
}⚠️ Critical Check: Always verify malloc() success, during dynamic memory allocation! Always initialise a pointer to NULL to avoid dangling pointers.
Step 3: Build the Linked List
node* head = NULL; // Start with empty list
// Create nodes
node* node1 = createNode(10);
node* node2 = createNode(20);
node* node3 = createNode(30);
node* node4 = createNode(40);
// Link nodes: 10 → 20 → 30 → 40
head = node1;
node1->next = node2;
node2->next = node3;
node3->next = node4;Step 4: Traverse the List (Iterative)
Traversal Logic:
- Start at
headnode. - Print
data, move tonextnode. - Stop at
NULL(end of list).
void traverseList(node* head)
{
node* current = head; // Start from head
while (current != NULL)
{
printf("%d → ", current->data);
current = current->next; // Move to next node
}
printf("\n");
}
// Output: 10 → 20 → 30 → 40 → NULLMemory Management Best Practices
- Free Memory: When allocating memory dynamically, always clean the allocated memory before exiting. Use
free()function to clear memory on demand. - Avoid Leaks: Call
freeList(head)before program exit.
C Program to Create and Traverse Singly Linked List
/**
* C program to create and traverse a Linked List
*/
#include <stdio.h>
#include <stdlib.h>
/* Structure of a node */
typedef struct node
{
int data; // Stores integer value, can be char, struct etc.
struct node* next; // Pointer to next node
} node;
// Functions to create, traverse and display list
node* createNode(int data);
node* createList(int nodeCount);
void traverseList(node* head);
void freeList(node* head);
int main()
{
int totalNode;
node *head;
printf("Enter the total number of nodes: ");
scanf("%d", &totalNode);
head = createList(totalNode);
printf("\nData in the list \n");
traverseList(head);
freeList(head);
return 0;
}
/**
* Creates a new node with the given data and returns the pointer to the node.
*/
node* createNode(int data)
{
// Allocate memory for new node
node* newNode = (node*) malloc(sizeof(node));
if (newNode == NULL)
{
// Memory allocation failed, exit program
printf("Memory allocation failed!");
exit(1);
}
// Initialise node data and pointer
newNode->data = data;
newNode->next = NULL;
return newNode;
}
/**
* Create a list of nodes. Returns the pointer to head node.
*/
node* createList(int nodeCount)
{
node *head = NULL, *newNode = NULL, *prevNode = NULL;
int data, counter;
// No new node to create
if (nodeCount <= 0)
{
printf("No nodes to create");
return head;
}
for (counter = 1; counter <= nodeCount; counter++)
{
// Read data in the node from user
printf("Input data at node %d: ", counter);
scanf("%d", &data);
// Create a new node
newNode = createNode(data);
if (prevNode != NULL)
{
// Link previous node with newNode
prevNode->next = newNode;
}
// For next iteration make current node as previous node
prevNode = newNode;
// Ensure head points to the first node in the list
if (counter == 1)
{
head = newNode;
}
}
return head;
}
/**
* Traverses through entire linked list and prints data to console.
*/
void traverseList(node* head)
{
node* current = head; // Start from head
while (current != NULL)
{
printf("%d -> ", current->data); // Print data of current node
current = current->next; // Move to next node
}
printf("\n");
}
/**
* Traverses from head till NULL, deletes all the allocated bytes.
*/
void freeList(node* head)
{
printf("Clearing memory!");
node* current;
while (head != NULL)
{
current = head;
// Move head to next node
head = head->next;
// Clear the current node
free(current);
}
}Enter the total number of nodes: 4
Input data at node 1: 10
Input data at node 2: 20
Input data at node 3: 30
Input data at node 4: 40
Data in the list
10 -> 20 -> 30 -> 40 ->
Clearing memory!
Recursive Traversal (Advanced Technique)
You can also use recursive approach to traverse linked list. Below is a code sample.
void recursiveTraverse(node* head) {
// Base case: end of list
if (head == NULL) {
return;
}
// Print current node data
printf("%d → ", head->data);
// Recursive call for next node
recursiveTraverse(head->next);
}🚀 Performance Note: Recursive traversal uses O(n) stack space, while iterative uses O(1) space.
Iterative traversal vs Recursive traversal
| Aspect | Iterative Approach | Recursive Approach |
|---|---|---|
| Space Complexity | O(1) – Constant space | O(n) – Call stack overhead |
| Performance | Faster execution | Slower due to function calls |
| Memory Safety | No stack overflow risk | Stack overflow for large lists |
| Code Style | More explicit, longer | Concise and elegant |
| Use case | Large datasets | Small lists |