Data Structures in C

Published: 19 Nov 2025 | Reading Time: 7 min read

Introduction

Have you ever written a C program that worked, but became painfully slow the moment you added more data?

Or faced errors you couldn't explain because everything felt "too mixed up" in your logic?

Or wondered why senior developers write cleaner, faster programs with half the effort?

The difference is never "more experience." It's data structures — the part of C programming that decides how efficiently your code stores, accesses, and manages data.

If you're building programs that freeze with large input, take too long to search a list, or become impossible to scale, the real problem is simple:

You're using the wrong data structure.

This blog gives you a clear, practical, student-friendly guide to understanding exactly how and why these structures work. You'll see how arrays, linked lists, stacks, queues, trees, and graphs actually solve real problems — not theoretically, but in the same way they're used in operating systems, compilers, networks, and real applications.

What This Blog Covers

What are Data Structures and Algorithms in C?

Data structures provide systematic ways to store, organize, and manage data, making operations like searching, inserting, or deleting faster. Algorithms are the step-by-step procedures that operate on these structures to perform tasks efficiently.

Together, DSA in C determines:

From simple arrays to complex graphs, each structure brings a new way of thinking about problems.

Essential Concepts of C Language Data Structure

Data Structures in C Algorithms

In C, there are two main types of algorithms to learn and understand, which are the basis of data structures in C. Understanding these two types of core algorithms will be worthwhile in building flow and performance in applications.

Searching Algorithms

Sorting Algorithms

Types Of Data Structure in C

The C programming language provides a variety of built-in data types and also provides the flexibility to create custom, user-defined types. These data structures in C can be divided into two main categories: primitive and non-primitive data structures.

1. Primitive Data Structures

Primitive data structures are the basic types provided by C. These are simple and fundamental building blocks for creating more complex structures in Data Structures in C:

2. Non-Primitive Data Structures

Non-primitive data structures are more complicated structures and are built using primitive data types. These allow for storing and organizing large sets of data in more flexible ways.

Linear Data Structure In C

Non-Linear Data Structure In C

Detailed Overview of Common Data Structures in C

Here is the detailed overview of common data structures in C that you should know to acquire a complete understanding of data structures, as they allow efficient storage, manipulation, and retrieval of data.

1. Arrays In Data Structure Using C

Arrays in C are one of the simplest and most commonly used data structures. Arrays provide an efficient way to store a fixed collection of elements, and each element can be accessed via an index.

In C, an array can be declared by specifying its type by the array name and the number of elements it will contain.

int numbers[5] = {1, 2, 3, 4, 5};

Example Code

#include <stdio.h>

int main() {
    // Declaration and initialization of the array
    int numbers[5] = {1, 2, 3, 4, 5};

    for (int i = 0; i < 5; i++) {
        printf("%d\n", numbers[i]);
    }

    return 0;
}

Output

1
2
3
4
5

Explanation Of Code

Time and Space Complexity

2. Linked Lists

A linked list is a linear data structure that is made up of a series of nodes. Each node has data and a pointer (reference) to the next node in the series. Concerning an array, a linked list does not require contiguous memory to store its elements, and each node can be stored in different places in memory. The list is still conceptually maintained through a set of pointers.

Key Concepts and Relevant Terms

Types of Linked Lists

Example Code

#include <stdio.h>
#include <stdlib.h>

// Define the structure of a node
struct Node {
    int data;          // Store the data in the node
    struct Node* next; // Pointer to the next node
};

// Function to create a new node
struct Node* createNode(int data) {
    struct Node* newNode = (struct Node*)malloc(sizeof(struct Node)); // Allocate memory for a new node
    newNode->data = data; // Assign the data
    newNode->next = NULL;  // Set the next pointer to NULL (end of the list)
    return newNode;        // Return the new node
}

// Function to print the linked list
void printList(struct Node* head) {
    struct Node* temp = head; // Start from the head
    while (temp != NULL) {     // Traverse through the list
        printf("%d ", temp->data); // Print the data of each node
        temp = temp->next;     // Move to the next node
    }
}

int main() {
    // Create a linked list with three nodes
    struct Node* head = createNode(1);
    head->next = createNode(2);    // Link first node to second node
    head->next->next = createNode(3); // Link second node to third node

    // Print the linked list
    printList(head);  // Output should be: 1 2 3

    return 0;
}

Output

1 2 3

Explanation Of Code

Time and Space Complexity

Use Cases and Advantages

Disadvantages

3. Stacks

A stack data structure is a Last In, First Out (LIFO) structure—the last place you put an element in the stack is the first to be removed from the stack. Stacks are often used in situations, such as function calls, the undo feature of apps, and parsing expressions.

Example Code

#include <stdio.h>
#include <stdlib.h>

#define MAX 100

// Structure to represent a stack
struct Stack {
    int top;           // Index of the top element
    int items[MAX];    // Array to store stack elements
};

// Function to push an element onto the stack
void push(struct Stack* stack, int item) {
    if (stack->top == MAX - 1) {
        printf("Stack Overflow\n");
        return;  // Return if the stack is full
    }
    stack->items[++stack->top] = item;  // Increment top and add item
}

int pop(struct Stack* stack) {
    if (stack->top == -1) {
        printf("Stack Underflow\n");
        return -1;  // Return -1 if the stack is empty
    }
    return stack->items[stack->top--];  // Return the top element and decrement top
}

int main() {
    struct Stack stack;
    stack.top = -1;  // Initialize stack with an empty state

    push(&stack, 10);  // Push 10 onto the stack
    push(&stack, 20);  // Push 20 onto the stack

    printf("%d popped from stack\n", pop(&stack));  // Pop the top element and print it

    return 0;
}

Output

20 popped from stack

Explanation Of Code

Time and Space Complexity

4. Queues

A queue is a linear data structure that functions based on a First In, First Out (FIFO) principle. The first element you put in the queue is the first element removed from the queue, which is just like being in a line at a ticket counter.

Key Operations

Variants

Typical Use Cases

Example Code

#include <stdio.h>
#include <stdlib.h>

#define MAX 5 // Maximum size of the queue

struct Queue {
    int front, rear;
    int items[MAX];
};

void enqueue(struct Queue* queue, int item) {
    if (queue->rear == MAX - 1) {
        printf("Queue Overflow\n");
        return;
    }
    queue->items[++queue->rear] = item;
}

int dequeue(struct Queue* queue) {
    if (queue->front > queue->rear) {
        printf("Queue Underflow\n");
        return -1; // Indicates empty queue
    }
    return queue->items[queue->front++];
}

int main() {
    struct Queue queue;
    queue.front = queue.rear = -1;

    enqueue(&queue, 10);
    enqueue(&queue, 20);

    printf("%d dequeued from queue\n", dequeue(&queue));

    return 0;
}

Output

10 dequeued from queue

Explanation Of Code

Time and Space Complexity

Quick Recap

Advanced Data Structures in C

Advanced data structures in C offer more sophisticated ways to represent and process data. These data structures are ideal when you need to work with more complex relationships or when managing large volumes of data efficiently.

1. Trees and Binary Trees

A tree is a non-linear, hierarchical data structure comprised of nodes connected by edges. The topmost node is referred to as the root node. A node can have zero or more children; however, in a binary tree, a node has at most two child nodes, which are called a left child and a right child. Trees are important for representing hierarchical relationships between data, like file systems and organizational charts.

Node Structure

Each node of a binary tree will typically contain:

Binary Search Tree (BST)

A binary search tree is a special type of binary tree that has the property that:

This property makes searching, inserting, and removing in the tree more efficient.

Key Operations

Example Code: Node Structure and In-Order Traversal in C

#include <stdio.h>
#include <stdlib.h>

struct Node {
    int data;
    struct Node* left;
    struct Node* right;
};

struct Node* createNode(int data) {
    struct Node* node = (struct Node*)malloc(sizeof(struct Node));
    node->data = data;
    node->left = node->right = NULL;
    return node;
}

void inorder(struct Node* root) {
    if (root != NULL) {
        inorder(root->left);
        printf("%d ", root->data);
        inorder(root->right);
    }
}

int main() {
    struct Node* root = createNode(10);
    root->left = createNode(5);
    root->right = createNode(15);

    printf("In-order traversal: ");
    inorder(root);  // Output: 5 10 15

    return 0;
}

Advantages

Disadvantages

Applications

2. Heaps

A heap is a specialized tree-based data structure that satisfies the heap property. Heaps are most commonly implemented as arrays, allowing efficient access and manipulation of elements.

Key Concepts and Relevant Terms

Example: Max-Heap Implementation in C

#include <stdio.h>
#define MAX 100

int heap[MAX];
int size = 0;

void swap(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

// Insert a new element into the heap
void insert(int value) {
    if (size >= MAX) {
        printf("Heap overflow\n");
        return;
    }

    heap[size] = value;
    int i = size;
    size++;

    while (i != 0 && heap[(i - 1) / 2] < heap[i]) {
        swap(&heap[(i - 1) / 2], &heap[i]);
        i = (i - 1) / 2;
    }
}

// Heapify down from index i
void heapifydown(int i) {
    int largest = i;
    int left = 2 * i + 1;
    int right = 2 * i + 2;

    if (left < size && heap[left] > heap[largest])
        largest = left;

    if (right < size && heap[right] > heap[largest])
        largest = right;

    if (largest != i) {
        swap(&heap[i], &heap[largest]);
        heapifydown(largest);
    }
}

// Remove and return the maximum element (root)
int extractmax() {
    if (size == 0) {
        printf("Heap underflow\n");
        return -1;
    }

    int root = heap[0];
    heap[0] = heap[size - 1];
    size--;
    heapifydown(0);

    return root;
}

Primary Use Cases

3. Graph

A graph is a non-linear data structure representing a collection of vertices (also referred to as nodes) and a collection of edges that link pairs of vertices. Graphs are excellent for modeling relationships or networks, such as social networks, transportation systems, and communication networks.

Key Concepts and Relevant Terms

Graph Representation in C

Graphs can be represented in several different ways, however, the most common representations are an adjacency list (suggested) or an adjacency matrix (less preferred) is one way to represent the structure of a graph. Adjacency list is preferred as an efficient sparse graph representation.

Adjacency List Example in C

#include <stdio.h>
#include <stdlib.h>

struct Node {
    int vertex;
    struct Node* next;
};

struct Graph {
    int numVertices;
    struct Node** adjLists;
};

struct Node* createNode(int v) {
    struct Node* newNode = malloc(sizeof(struct Node));
    newNode->vertex = v;
    newNode->next = NULL;
    return newNode;
}

struct Graph* createGraph(int vertices) {
    struct Graph* graph = malloc(sizeof(struct Graph));
    graph->numVertices = vertices;

    graph->adjLists = malloc(vertices * sizeof(struct Node*));
    for (int i = 0; i < vertices; i++)
        graph->adjLists[i] = NULL;

    return graph;
}

void addEdge(struct Graph* graph, int src, int dest) {
    // Add edge from src to dest
    struct Node* newNode = createNode(dest);
    newNode->next = graph->adjLists[src];
    graph->adjLists[src] = newNode;

    // Add edge from dest to src (undirected graph)
    newNode = createNode(src);
    newNode->next = graph->adjLists[dest];
    graph->adjLists[dest] = newNode;
}

void printGraph(struct Graph* graph) {
    for (int v = 0; v < graph->numVertices; v++) {
        struct Node* temp = graph->adjLists[v];
        printf("Vertex %d: ", v);

        while (temp) {
            printf("%d -> ", temp->vertex);
            temp = temp->next;
        }
        printf("NULL\n");
    }
}

int main() {
    struct Graph* graph = createGraph(4);

    addEdge(graph, 0, 1);
    addEdge(graph, 0, 2);
    addEdge(graph, 1, 2);
    addEdge(graph, 2, 3);

    printGraph(graph);

    return 0;
}

Common Scenarios Where Graphs are Applied

4. Hashing

Hashing is a method for mapping data (for example, keys) to a fixed-size value (the "hash value/digest") using a mathematical method (the "hash function"). This process leads to more efficient searches, inserts and deletes in data structures such as "hash tables".

Key Concepts and Relevant Terms

Example: Hash Table Implementation in C Using Chaining

#include <stdio.h>
#include <stdlib.h>

#define TABLE_SIZE 10

struct Node {
    int key;
    struct Node* next;
};

struct Node* hashTable[TABLE_SIZE];

// Hash function
int hashFunction(int key) {
    return key % TABLE_SIZE;
}

// Insert key
void insert(int key) {
    int index = hashFunction(key);

    struct Node* newNode = malloc(sizeof(struct Node));
    newNode->key = key;
    newNode->next = hashTable[index];

    hashTable[index] = newNode;
}

// Search key
int search(int key) {
    int index = hashFunction(key);
    struct Node* temp = hashTable[index];

    while (temp) {
        if (temp->key == key)
            return 1;
        temp = temp->next;
    }

    return 0;
}

// Delete key
void delete(int key) {
    int index = hashFunction(key);
    struct Node* temp = hashTable[index];
    struct Node* prev = NULL;

    while (temp) {
        if (temp->key == key) {
            if (prev)
                prev->next = temp->next;
            else
                hashTable[index] = temp->next;

            free(temp);
            return;
        }

        prev = temp;
        temp = temp->next;
    }
}

Typical Applications

Quick Summary: Advanced Data Structures in C

Pointers in Data Structure

A pointer is a variable that contains a memory address instead of the actual data value. Pointers are the basis of C programming when dealing with dynamic data; data can be accessed via a pointer rather than its value. For example, in the context of Data Structures in C, Pointers allow access to memory efficiently and manipulate the data without caching large data values.

Example Code

#include <stdio.h>
#include <stdlib.h>

// Define the structure of a node in the linked list
struct Node {
    int data;
    struct Node* next;
};

int main() {
    // Dynamically allocate memory for the first node
    struct Node* head = (struct Node*)malloc(sizeof(struct Node));

    // Assign data to the node
    head->data = 10;

    // Set the next pointer to NULL (indicating the end of the list)
    head->next = NULL;

    // Print the data stored in the node
    printf("Data: %d", head->data);

    // Free the allocated memory
    free(head);

    return 0;
}

Output

Data: 10

Explanation of Code

Time and Space Complexity

Bottom Line

Pointers are the driving forces behind dynamic data structures in C. They let you create dynamic structures, like linked lists, trees, and graphs, without the overhead of duplicating large pieces of data. You must understand pointers. They are what make simple programs into larger, real-world systems, which are usually much more complicated.

Structure and Union

Structures and unions are user-defined data types in C that allow you to group different types of variables together. They allow you to store a collection of data of different types. However, they differ in terms of memory management and usage in some significant ways.

Structures

A structure provides you the ability to group the variables of different types into one variable using a single name to access them. This is convenient when you are trying to represent a collection of related data such as characteristics about a person, book or product.

Code Example

#include <stdio.h>

struct Person {
    char name[50];
    int age;
    float salary;
};

int main() {
    struct Person person1 = {"John Doe", 30, 50000.50};

    printf("Name: %s\n", person1.name);
    printf("Age: %d\n", person1.age);
    printf("Salary: %.2f\n", person1.salary);

    return 0;
}

Output

Name: John Doe
Age: 30
Salary: 50000.50

Unions

A union allows multiple data types to be stored in the same memory location. However, only one value can be stored at any given time. The memory used by a union is the size of its largest member, and all members share the same memory space.

Code Example

#include <stdio.h>

union Data {
    int intValue;
    float floatValue;
    char charValue;
};

int main() {
    union Data data;

    data.intValue = 10;
    printf("Integer: %d\n", data.intValue);

    data.floatValue = 3.14;
    printf("Float: %.2f\n", data.floatValue);

    data.charValue = 'A';
    printf("Character: %c\n", data.charValue);

    return 0;
}

Output

Integer: 10
Float: 3.14
Character: A

Bottom Line

The difference between structures and union in C is that structures let you store multiple related values together, while unions let you save memory by storing different values in the same location, but only one at a time. Use structures when you need to keep all fields, and unions when memory efficiency matters. Together, they give C the flexibility to model real-world data effectively.

Applications of Data Structures in C

Data structures in C are fundamental to many areas of computer science and used in combinations of applications to help solve real-world problems efficiently. In C, these structures are of fundamental importance in several areas.

Conclusion

Before you move to more advanced programming, ask yourself one thing: Are you writing code that works… or code that performs? Anyone can build a program that runs, but only a developer who understands data structures can build one that scales, responds quickly, and handles real-world complexity without breaking.

Every structure you learned — arrays, linked lists, stacks, queues, trees, graphs, hashing, pointers, structures — exists because real problems demand real organization. When data grows, when time matters, when memory becomes tight, the right data structure is what saves your program.

So the real question is: do you want code that just solves the problem today, or would you prefer code that just solves the problem today and still executes efficiently tomorrow?

Understanding data structures in C is how you assimilate into that next level.

Frequently Asked Questions

1. What are data structures in C?

Data structures in C facilitate the efficient management and storage of data. Acquaintance with data structures like arrays, linked lists, stacks, queues, trees, and graphs is crucial, as different data structures are needed for different tasks in programming.

2. What is the difference between arrays and linked lists?

An array is a data structure that stores elements in a contiguous block of memory of a fixed size. A linked list is comprised of elements stored in nodes linked together by pointers, which allows for the dynamic size of a data structure, while also slower access due to its more complicated nature.

3. How do pointers work in data structures?

Pointers allow for the storage of memory addresses of variables of values. Data structures such a linked list are advantageous because each group, or node, can refer to un-related data, which allows for efficient data usage while retaining the ability to link the data.

4. What is a stack and its applications?

A stack implements data on a Last In First Out (LIFO) basis. There are many applications of a stack including managing function calls, evaluating expressions, and backtracking algorithms.

5. What are the advantages of using a queue?

A queue is a different data structure that operates based on a first-come, first-serve basis. Similar to a stack, there are many potential uses of a queue, including managing the printing of documents, scheduling a process, and handling a data flow.

6. What is the role of trees in data structures?

A tree shows the hierarchical relationship of an arrangement of data. A tree can be used in applications such as a database, and in an organizational cycle, such as a file system tree. Trees can also illustrate algorithms very effectively in sorting and searching.

7. How do dynamic data structures differ from static ones?

Dynamic data structures can grow or shrink during execution, like linked lists, while static ones, like arrays, have a fixed size set at compile time.


Related Resources


Source: NxtWave - https://www.ccbp.in/blog/articles/data-structures-in-c