Create & Traverse Singly Linked List in C: Step-by-Step Guide

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.

Create and traverse Linked List in C

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

  1. Allocate new memory using malloc(), pass size in bytes to allocate as argument. Get the size of our node using sizeof(node).
  2. Typecast the malloc() result to the pointer to node.
  3. malloc(), returns NULL on failure. Verify for successful memory allocation.
  4. Assign data and pointer to next 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;
}

⚠️ 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:

  1. Start at head node.
  2. Print data, move to next node.
  3. 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 → NULL

Memory Management Best Practices

  1. Free Memory: When allocating memory dynamically, always clean the allocated memory before exiting. Use free() function to clear memory on demand.
  2. 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

AspectIterative ApproachRecursive Approach
Space ComplexityO(1) – Constant spaceO(n) – Call stack overhead
PerformanceFaster executionSlower due to function calls
Memory SafetyNo stack overflow riskStack overflow for large lists
Code StyleMore explicit, longerConcise and elegant
Use caseLarge datasetsSmall lists