C Advanced Projects

Complex projects showcasing advanced C programming concepts

🚀 Welcome to Advanced Projects

Master advanced C programming with complex projects involving dynamic memory, data structures, networking, and system programming for professional development skills.

Advanced Project Categories

🏗️

Data Structures

Complex data organization

Linked Lists Binary Trees Hash Tables Graphs
💾

System Programming

Low-level system operations

Memory Manager Process Monitor File System
🌐

Network Programming

Client-server applications

Chat Server HTTP Client FTP Server
🔐

Security & Crypto

Encryption and security tools

Password Manager File Encryption Hash Functions

🔹 Project 1: Dynamic Linked List Implementation

Building a complete linked list develops your understanding of dynamic memory management and pointer manipulation in C. This project involves creating nodes with data and next pointers, implementing insertion and deletion operations, and traversing the list. You'll practice allocating memory with malloc(), deallocating with free(), and handling edge cases. A linked list project reinforces pointer concepts while building a fundamental data structure used in countless applications. This hands-on experience strengthens your ability to manage dynamic memory effectively.

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

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

struct Node* head = NULL;

struct Node* createNode(int data) {
    struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
    if(newNode == NULL) {
        printf("Memory allocation failed!\n");
        exit(1);
    }
    newNode->data = data;
    newNode->next = NULL;
    return newNode;
}

void insertAtBeginning(int data) {
    struct Node* newNode = createNode(data);
    newNode->next = head;
    head = newNode;
    printf("Inserted %d at beginning\n", data);
}

void insertAtEnd(int data) {
    struct Node* newNode = createNode(data);
    
    if(head == NULL) {
        head = newNode;
        printf("Inserted %d as first element\n", data);
        return;
    }
    
    struct Node* temp = head;
    while(temp->next != NULL) {
        temp = temp->next;
    }
    temp->next = newNode;
    printf("Inserted %d at end\n", data);
}

void insertAtPosition(int data, int position) {
    if(position < 1) {
        printf("Position should be >= 1\n");
        return;
    }
    
    if(position == 1) {
        insertAtBeginning(data);
        return;
    }
    
    struct Node* newNode = createNode(data);
    struct Node* temp = head;
    
    for(int i = 1; i < position - 1 && temp != NULL; i++) {
        temp = temp->next;
    }
    
    if(temp == NULL) {
        printf("Position out of bounds\n");
        free(newNode);
        return;
    }
    
    newNode->next = temp->next;
    temp->next = newNode;
    printf("Inserted %d at position %d\n", data, position);
}

void deleteByValue(int data) {
    if(head == NULL) {
        printf("List is empty\n");
        return;
    }
    
    if(head->data == data) {
        struct Node* temp = head;
        head = head->next;
        free(temp);
        printf("Deleted %d from beginning\n", data);
        return;
    }
    
    struct Node* temp = head;
    while(temp->next != NULL && temp->next->data != data) {
        temp = temp->next;
    }
    
    if(temp->next == NULL) {
        printf("Element %d not found\n", data);
        return;
    }
    
    struct Node* nodeToDelete = temp->next;
    temp->next = nodeToDelete->next;
    free(nodeToDelete);
    printf("Deleted %d from list\n", data);
}

void displayList() {
    if(head == NULL) {
        printf("List is empty\n");
        return;
    }
    
    printf("List: ");
    struct Node* temp = head;
    while(temp != NULL) {
        printf("%d -> ", temp->data);
        temp = temp->next;
    }
    printf("NULL\n");
}

void searchElement(int data) {
    struct Node* temp = head;
    int position = 1;
    
    while(temp != NULL) {
        if(temp->data == data) {
            printf("Element %d found at position %d\n", data, position);
            return;
        }
        temp = temp->next;
        position++;
    }
    printf("Element %d not found in list\n", data);
}

void freeList() {
    struct Node* temp;
    while(head != NULL) {
        temp = head;
        head = head->next;
        free(temp);
    }
    printf("Memory freed successfully\n");
}

int main() {
    int choice, data, position;
    
    do {
        printf("\n=== Dynamic Linked List ===\n");
        printf("1. Insert at Beginning\n");
        printf("2. Insert at End\n");
        printf("3. Insert at Position\n");
        printf("4. Delete by Value\n");
        printf("5. Display List\n");
        printf("6. Search Element\n");
        printf("7. Exit\n");
        printf("Enter choice: ");
        scanf("%d", &choice);
        
        switch(choice) {
            case 1:
                printf("Enter data: ");
                scanf("%d", &data);
                insertAtBeginning(data);
                break;
            case 2:
                printf("Enter data: ");
                scanf("%d", &data);
                insertAtEnd(data);
                break;
            case 3:
                printf("Enter data: ");
                scanf("%d", &data);
                printf("Enter position: ");
                scanf("%d", &position);
                insertAtPosition(data, position);
                break;
            case 4:
                printf("Enter data to delete: ");
                scanf("%d", &data);
                deleteByValue(data);
                break;
            case 5:
                displayList();
                break;
            case 6:
                printf("Enter data to search: ");
                scanf("%d", &data);
                searchElement(data);
                break;
            case 7:
                freeList();
                printf("Goodbye!\n");
                break;
            default:
                printf("Invalid choice!\n");
        }
    } while(choice != 7);
    
    return 0;
}

Sample Output:

=== Dynamic Linked List ===
1. Insert at Beginning
2. Insert at End
3. Insert at Position
4. Delete by Value
5. Display List
6. Search Element
7. Exit
Enter choice: 2
Enter data: 10
Inserted 10 as first element

🔹 Project 2: Binary Search Tree

Implementing a binary search tree teaches advanced pointer manipulation and recursive algorithms for efficient data organization. This project requires creating tree nodes, implementing insertion with proper ordering, and writing traversal functions like inorder, preorder, and postorder. You'll practice recursive pointer navigation and develop search, deletion, and rebalancing logic. A BST project demonstrates how pointers enable complex hierarchical data structures. Completing this project significantly enhances your problem-solving skills and data structure knowledge.

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

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

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

struct TreeNode* insert(struct TreeNode* root, int data) {
    if(root == NULL) {
        return createNode(data);
    }
    
    if(data < root->data) {
        root->left = insert(root->left, data);
    } else if(data > root->data) {
        root->right = insert(root->right, data);
    }
    
    return root;
}

struct TreeNode* findMin(struct TreeNode* root) {
    while(root && root->left) {
        root = root->left;
    }
    return root;
}

struct TreeNode* deleteNode(struct TreeNode* root, int data) {
    if(root == NULL) {
        return root;
    }
    
    if(data < root->data) {
        root->left = deleteNode(root->left, data);
    } else if(data > root->data) {
        root->right = deleteNode(root->right, data);
    } else {
        // Node to be deleted found
        if(root->left == NULL) {
            struct TreeNode* temp = root->right;
            free(root);
            return temp;
        } else if(root->right == NULL) {
            struct TreeNode* temp = root->left;
            free(root);
            return temp;
        }
        
        // Node with two children
        struct TreeNode* temp = findMin(root->right);
        root->data = temp->data;
        root->right = deleteNode(root->right, temp->data);
    }
    return root;
}

struct TreeNode* search(struct TreeNode* root, int data) {
    if(root == NULL || root->data == data) {
        return root;
    }
    
    if(data < root->data) {
        return search(root->left, data);
    }
    
    return search(root->right, data);
}

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

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

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

int findHeight(struct TreeNode* root) {
    if(root == NULL) {
        return -1;
    }
    
    int leftHeight = findHeight(root->left);
    int rightHeight = findHeight(root->right);
    
    return 1 + (leftHeight > rightHeight ? leftHeight : rightHeight);
}

int countNodes(struct TreeNode* root) {
    if(root == NULL) {
        return 0;
    }
    return 1 + countNodes(root->left) + countNodes(root->right);
}

int main() {
    struct TreeNode* root = NULL;
    int choice, data;
    struct TreeNode* result;
    
    do {
        printf("\n=== Binary Search Tree ===\n");
        printf("1. Insert Node\n");
        printf("2. Delete Node\n");
        printf("3. Search Node\n");
        printf("4. Inorder Traversal\n");
        printf("5. Preorder Traversal\n");
        printf("6. Postorder Traversal\n");
        printf("7. Tree Height\n");
        printf("8. Count Nodes\n");
        printf("9. Exit\n");
        printf("Enter choice: ");
        scanf("%d", &choice);
        
        switch(choice) {
            case 1:
                printf("Enter data to insert: ");
                scanf("%d", &data);
                root = insert(root, data);
                printf("Inserted %d into tree\n", data);
                break;
            case 2:
                printf("Enter data to delete: ");
                scanf("%d", &data);
                root = deleteNode(root, data);
                printf("Deleted %d from tree\n", data);
                break;
            case 3:
                printf("Enter data to search: ");
                scanf("%d", &data);
                result = search(root, data);
                if(result) {
                    printf("Element %d found in tree\n", data);
                } else {
                    printf("Element %d not found in tree\n", data);
                }
                break;
            case 4:
                printf("Inorder Traversal: ");
                inorderTraversal(root);
                printf("\n");
                break;
            case 5:
                printf("Preorder Traversal: ");
                preorderTraversal(root);
                printf("\n");
                break;
            case 6:
                printf("Postorder Traversal: ");
                postorderTraversal(root);
                printf("\n");
                break;
            case 7:
                printf("Tree Height: %d\n", findHeight(root));
                break;
            case 8:
                printf("Total Nodes: %d\n", countNodes(root));
                break;
            case 9:
                printf("Goodbye!\n");
                break;
            default:
                printf("Invalid choice!\n");
        }
    } while(choice != 9);
    
    return 0;
}

Sample Output:

=== Binary Search Tree ===
1. Insert Node
2. Delete Node
3. Search Node
4. Inorder Traversal
5. Preorder Traversal
6. Postorder Traversal
7. Tree Height
8. Count Nodes
9. Exit
Enter choice: 1
Enter data to insert: 50
Inserted 50 into tree

🔹 Project 3: Custom Memory Pool Allocator

Creating a custom memory allocator teaches advanced memory management and demonstrates why efficient allocation strategies matter in production systems. This ambitious project involves pre-allocating large memory blocks and implementing allocation/deallocation from your pool. You'll develop tracking mechanisms, handle fragmentation, and optimize performance. Understanding memory pool allocation deepens your appreciation for system-level programming. This project combines pointer arithmetic, data structures, and performance optimization techniques into one comprehensive challenge.

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

#define POOL_SIZE 1024
#define BLOCK_SIZE 32
#define MAX_BLOCKS (POOL_SIZE / BLOCK_SIZE)

typedef struct Block {
    int is_free;
    size_t size;
    struct Block* next;
} Block;

typedef struct MemoryPool {
    char memory[POOL_SIZE];
    Block blocks[MAX_BLOCKS];
    Block* free_list;
    int total_blocks;
    int free_blocks;
    size_t total_allocated;
} MemoryPool;

MemoryPool pool;

void initializePool() {
    memset(&pool, 0, sizeof(MemoryPool));
    
    // Initialize all blocks as free
    for(int i = 0; i < MAX_BLOCKS; i++) {
        pool.blocks[i].is_free = 1;
        pool.blocks[i].size = BLOCK_SIZE;
        pool.blocks[i].next = (i < MAX_BLOCKS - 1) ? &pool.blocks[i + 1] : NULL;
    }
    
    pool.free_list = &pool.blocks[0];
    pool.total_blocks = MAX_BLOCKS;
    pool.free_blocks = MAX_BLOCKS;
    
    printf("Memory pool initialized: %d blocks of %d bytes each\n", MAX_BLOCKS, BLOCK_SIZE);
}

void* poolAlloc(size_t size) {
    if(size > BLOCK_SIZE) {
        printf("Error: Requested size %zu exceeds block size %d\n", size, BLOCK_SIZE);
        return NULL;
    }
    
    if(pool.free_list == NULL) {
        printf("Error: No free blocks available\n");
        return NULL;
    }
    
    Block* block = pool.free_list;
    pool.free_list = block->next;
    
    block->is_free = 0;
    block->size = size;
    block->next = NULL;
    
    pool.free_blocks--;
    pool.total_allocated += size;
    
    // Calculate memory address
    int block_index = block - pool.blocks;
    void* ptr = pool.memory + (block_index * BLOCK_SIZE);
    
    printf("Allocated %zu bytes at block %d\n", size, block_index);
    return ptr;
}

void poolFree(void* ptr) {
    if(ptr == NULL) {
        printf("Error: Cannot free NULL pointer\n");
        return;
    }
    
    // Calculate block index from pointer
    char* char_ptr = (char*)ptr;
    if(char_ptr < pool.memory || char_ptr >= pool.memory + POOL_SIZE) {
        printf("Error: Pointer not in pool range\n");
        return;
    }
    
    int block_index = (char_ptr - pool.memory) / BLOCK_SIZE;
    if(block_index >= MAX_BLOCKS) {
        printf("Error: Invalid block index\n");
        return;
    }
    
    Block* block = &pool.blocks[block_index];
    if(block->is_free) {
        printf("Error: Block %d is already free\n", block_index);
        return;
    }
    
    // Free the block
    pool.total_allocated -= block->size;
    block->is_free = 1;
    block->next = pool.free_list;
    pool.free_list = block;
    pool.free_blocks++;
    
    printf("Freed block %d\n", block_index);
}

void printPoolStatus() {
    printf("\n=== Memory Pool Status ===\n");
    printf("Total blocks: %d\n", pool.total_blocks);
    printf("Free blocks: %d\n", pool.free_blocks);
    printf("Used blocks: %d\n", pool.total_blocks - pool.free_blocks);
    printf("Total allocated: %zu bytes\n", pool.total_allocated);
    printf("Pool utilization: %.2f%%\n", 
           (float)(pool.total_blocks - pool.free_blocks) / pool.total_blocks * 100);
    
    printf("\nBlock status: ");
    for(int i = 0; i < MAX_BLOCKS; i++) {
        printf("%c", pool.blocks[i].is_free ? 'F' : 'U');
        if((i + 1) % 16 == 0) printf("\n              ");
    }
    printf("\n(F = Free, U = Used)\n");
}

void defragmentPool() {
    printf("Defragmenting pool...\n");
    
    // Simple defragmentation: rebuild free list
    pool.free_list = NULL;
    pool.free_blocks = 0;
    
    for(int i = MAX_BLOCKS - 1; i >= 0; i--) {
        if(pool.blocks[i].is_free) {
            pool.blocks[i].next = pool.free_list;
            pool.free_list = &pool.blocks[i];
            pool.free_blocks++;
        }
    }
    
    printf("Defragmentation complete\n");
}

int main() {
    int choice;
    size_t size;
    void* ptr;
    void* allocated_ptrs[MAX_BLOCKS] = {NULL};
    int ptr_count = 0;
    
    initializePool();
    
    do {
        printf("\n=== Memory Pool Allocator ===\n");
        printf("1. Allocate Memory\n");
        printf("2. Free Memory\n");
        printf("3. Pool Status\n");
        printf("4. Defragment Pool\n");
        printf("5. Test Allocation Pattern\n");
        printf("6. Exit\n");
        printf("Enter choice: ");
        scanf("%d", &choice);
        
        switch(choice) {
            case 1:
                printf("Enter size to allocate: ");
                scanf("%zu", &size);
                ptr = poolAlloc(size);
                if(ptr && ptr_count < MAX_BLOCKS) {
                    allocated_ptrs[ptr_count++] = ptr;
                }
                break;
                
            case 2:
                if(ptr_count > 0) {
                    printf("Freeing last allocated pointer...\n");
                    poolFree(allocated_ptrs[--ptr_count]);
                    allocated_ptrs[ptr_count] = NULL;
                } else {
                    printf("No allocated pointers to free\n");
                }
                break;
                
            case 3:
                printPoolStatus();
                break;
                
            case 4:
                defragmentPool();
                break;
                
            case 5:
                printf("Testing allocation pattern...\n");
                for(int i = 0; i < 5; i++) {
                    void* test_ptr = poolAlloc(16);
                    if(test_ptr && ptr_count < MAX_BLOCKS) {
                        allocated_ptrs[ptr_count++] = test_ptr;
                    }
                }
                printPoolStatus();
                break;
                
            case 6:
                printf("Cleaning up and exiting...\n");
                break;
                
            default:
                printf("Invalid choice!\n");
        }
    } while(choice != 6);
    
    return 0;
}

Sample Output:

Memory pool initialized: 32 blocks of 32 bytes each

=== Memory Pool Allocator ===
1. Allocate Memory
2. Free Memory
3. Pool Status
4. Defragment Pool
5. Test Allocation Pattern
6. Exit
Enter choice: 1
Enter size to allocate: 24
Allocated 24 bytes at block 0

🧠 Test Your Knowledge

Which function is used to allocate memory dynamically in C?