PYnative

Python Programming

  • Learn Python
    • Python Tutorials
    • Python Basics
    • Python Interview Q&As
  • Exercises
    • Python Exercises
    • C Programming Exercises
    • C++ Exercises
  • Quizzes
  • Code Editor
    • Online Python Code Editor
    • Online C Compiler
    • Online C++ Compiler
Home » C Programming Exercises » Intermediate C Programming Exercises: 45 Coding Problems with Solutions

Intermediate C Programming Exercises: 45 Coding Problems with Solutions

Updated on: January 7, 2026 | Leave a Comment

Once you have mastered the basics of C programming and completed our 9 topic-specific C programming exercises, it’s time to move on to more complex problems that bridge the gap between writing code and engineering software.

This comprehensive guide provides 45+ Intermediate C programming practice problems designed specifically for Intermediate developers.

Each coding exercise follows a structured format: a clear Practice Problem, the Given Input to get you started, a Hint to nudge your logic, and a Fully Explained Solution to ensure you understand the why behind the how.

Also, See: C Programming Exercises with 10 topic-wise sets and 320+ practice questions.

What You’ll Practice

These challenges focus on:

  • General Intermediate coding questions
  • Memory Manipulation: Using pointers and malloc/free correctly.
  • Data Structures: Implementing linked lists, stacks, and queues from scratch.
  • Algorithm Logic: Sorting, searching, and pattern-based problem solving.
  • File I/O: Reading and writing structured and binary data to the disk.

Use Online C Compiler to solve exercises.

+ Table Of Contents (45 Exercises)

Table of contents

  • Exercise 1: Swap by Reference
  • Exercise 2: Dynamic Array Allocation
  • Exercise 3: Find Max with Pointers
  • Exercise 4: Array of Pointers (String Sorting)
  • Exercise 5: Realloc Growth (Dynamic Sizing)
  • Exercise 6: Function Pointers (Operation Selection)
  • Exercise 7: Custom strlen with Pointer Subtraction
  • Exercise 8: String Reversal In-Place
  • Exercise 9: Anagram Checker (Frequency Array)
  • Exercise 10: Palindrome Checker (Two-Pointer Method)
  • Exercise 11: Word Counter
  • Exercise 12: Student Database (Struct Arrays)
  • Exercise 13: Complex Number Arithmetic (Returning Structs)
  • Exercise 14: Time Difference (Normalization)
  • Exercise 15: Nested Structs (Composition)
  • Exercise 16: Unions vs. Structs (Memory Sharing)
  • Exercise 17: File Copy (Character Stream)
  • Exercise 18: Line Counter
  • Exercise 19: Binary Struct Storage
  • Exercise 20: Singly Linked List (Insertion at Head)
  • Exercise 21: Linked List Search
  • Exercise 22: Stack (Array-based)
  • Exercise 23: Queue (Enqueue Operation)
  • Exercise 24: Delete Node by Value
  • Exercise 25: Binary Search
  • Exercise 26: Bubble Sort vs Selection Sort
  • Exercise 27: Bitwise Toggle
  • Exercise 28: Decimal to Binary (Bitmasking)
  • Exercise 29: Sorting Structure Data
  • Exercise 30: Second Largest Element in an Array
  • Exercise 31: Merge Two Sorted Arrays
  • Exercise 32: Remove Duplicate Elements
  • Exercise 33: Most Frequent Character
  • Exercise 34: Fibonacci Series (Recursion)
  • Exercise 35: Intersection of Two Arrays
  • Exercise 36: Move Zeros to the End
  • Exercise 37: Sort Strings Alphabetically (Pointers)
  • Exercise 38: Longest Word in a Sentence
  • Exercise 39: Pascal’s Triangle
  • Exercise 40: Hollow Triangle Pattern
  • Exercise 41: Solid Diamond Pattern
  • Exercise 42: Alternating Positive and Negative Order
  • Exercise 43: File Stats (Lines, Words, Characters)
  • Exercise 44: Middle Element of a Linked List

Exercise 1: Swap by Reference

Practice Problem: Write a function swap(int *a, int *b) that takes the addresses of two integers and swaps their values.

Exercise Purpose: This is the foundation of pointer-based manipulation. It teaches how C uses “Pass by Reference” via pointers to allow a function to change variables located in a different scope (like main).

Given Input: int x = 10, y = 20

Expected Output:

Before Swap: x = 10, y = 20
After Swap: x = 20, y = 10
+ Hint

Use a temporary integer variable inside the function to store the value of *a before you overwrite it with the value of *b.

+ Show Solution
#include <stdio.h>

void swap(int *a, int *b) {
    int temp = *a; // Store value at address a
    *a = *b;       // Put value from b into address a
    *b = temp;     // Put stored value into address b
}

int main() {
    int x = 10, y = 20;

    printf("Before Swap: x = %d, y = %d\n", x, y);
    
    swap(&x, &y); // Passing addresses of x and y
    
    printf("After Swap:  x = %d, y = %d\n", x, y);
    return 0;
}Code language: C++ (cpp)
Run

Explanation:

  • swap(&x, &y): The & operator sends the memory addresses of x and y to the function.
  • int temp = *a: The * (dereference) operator looks inside the memory address a to get the actual number (10) and saves it in temp.
  • *a = *b: This directly changes the memory at address a to whatever is currently sitting at address b.

Exercise 2: Dynamic Array Allocation

Practice Problem: Write a C++ program for a size N, allocates an integer array of that size using malloc, and fills it with values. Print the memory address of the array to show it is located on the Heap, then free the memory.

Exercise Purpose: Static arrays have a fixed size at compile-time. This exercise introduces Dynamic Memory Allocation, allowing your program to request exactly as much memory as it needs while it is running.

Given Input:

  • Array Size: 3
  • Values: 100, 200, 300

Expected Output:

Array allocated at Heap Address: 0x5f5579932910
Value at index 0: 100
Value at index 1: 200
Value at index 2: 300
Memory successfully freed.
+ Hint
  • Remember to include <stdlib.h> for malloc and always call free() at the end of your program.
  • malloc returns a void*, so you should cast it to (int*). The total bytes requested should be size * sizeof(int).
+ Show Solution
#include <stdio.h>
#include <stdlib.h>

int main() {
    int n = 3;
    int *arr = (int*)malloc(n * sizeof(int));

    if (arr == NULL) return 1; // Check if allocation failed

    printf("Array allocated at Heap Address: %p\n", (void*)arr);

    for (int i = 0; i < n; i++) {
        arr[i] = (i + 1) * 100;
        printf("Value at index %d: %d\n", i, arr[i]);
    }

    free(arr); // Clean up
    printf("Memory successfully freed.\n");
    return 0;
}Code language: C++ (cpp)
Run

Explanation of Solution:

Static arrays require a size known at compile-time.

  • malloc(n * sizeof(int)): Requests a block of bytes from the Heap. Since an int is usually 4 bytes, this requests 12 bytes for 3 integers.
  • if (arr == NULL): This is a safety check. If the computer is out of RAM, malloc returns NULL, and attempting to write to it would crash the program.
  • free(arr): This tells the Operating System that the 12 bytes are no longer needed. If you skip this in a large program, you create a “Memory Leak.”

Exercise 3: Find Max with Pointers

Practice Problem: Write a function that accepts an array and its size, then returns a pointer to the maximum element in that array. Print the value and the index of the max element using that pointer.

Exercise Purpose: This demonstrates that pointers can be used not just to point to variables, but to navigate through array elements using pointer arithmetic.

Given Input: Array: {12, 45, 7, 99, 23}

Expected Output:

Searching array: 12, 45, 7, 99, 23
Max Value: 99 found at Index: 3
+ Hint

Instead of returning an integer, return int*. Inside the function, compare the value at the current pointer address with the value at the max pointer address.

+ Show Solution
#include <stdio.h>

int* find_max(int *arr, int size) {
    int *max_ptr = arr;
    for (int i = 1; i < size; i++) {
        if (*(arr + i) > *max_ptr) {
            max_ptr = arr + i;
        }
    }
    return max_ptr;
}

int main() {
    int nums[] = {12, 45, 7, 99, 23};
    int *max = find_max(nums, 5);
    printf("Max value: %d\n", *max);
    return 0;
}Code language: C++ (cpp)
Run

Explanation:

  • max_ptr = arr + i: arr is the address of index 0. arr + i uses pointer arithmetic to calculate the memory address of index i.
  • *(arr + i): The * dereferences the calculated address to get the number stored there for comparison.
  • max - nums: Subtracting the base address of the array (nums) from the pointer to an element (max) gives the number of elements between them, which is the index.

This approach is widely used in C to allow the caller to not only read the maximum value but also modify it directly at its source memory location if needed.

Exercise 4: Array of Pointers (String Sorting)

Practice Problem: Create an array of 3 strings (char pointers). Write a program to swap the pointers to “sort” the strings based on their length.

Given:

const char *list[] = {"Watermelon", "Apple", "Banana"};Code language: C++ (cpp)

Expected Output:

Shortest: Apple, Longest: Watermelon
+ Hint

You aren’t swapping the characters inside the strings; you are just swapping the memory addresses stored in the pointer array.

+ Show Solution
#include <stdio.h>
#include <string.h>

int main() {
    char *list[] = {"Watermelon", "Apple", "Banana"};
    
    for (int i = 0; i < 2; i++) {
        if (strlen(list[i]) > strlen(list[i+1])) {
            char *temp = list[i];
            list[i] = list[i+1];
            list[i+1] = temp;
        }
    }
    printf("Shortest: %s, Longest: %s\n", list[0], list[1]);
    return 0;
}Code language: C++ (cpp)
Run

Explanation:

An array of pointers treats strings as independent blocks of memory. Swapping two pointers is a constant-time operation (O(1)), which is significantly faster than using strcpy to move every character of a long string.

This technique is the backbone of efficient sorting algorithms in C, where we move small “handles” (pointers) to the data rather than moving the bulky data itself.

Exercise 5: Realloc Growth (Dynamic Sizing)

Practice Problem: Create a dynamic array with an initial capacity of 2. Add numbers to it, and when the capacity is reached, use realloc to double the size. Stop when 5 numbers are added and print the final capacity.

Exercise Purpose: This shows how the realloc function handles memory. If there isn’t enough space to expand the current block, it finds a new home for your data, copies it, and deletes the old block automatically.

Given Input:

  • Initial Capacity: 2
  • Total Numbers to Add: 5

Expected Output:

Initial Capacity: 2
..Capacity doubled to 4..
..Capacity doubled to 8..
Final Data: 10 20 30 40 50
Final Capacity: 8
+ Hint
  • Always use a temporary pointer to hold the result of realloc.
  • If realloc fails and returns NULL, you don’t want to lose your original pointer to the existing data.
+ Show Solution
#include <stdio.h>
#include <stdlib.h>

int main() {
    int capacity = 2, count = 0;
    int *arr = malloc(capacity * sizeof(int));

    printf("Initial Capacity: %d\n", capacity);

    for (int i = 1; i <= 5; i++) {
        if (count == capacity) {
            capacity *= 2;
            int *temp = realloc(arr, capacity * sizeof(int));
            if (temp != NULL) arr = temp;
            printf("..Capacity doubled to %d..\n", capacity);
        }
        arr[count++] = i * 10;
    }

    printf("Final Data: ");
    for (int i = 0; i < count; i++) printf("%d ", arr[i]);
    printf("\nFinal Capacity: %d\n", capacity);

    free(arr);
    return 0;
}Code language: C++ (cpp)
Run

Explanation of Solution:

  • capacity *= 2: We grow the array exponentially. This is an industry-standard strategy to balance performance and memory usage.
  • realloc(arr, capacity * sizeof(int)): It looks for a larger block. If it can’t find one at the current address, it moves the data to a new location and returns the new address.
  • arr = temp: We update our pointer to the new address provided by realloc.

Exercise 6: Function Pointers (Operation Selection)

Practice Problem: Create a “Calculator” function that performs an arithmetic operation on two integers using a function pointer. The program should demonstrate calling the add logic and then switching to the subtract logic via the same pointer.

Exercise Purpose: Function pointers allow you to treat logic as data. This is an essential intermediate skill used for implementing “callbacks” and designing modular code where the specific behavior of a function can be changed at runtime.

Given Input:

  • Integers: 15, 5
  • Operations: add(a, b) and subtract(a, b)

Expected Output:

Operation: ADD | Result: 20
Operation: SUB | Result: 10
+ Hint
  • The syntax for a function pointer is return_type (*pointer_name)(parameter_types).
  • You can assign a function name directly to this pointer.
+ Show Solution
#include <stdio.h>

int add(int a, int b) { return a + b; }
int subtract(int a, int b) { return a - b; }

int main() {
    int x = 15, y = 5;
    
    // Declare a function pointer
    int (*operation)(int, int);

    // Point to add and execute
    operation = add;
    printf("Operation: ADD | Result: %d\n", operation(x, y));

    // Point to subtract and execute
    operation = subtract;
    printf("Operation: SUB | Result: %d\n", operation(x, y));

    return 0;
}Code language: C++ (cpp)
Run

Explanation of Solution:

  • int (*operation)(int, int): This tells the compiler that operation is a pointer to a function that returns an int and takes two int parameters.
  • operation = add: In C, the name of a function is essentially its memory address. Here, we store the address of the add function in our pointer.
  • operation(x, y): The pointer is dereferenced to execute the code located at that memory address, behaving exactly like a direct function call.

Exercise 7: Custom strlen with Pointer Subtraction

Practice Problem: Write a function my_strlen(char *s) that calculates the length of a string without using any built-in library functions. Print the original string and the calculated length.

Exercise Purpose: This exercise demonstrates that strings are simply pointers to the first character of a null-terminated sequence. It also introduces the concept of pointer subtraction, which is an idiomatic and fast way to calculate distance in memory.

Given Input: String: "Hello World"

Expected Output:

String: Hello World
Calculated Length: 11
+ Hint
  • Move a second pointer p through the string until it hits the null terminator (\0).
  • The length is then the difference between the final pointer p and the start pointer s.
+ Show Solution
#include <stdio.h>

int my_strlen(char *s) {
    char *p = s; // Copy of the starting address
    while (*p != '\0') {
        p++; // Move pointer forward
    }
    return p - s; // Subtract starting address from end address
}

int main() {
    char text[] = "Hello World";
    
    int length = my_strlen(text);
    
    printf("String: %s\n", text);
    printf("Calculated Length: %d\n", length);
    
    return 0;
}Code language: C++ (cpp)
Run

Explanation:

  • while (*p != '\0'): The loop continues until the character at the current address p is the “null” character, which marks the end of all C strings.
  • p++: Increments the address stored in p by the size of one char (1 byte), moving us to the next character in memory.
  • p - s: Subtracting two pointers of the same type results in the number of elements between them. This is more efficient than using a separate integer counter.

Exercise 8: String Reversal In-Place

Practice Problem: Write a function to reverse a string “in-place,” meaning you modify the original string without using a second array. Print the string before and after reversal.

Exercise Purpose: This exercise teaches memory efficiency operation with O(1) space complexity and how to manipulate char arrays using two-pointer logic (one at the start, one at the end).

Given Input: String: "PYnative"

Expected Output:

Before: PYnative
After: evitanYP
+ Hint
  • Use two pointers: one at the start and one at the end.
  • Swap the characters and move the pointers toward each other. (Swap the first character with the last, then the second with the second-to-last).
  • Stop when the indices meet in the middle.
+ Show Solution
#include <stdio.h>
#include <string.h>

void reverse_string(char *str) {
    int len = strlen(str);
    for (int i = 0; i < len / 2; i++) {
        char temp = str[i];
        str[i] = str[len - 1 - i];
        str[len - 1 - i] = temp;
    }
}

int main() {
    char my_str[] = "C-Prog";

    printf("Before: %s\n", my_str);
    reverse_string(my_str);
    printf("After:  %s\n", my_str);

    return 0;
}Code language: C++ (cpp)
Run

Explanation of Solution:

  • len / 2: We only loop halfway. If we went the full length, we would swap the characters back to their original positions.
  • str[len - 1 - i]: This calculates the “mirror” index from the end. For index 0, it targets index 5 (if length is 6).
  • char my_str[]: The string must be defined as an array. If defined as char *s = "C-Prog", it is stored in read-only memory and would cause a crash if you tried to swap its characters.

Exercise 9: Anagram Checker (Frequency Array)

Practice Problem: Write a program to check if two strings are anagrams (contain the same letters in different orders). The program should display the two strings and the final result (Found/Not Found).

Exercise Purpose: This introduces the Frequency Array technique, a simple form of a hash map. Instead of expensive sorting, you use the character’s ASCII value as an index in an array to count occurrences.

Given Input:

  • String 1: "silent"
  • String 2: "listen"

Expected Output:

String A: silent | String B: listen
Result: Anagrams Found
+ Hint
  • Use an integer array of size 256 (for all ASCII characters).
  • Increment counts for string 1 and decrement for string 2.
  • If the array is all zeros at the end, they are anagrams.
+ Show Solution
#include <stdio.h>
#include <string.h>

int is_anagram(char *s1, char *s2) {
    int count[256] = {0}; // Initialize all counts to 0
    
    if (strlen(s1) != strlen(s2)) return 0;

    for (int i = 0; s1[i] != '\0'; i++) {
        count[(unsigned char)s1[i]]++; // Add for s1
        count[(unsigned char)s2[i]]--; // Subtract for s2
    }

    for (int i = 0; i < 256; i++) {
        if (count[i] != 0) return 0; // Any mismatch found
    }
    return 1;
}

int main() {
    char a[] = "silent", b[] = "listen";

    printf("String A: %s | String B: %s\n", a, b);
    
    if (is_anagram(a, b)) printf("Result: Anagrams Found\n");
    else printf("Result: Not Anagrams\n");

    return 0;
}Code language: C++ (cpp)
Run

Explanation of Solution:

  • count[(unsigned char)s1[i]]++: We use the character itself (e.g., ‘a’ = 97) as the index in our count array. If ‘a’ appears, index 97 becomes 1.
  • count[...]--: By subtracting for the second string, we “cancel out” the counts. If both strings have the same letters, every index in the array will eventually return to zero.
  • O(n) Time Complexity: This method is much faster than sorting both strings because it only looks at each character twice.

Exercise 10: Palindrome Checker (Two-Pointer Method)

Practice Problem: Use pointers to determine if a string is a palindrome. Print the word and a message indicating whether the word reads the same forward and backward.

Exercise Purpose: This demonstrates the efficiency of moving inward from both ends of a memory block simultaneously. It reinforces pointer arithmetic and conditional comparison.

Given Input: Word: "racecar"

Expected Output:

Testing word: racecar
Result: Is a Palindrome
+ Hint
  • Set a start pointer to the first character and an end pointer to the last.
  • While start is less than end, compare the characters they point to and move them toward the center.
+ Show Solution
#include <stdio.h>
#include <string.h>

int is_palindrome(char *str) {
    char *start = str;
    char *end = str + strlen(str) - 1;

    while (end > start) {
        if (*start != *end) return 0; // Mismatch
        start++; // Move forward
        end--;   // Move backward
    }
    return 1;
}

int main() {
    char word[] = "racecar";

    printf("Testing word: %s\n", word);
    
    if (is_palindrome(word)) printf("Result: Is a Palindrome\n");
    else printf("Result: Not a Palindrome\n");

    return 0;
}Code language: C++ (cpp)
Run

Explanation of Solution:

  • char *end = str + strlen(str) - 1: Calculates the address of the very last character before the null terminator.
  • while (end > start): The loop stops once the pointers meet or cross in the middle.
  • *start != *end: Dereferences both pointers to compare the characters. If “r” equals “r”, the pointers move; if they didn’t match, we would exit immediately.

Exercise 11: Word Counter

Practice Problem: Write a program that counts the number of words in a sentence, where words are separated by spaces. Print the sentence and the final word count.

Exercise Purpose: This teaches “State-based” logic. Instead of just counting spaces (which fails if there are multiple spaces between words), you track whether you are currently “inside” or “outside” of a word.

Given Input: Sentence: " Intermediate C is fun "

Expected Output:

Sentence: "  Intermediate C   is fun  "
Word Count: 4
+ Hint
  • Use a “state” variable (e.g., in_word = 0 or 1) to track whether you are currently inside a word or in the whitespace between words.
  • If you hit a non-space character and in_word is 0, you’ve found the start of a new word.
+ Show Solution
#include <stdio.h>
#include <ctype.h>

int count_words(char *s) {
    int count = 0, in_word = 0;

    while (*s) {
        if (isspace(*s)) {
            in_word = 0; // We are in whitespace
        } else if (in_word == 0) {
            in_word = 1; // Transition from space to letter
            count++;
        }
        s++;
    }
    return count;
}

int main() {
    char text[] = "  Intermediate C   is fun  ";
    
    printf("Sentence: \"%s\"\n", text);
    printf("Word Count: %d\n", count_words(text));

    return 0;
}Code language: C++ (cpp)
Run

Explanation:

  • isspace(*s): A helper function from <ctype.h> that checks for spaces, tabs, or newlines.
  • else if (in_word == 0): This condition only triggers when we hit the first letter of a word after coming from a space. This prevents multiple spaces from being counted as multiple words.
  • count++: We only increment when we “enter” a word, making the count accurate even if the sentence starts or ends with extra spaces.

Exercise 12: Student Database (Struct Arrays)

Practice Problem: Create a struct Student containing a name, roll number, and GPA. Initialize an array of 3 students, then write logic to find and print the student with the highest GPA.

Exercise Purpose: Structs allow you to group variables of different types into a single logical unit. This exercise teaches how to manage “collections of records,” which is the precursor to handling database rows or object-oriented programming.

Given Input:

  • Student 1: {"Alice", 101, 3.8}
  • Student 2: {"Bob", 102, 3.9}
  • Student 3: {"Charlie", 103, 3.5}

Expected Output:

Initial Data:
Alice (GPA: 3.8)
Bob (GPA: 3.9)
Charlie (GPA: 3.5)

Top Student: Bob with GPA: 3.9
+ Hint
  • Access struct members using the dot operator (.).
  • Use a loop to iterate through the array. Keep track of the index of the student with the highest GPA found so far, then use that index to print the final result.
+ Show Solution
#include <stdio.h>

struct Student {
    char name[50];
    int roll;
    float gpa;
};

int main() {
    struct Student class[3] = {
        {"Alice", 101, 3.8},
        {"Bob", 102, 3.9},
        {"Charlie", 103, 3.5}
    };

    int best_idx = 0;
    printf("Initial Data:\n");
    for(int i = 0; i < 3; i++) {
        printf("%s (GPA: %.1f)\n", class[i].name, class[i].gpa);
        if (class[i].gpa > class[best_idx].gpa) {
            best_idx = i;
        }
    }

    printf("\nTop Student: %s with GPA: %.1f\n", class[best_idx].name, class[best_idx].gpa);
    return 0;
}Code language: C++ (cpp)
Run

Explanation of Solution:

  • struct Student class[3]: This creates a contiguous block of memory large enough to hold three student “objects.”
  • class[i].gpa: The dot (.) operator is used to access specific members inside a struct instance.
  • best_idx = i: Instead of copying the whole struct, we just store the index of the “winner,” which is much more memory-efficient.

Exercise 13: Complex Number Arithmetic (Returning Structs)

Practice Problem: Define a struct for complex numbers ($a + bi$). Write a function that takes two complex number structs, adds them together, and returns a new struct containing the result.

Exercise Purpose: This demonstrates that functions can return entire structs by value. It teaches you how to design custom mathematical types and handle multi-variable data as a single return type.

Given Input:

  • Complex 1: 3.0 + 4.0i
  • Complex 2: 1.0 + 2.0i

Expected Output:

Adding: (3.0 + 4.0i) and (1.0 + 2.0i)
Result: 4.0 + 6.0i
+ Hint
  • Add the real parts and the imaginary parts separately inside the function.
  • Use typedef to make your code cleaner so you don’t have to type struct repeatedly.
+ Show Solution
#include <stdio.h>

typedef struct {
    float real;
    float imag;
} Complex;

Complex add(Complex n1, Complex n2) {
    Complex result;
    result.real = n1.real + n2.real;
    result.imag = n1.imag + n2.imag;
    return result;
}

int main() {
    Complex c1 = {3.0, 4.0}, c2 = {1.0, 2.0};
    
    printf("Adding: (%.1f + %.1fi) and (%.1f + %.1fi)\n", c1.real, c1.imag, c2.real, c2.imag);
    
    Complex sum = add(c1, c2);
    
    printf("Result: %.1f + %.1fi\n", sum.real, sum.imag);
    return 0;
}Code language: C++ (cpp)
Run

Explanation of Solution:

  • typedef struct: This creates an alias, allowing us to use Complex as a standalone type name just like int or float.
  • Complex result: A local struct is created inside the function to hold the calculated values.
  • return result: In C, when a struct is returned, the entire block of memory for that struct is copied back to the calling function.

Exercise 14: Time Difference (Normalization)

Practice Problem: Create a Time struct (hours, minutes, seconds). Write a function that calculates the absolute difference in total seconds between two time points.

Exercise Purpose: This exercise teaches the concept of Data Normalization. Instead of dealing with complex math (like “borrowing” minutes), you convert all data to a single base unit (seconds) to perform simple arithmetic.

Given Input:

  • Start: 10h 30m 00s
  • End: 12h 15m 30s

Expected Output:

Time 1: 10:30:00 (37800 sec)
Time 2: 12:15:30 (44130 sec)
Difference: 6330 seconds
+ Hint
  • Convert both times into total seconds since the start of the day (total = (h * 3600) + (m * 60) + s), then subtract them.
  • Use the abs() function from stdlib.h to ensure the result is positive.
+ Show Solution
#include <stdio.h>
#include <stdlib.h>

struct Time { int h, m, s; };

int to_seconds(struct Time t) {
    return (t.h * 3600) + (t.m * 60) + t.s;
}

int main() {
    struct Time start = {10, 30, 0};
    struct Time end = {12, 15, 30};

    int s1 = to_seconds(start);
    int s2 = to_seconds(end);
    
    printf("Time 1: %02d:%02d:%02d (%d sec)\n", start.h, start.m, start.s, s1);
    printf("Time 2: %02d:%02d:%02d (%d sec)\n", end.h, end.m, end.s, s2);
    
    int diff = abs(s2 - s1);
    printf("Difference: %d seconds\n", diff);
    
    return 0;
}Code language: C++ (cpp)
Run

Explanation of Solution:

  • to_seconds(struct Time t): This helper function “flattens” the struct into a single integer, making the comparison trivial.
  • %02d: This format specifier ensures that the time prints with leading zeros (e.g., “05” instead of “5”), making it look like a digital clock.
  • abs(s2 - s1): This ensures that regardless of which time is “start” or “end,” you get a positive duration.

Exercise 15: Nested Structs (Composition)

Practice Problem: Create a Point struct (x, y) and a Circle struct that contains a Point as its center and a radius. Calculate the area of the circle using the nested center coordinates.

Exercise Purpose: This introduces Composition, where you build a complex struct using other structs as building blocks. It is essential for organizing data in graphics, physics engines, and mapping software.

Given Input:

  • Center: x=0, y=0
  • Radius: 5.0

Expected Output:

Circle Center: (0.0, 0.0)
Circle Radius: 5.0
Calculated Area: 78.54
+ Hint

To access the x-coordinate from a circle variable c, use double-dot notation: c.center.x.

+ Show Solution
#include <stdio.h>

typedef struct { float x, y; } Point;
typedef struct { Point center; float radius; } Circle;

int main() {
    Circle myCircle = {{0.0, 0.0}, 5.0};
    
    float area = 3.14159 * myCircle.radius * myCircle.radius;
    
    printf("Circle Center: (%.1f, %.1f)\n", myCircle.center.x, myCircle.center.y);
    printf("Circle Radius: %.1f\n", myCircle.radius);
    printf("Calculated Area: %.2f\n", area);
    
    return 0;
}Code language: C++ (cpp)
Run

Explanation of Solution:

  • Point center: Inside the Circle definition, we don’t just store floats; we store an actual Point type.
  • {{0.0, 0.0}, 5.0}: The nested curly braces initialize the inner Point struct first, then the radius.
  • myCircle.center.x: The compiler first looks into myCircle to find the center struct, then looks into center to find the x float.

Exercise 16: Unions vs. Structs (Memory Sharing)

Practice Problem: Define a union Data that can hold an int or a float. Assign a value to the integer, print it, then assign a value to the float and observe how it affects the memory of the integer.

Exercise Purpose: Unions are a special data type where all members share the same memory location. This exercise teaches you about memory conservation and how C interprets the same raw bits as different data types.

Given Input:

  • Integer value: 10
  • Float value: 220.5

Expected Output:

Step 1: Assigned Int = 10
Memory state (as float): 0.000000

Step 2: Assigned Float = 220.5
Memory state (as int): 1130135552 (Now Overwritten!)
+ Hint
  • A union’s size is equal to its largest member.
  • Only one member can be used at a time; updating one will overwrite the other.
+ Show Solution
#include <stdio.h>

union Data {
    int i;
    float f;
};

int main() {
    union Data d;

    d.i = 10;
    printf("Step 1: Assigned Int = %d\n", d.i);
    printf("Memory state (as float): %f\n", d.f);

    d.f = 220.5;
    printf("\nStep 2: Assigned Float = %.1f\n", d.f);
    printf("Memory state (as int): %d (Now Overwritten!)\n", d.i);

    return 0;
}Code language: C++ (cpp)
Run

Explanation of Solution:

  • union Data d: This allocates only 4 bytes of memory (enough for either one int or one float).
  • Shared Memory: In Step 1, when we print d.f after assigning d.i, we see a strange decimal number. This is because the computer is trying to read the integer 10 bits as if they were a floating-point number.
  • Overwriting: In Step 2, assigning d.f physically overwrites the bytes that stored 10. This shows why you must track which member of a union is currently “active.”

Exercise 17: File Copy (Character Stream)

Practice Problem: Write a program that opens an existing text file (source.txt) and copies its entire content into a new file (destination.txt) character by character. Print a message to the console once the copy is complete.

Exercise Purpose: This exercise introduces the “Stream” concept. Instead of loading a massive file into RAM (which could crash your program), you process it one byte at a time. It also teaches the importance of checking for NULL file pointers.

Given Input:

  • Source File: source.txt (contains “Hello File I/O!”)
  • Destination File: destination.txt

Expected Output:

Starting copy process...
File copied successfully.
+ Hint
  • Use fgetc() to read a character and fputc() to write it.
  • The loop should continue until fgetc() returns the special constant EOF (End Of File).
+ Show Solution
#include <stdio.h>

int main() {
    FILE *src = fopen("source.txt", "r");
    FILE *dest = fopen("destination.txt", "w");

    if (src == NULL || dest == NULL) {
        printf("Error: Could not open files.\n");
        return 1;
    }

    char ch;
    printf("Starting copy process...\n");

    while ((ch = fgetc(src)) != EOF) {
        fputc(ch, dest);
    }

    printf("File copied successfully.\n");

    fclose(src);
    fclose(dest);
    return 0;
}Code language: C++ (cpp)
Run

Explanation of Solution:

  • fopen("source.txt", "r"): Opens the file in “read” mode. If the file doesn’t exist, it returns NULL.
  • while ((ch = fgetc(src)) != EOF): This is a classic C pattern. It reads a character, assigns it to ch, and checks if it’s the end of the file all in one line.
  • fclose(src): Closing files is mandatory. It releases the file handle back to the Operating System and ensures all data is physically written to the disk.

Exercise 18: Line Counter

Practice Problem: Write a program that opens a text file and counts how many lines it contains by detecting the newline character (\n). Print the total count to the console.

Exercise Purpose: This exercise teaches how to parse file data for specific patterns. It reinforces that text files are just sequences of characters, and “lines” are simply characters separated by \n.

Given Input:

  • A file data.txt with 3 lines of text.

Expected Output:

Total lines: 3
+ Hint
  • Every time fgetc() encounters \n, increment a counter.
  • Note that the last line might not have a \n, so you may need to account for that.
+ Show Solution
#include <stdio.h>

int main() {
    FILE *fptr = fopen("data.txt", "r");
    if (fptr == NULL) return 1;

    int count = 0;
    char ch;

    while ((ch = fgetc(fptr)) != EOF) {
        if (ch == '\n') {
            count++;
        }
    }

    printf("Total Newline Characters: %d\n", count);
    printf("Estimated Total Lines: %d\n", count + 1);

    fclose(fptr);
    return 0;
}Code language: C++ (cpp)
Run

Explanation of Solutions:

  • if (ch == '\n'): This identifies the specific character used by the system to move to a new line.
  • count + 1: We add one to the final count because if a file has one line of text but no newline at the end, the loop would count 0, even though a line of text exists.

Exercise 19: Binary Struct Storage

Practice Problem: Write a program to save an array of struct Student to a binary file and then read it back. You must demonstrate that the data persists by printing the original array, clearing the memory, and then printing the data retrieved from the file.

Exercise Purpose: Binary storage is different from text storage because it writes the exact bit-pattern of the RAM to the disk. This is much faster and more space-efficient than text files. This exercise teaches you how to handle “Deep Persistence,” which is essential for database engines and game save files.

Given Input:

  • struct Student { char name[20]; int age; };
  • Initial Data: {"Jessa", 20}, {"Kelly", 22}
  • File Name: students.bin

Expected Output:

--- Existing Data in Memory ---
Jessa, 20
Kelly, 22
Data written successfully in binary file

--- Data Read from Binary File ---
Jessa, 20
Kelly, 22
+ Hint
  • Use fwrite to send the raw memory of the struct array to the disk.
  • To read it back, use fread with the same struct size and count to ensure the bytes are mapped correctly back into the array.
+ Show Solution
#include <stdio.h>

struct Student { char name[20]; int age; };

int main() {
    struct Student out[2] = {{"User1", 20}, {"User2", 22}}, in[2];
    
    printf("--- Existing Data in Memory ---\n");
    for(int i=0; i<2; i++) printf("%s, %d\n", out[i].name, out[i].age);

    FILE *f = fopen("students.bin", "wb");
    if (!f) return 1;
    fwrite(out, sizeof(struct Student), 2, f);
    fclose(f);

    // Read back into 'in' array
    f = fopen("students.bin", "rb");
    if (!f) return 1;
    fread(in, sizeof(struct Student), 2, f);
    fclose(f);

    printf("\n--- Data Read from Binary File ---\n");
    for(int i=0; i<2; i++) printf("%s, %d\n", in[i].name, in[i].age);
    
    return 0;
}Code language: C++ (cpp)
Run

Explanation of Solution:

  • fwrite(out, sizeof(struct Student), 2, f): This line tells C to take the memory address of out, measure how big one Student is, and write 2 of them directly to the file stream.
  • Binary Mode ("wb", "rb"): The ‘b’ flag is crucial; it tells the OS not to translate newline characters, keeping the raw bit-pattern of the integers and strings intact.
  • fread(in, ...): This performs the exact reverse of fwrite. It fills the empty in array with the bytes found in the file. Since the struct definition is the same, the data fits back into the name and age fields perfectly.

Exercise 20: Singly Linked List (Insertion at Head)

Practice Problem: Implement a function to insert a new integer node at the beginning of a linked list. The program should display the list before and after the insertion to show how the “Head” pointer moves.

Exercise Purpose: Linked lists are the foundation of dynamic data structures. Unlike arrays, they don’t need contiguous memory. This exercise teaches “Pointer to a Pointer” logic, which is necessary because the function needs to modify the address stored in the head variable back in main.

Given Input:

  • Initial Node: 10
  • New value to insert: 20

Expected Output:

Head data: 20
+ Hint
  • To modify the head pointer from within a function, you must pass its address (&head).
  • Inside the function, dereference it once (*head_ref) to access the actual pointer.
+ Show Solution
#include <stdio.h>
#include <stdlib.h>

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

void push(struct Node **head_ref, int new_val) {
    struct Node *new_node = malloc(sizeof(struct Node));
    new_node->data = new_val;
    new_node->next = *head_ref; // New node points to old head
    *head_ref = new_node;       // Head now points to new node
}

void printList(struct Node *n) {
    while (n != NULL) { printf("%d -> ", n->data); n = n->next; }
    printf("NULL\n");
}

int main() {
    struct Node *head = malloc(sizeof(struct Node));
    head->data = 10; head->next = NULL;

    printf("Existing List: "); printList(head);
    push(&head, 20);
    printf("After Push(20): "); printList(head);
    return 0;
}Code language: C++ (cpp)
Run

Explanation:

  • malloc(sizeof(struct Node)): Allocates a block of memory on the heap large enough for one integer and one pointer.
  • new_node->next = *head_ref: This “wires” our new node to the existing list. The new node’s “next” arm now holds the address of the node that used to be first.
  • *head_ref = new_node: We update the head variable in main to the address of our new node. The new node is now officially the first element in the chain.

Exercise 21: Linked List Search

Practice Problem: Create a search function that traverses a linked list to find a specific integer. The program should print the full list first, then output whether the target value exists.

Exercise Purpose: Searching a linked list is a linear operation (O(n)). Because you can’t jump to a specific index like in an array, you must follow the “chain” of pointers. This teaches you how to traverse memory dynamically and reinforces the concept of the Null Terminator in data structures.

Given Input:

  • Linked List: 5 -> 15 -> 25 -> NULL
  • Search Key: 15

Expected Output:

Current List: 5 -> 15 -> 25 -> NULL
Search Result for 15: Found
+ Hint
  • Use a while loop to traverse the list: current = current->next.
  • Stop when current == NULL.
+ Show Solution
#include <stdio.h>
#include <stdlib.h>

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

int search(struct Node *head, int key) {
    struct Node *curr = head;
    while (curr != NULL) {
        if (curr->data == key) return 1;
        curr = curr->next;
    }
    return 0;
}

int main() {
    struct Node n3 = {25, NULL}, n2 = {15, &n3}, n1 = {5, &n2};
    
    printf("Current List: 5 -> 15 -> 25 -> NULL\n");
    int found = search(&n1, 15);
    printf("Search Result for 15: %s\n", found ? "Found" : "Not Found");
    return 0;
}Code language: C++ (cpp)
Run

Explanation:

  • struct Node *curr = head: We create a temporary pointer curr so we don’t “lose” the start of the list.
  • curr = curr->next: This is the engine of the search. It updates curr to hold the memory address stored in the next field of the current node, effectively jumping to the next block of data.
  • while (curr != NULL): This ensures we don’t try to access memory at address 0 (NULL), which would cause a segmentation fault.

Exercise 22: Stack (Array-based)

Practice Problem: Implement a Stack using a fixed array. Demonstrate the “Push” operation by showing the stack contents and the top index before and after adding a new value.

Exercise Purpose: A stack is a restricted data structure. It limits how you access data to ensure a specific order (LIFO). This is the logic used for the “Undo” feature in text editors or the “Back” button in web browsers. It teaches you how to manage data boundaries and the concept of Overflow and Underflow.

Given Input:

  • Array: int stack[5]
  • Current State: [50], top = 0
  • Value to Push: 100

Expected Output:

Before Push: top index = 0, value = 50
After Push: top index = 1, value = 100
+ Hint
  • top starts at -1.
  • push increments top before adding, pop returns the value and decrements top.
+ Show Solution
#include <stdio.h>

struct Stack { int items[5]; int top; };

void push(struct Stack *s, int val) {
    s->items[++(s->top)] = val;
}

int main() {
    struct Stack s = {{50}, 0};
    printf("Before Push: top index = %d, value = %d\n", s.top, s.items[s.top]);
    
    push(&s, 100);
    printf("After Push:  top index = %d, value = %d\n", s.top, s.items[s.top]);
    return 0;
}Code language: C++ (cpp)
Run

Explanation:

  • ++(s->top): This is a pre-increment. It changes the top value from 0 to 1 before it is used as an index.
  • s->items[...] = val: The value 100 is placed into items[1].
  • State Management: By keeping top inside the struct with the array, we ensure the index and the data are always passed together to any function, maintaining a clean organizational structure.

Exercise 23: Queue (Enqueue Operation)

Practice Problem: Implement a Queue using a linked list. Show the state of the front and rear pointers before and after an enqueue operation.

Exercise Purpose: Queues are used for scheduling tasks (like a printer queue). By using a linked list instead of an array, the queue can grow as large as the system’s memory allows. Having both a front and rear pointer allows both addition and removal to happen in O(1) time.

Given:

  • Initial Queue: [10] (Front and Rear both point to 10)
  • Value to Enqueue: 20

Expected Output:

Before: Front = 10, Rear = 10
After: Front = 10, Rear = 20
+ Hint
  • In a queue, you add to the rear.
  • You must update the next pointer of the current rear node to point to the new node, and then update the rear pointer itself.
+ Show Solution
#include <stdio.h>
#include <stdlib.h>

struct Node { int data; struct Node *next; };
struct Queue { struct Node *front, *rear; };

void enqueue(struct Queue *q, int val) {
    struct Node *temp = malloc(sizeof(struct Node));
    temp->data = val; temp->next = NULL;
    q->rear->next = temp;
    q->rear = temp;
}

int main() {
    struct Node *n1 = malloc(sizeof(struct Node));
    n1->data = 10; n1->next = NULL;
    struct Queue q = {n1, n1};

    printf("Before: Front = %d, Rear = %d\n", q.front->data, q.rear->data);
    enqueue(&q, 20);
    printf("After:  Front = %d, Rear = %d\n", q.front->data, q.rear->data);
    return 0;
}Code language: C++ (cpp)
Run

Explanation of Solution:

  • q->rear->next = temp: Since rear is pointing to the last node (10), we access that node’s next pointer and give it the address of our new node (20).
  • q->rear = temp: We move the rear label to the new node. Now, front still points to 10, but rear points to 20.
  • Result: This allows us to maintain a First-In-First-Out order, as we can always find the start via front and the end via rear instantly.

Exercise 24: Delete Node by Value

Practice Problem: Write a function to delete a specific node from a linked list. Print the list’s data before the operation and the remaining data after to prove the node was successfully unlinked and the memory was freed.

Exercise Purpose: Deletion is the trickiest part of linked list management. If you don’t “bridge” the previous node to the next node before freeing, you break the chain and lose the rest of your list. This exercise is vital for understanding Manual Memory Management and pointer redirection.

Given Input:

  • Initial List: 10 -> 20 -> 30 -> NULL
  • Target to Delete: 20

Expected Output:

Before Deletion: 10 -> 20 -> 30
After Deletion: 10 -> 30
+ Hint
  • You need to keep track of two nodes: the one you are checking (temp) and the one immediately before it (prev).
  • When you find the match, connect prev->next to temp->next to bypass the target.
+ Show Solution
#include <stdio.h>
#include <stdlib.h>

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

void deleteNode(struct Node **head, int key) {
    struct Node *temp = *head, *prev;
    while (temp != NULL && temp->data != key) {
        prev = temp; temp = temp->next;
    }
    prev->next = temp->next;
    free(temp);
}

int main() {
    struct Node *n3 = malloc(sizeof(struct Node)), *n2 = malloc(sizeof(struct Node)), *n1 = malloc(sizeof(struct Node));
    n1->data = 10; n1->next = n2; n2->data = 20; n2->next = n3; n3->data = 30; n3->next = NULL;

    printf("Before Deletion: 10 -> 20 -> 30\n");
    deleteNode(&n1, 20);
    printf("After Deletion:  10 -> %d\n", n1->next->data);
    return 0;
}Code language: C++ (cpp)
Run

Explanation of Solution:

  • while (temp != NULL && temp->data != key): This loop slides two pointers along the list. prev stays one step behind temp.
  • prev->next = temp->next: This is the “bridging” step. By taking the address of the node after 20 and giving it to the node before 20, the list now flows from 10 directly to 30.
  • free(temp): This is critical. It tells the operating system that the memory used by node 20 is now available for other uses, preventing a memory leak.

Exercise 25: Binary Search

Practice Problem: Implement a binary search on a sorted array. Print the low and high indices at every step to show how the algorithm cuts the search area in half until the target is found.

Exercise Purpose: Binary search is significantly faster (O(log n)) than linear search (O(n)). For an array of 1 million items, linear search might take 1,000,000 steps, while binary search takes only about 20. This exercise emphasizes why Sorted Data is so valuable in computer science.

Given Input:

  • Sorted Array: {10, 20, 30, 40, 50}
  • Target: 40

Expected Output:

Array: 10, 20, 30, 40, 50 | Target: 40
Searching between indices 0 and 4
Searching between indices 3 and 4
Found at index: 3
+ Hint
  • The middle point should be recalculated inside the while loop.
  • If the target is greater than the middle value, move the low index to mid + 1.
+ Show Solution
#include <stdio.h>

int binarySearch(int arr[], int n, int x) {
    int low = 0, high = n - 1;
    while (low <= high) {
        int mid = (low + high) / 2;
        printf("Searching between indices %d and %d\n", low, high);
        if (arr[mid] == x) return mid;
        if (arr[mid] < x) low = mid + 1;
        else high = mid - 1;
    }
    return -1;
}

int main() {
    int arr[] = {10, 20, 30, 40, 50};
    printf("Array: 10, 20, 30, 40, 50 | Target: 40\n");
    int res = binarySearch(arr, 5, 40);
    printf("Found at index: %d\n", res);
    return 0;
}Code language: C++ (cpp)
Run

Explanation of Solution:

  • mid = (low + high) / 2: This finds the center of the current search bounds.
  • low = mid + 1: If our target (40) is higher than our middle (30), we know the target cannot be in the lower half. We move our starting point to just past the middle.
  • Logarithmic Time: Each print statement shows the range shrinking. This O(log n) efficiency is why binary search is used for massive databases.

Exercise 26: Bubble Sort vs Selection Sort

Practice Problem: Perform a Selection Sort on an unsorted array. Print the array after the first full swap to demonstrate how the smallest element is “selected” and moved to the front.

Exercise Purpose: While both have O(n^2) time complexity, Selection Sort is generally better than Bubble Sort because it performs fewer writes to memory. This teaches you that “Efficiency” isn’t just about CPU cycles, but also about minimizing expensive memory operations.

Given Input: Array: {64, 25, 12, 22, 11}

Expected Output:

Original: 64, 25, 12, 22, 11
After 1st swap: 11, 25, 12, 22, 64
+ Hint

Use a secondary loop to find the index of the smallest number in the array. Swap that smallest number with the element at arr[0].

+ Show Solution
#include <stdio.h>

void selectionSort(int arr[], int n) {
    int min_idx = 0;
    for (int j = 1; j < n; j++) 
        if (arr[j] < arr[min_idx]) min_idx = j;
    
    int temp = arr[min_idx];
    arr[min_idx] = arr[0];
    arr[0] = temp;
}

int main() {
    int arr[] = {64, 25, 12, 22, 11};
    printf("Original: 64, 25, 12, 22, 11\n");
    selectionSort(arr, 5);
    printf("After 1st swap: %d, %d, %d, %d, %d\n", arr[0], arr[1], arr[2], arr[3], arr[4]);
    return 0;
}Code language: C++ (cpp)
Run

Explanation of Solution:

  • Outer Search: The code scans every element to find that 11 is the absolute minimum.
  • temp Swap: Standard three-step swap logic using a temporary variable to move 11 to the front and 64 to where 11 used to be.
  • Result: After the first pass, the first element of the array is guaranteed to be in its final sorted position.

Exercise 27: Bitwise Toggle

Practice Problem: Use bitwise operators to flip the status of a specific bit. Print the value of the number before and after the toggle to show how binary changes affect decimal values.

Exercise Purpose: Bitwise operations are the fastest operations a CPU can perform. Toggling bits is common in embedded systems (like turning a single LED on/off in a register) or in cryptography. This exercise helps you think about how data is actually represented at the hardware level.

Given Input:

  • Number: 5 (Binary 0101)
  • Bit Position to toggle: 1 (The second bit from the right)

Expected Output:

Initial Value: 5 (Binary 0101)
After Toggling Bit 1: 7 (Binary 0111)
+ Hint
  • Use the bitwise XOR (^) operator. XORing a bit with 1 will flip it, while XORing it with 0 will leave it alone.
  • Use 1 << pos to create the mask.
+ Show Solution
#include <stdio.h>

int main() {
    int num = 5;
    int pos = 1;
    printf("Initial Value: %d (Binary 0101)\n", num);
    
    num ^= (1 << pos);
    printf("After Toggling Bit 1: %d (Binary 0111)\n", num);
    return 0;
}Code language: C++ (cpp)
Run

Explanation of Solution:

  • 1 << pos: This takes the binary number 0001 and shifts the 1 to the left by 1 spot, resulting in 0010. This is our “mask.”
  • num ^= mask: The XOR operator compares 0101 and 0010. Since the second bit is different in each, it becomes a 1.
  • Decimal Change: In decimal, we changed the “2’s place” bit from 0 to 1, effectively adding 2 to the number (5 + 2 = 7).

Exercise 28: Decimal to Binary (Bitmasking)

Practice Problem: Create a function that displays the binary representation of a decimal number. Print the decimal value first, followed by its 8-bit binary string.

Exercise Purpose: This exercise bridges the gap between high-level numbers and low-level bits. It teaches you how to “Mask” data to extract specific information.

Given Input: Number: 10

Expected Output:

Decimal: 10
Binary (8-bit): 00001010
+ Hint

Use a loop that runs from 7 down to 0. In each iteration, shift the number right by i positions and use & 1 to “mask” and extract only the rightmost bit.

+ Show Solution
#include <stdio.h>

void printBinary(int n) {
    for (int i = 7; i >= 0; i--) {
        int bit = (n >> i) & 1;
        printf("%d", bit);
    }
    printf("\n");
}

int main() {
    int num = 10;
    printf("Decimal: %d\n", num);
    printf("Binary (8-bit): ");
    printBinary(num);
    return 0;
}Code language: C++ (cpp)
Run

Explanation of Solution:

  • n >> i: This moves the bit we want to inspect all the way to the “ones” column.
  • & 1: The AND operator with 1 acts like a filter. It ignores all other bits and only tells us if the current rightmost bit is a 1 or 0.
  • Reversed Loop: We start the loop at 7 and go down to 0 because we read binary strings from left (most significant) to right (least significant).

Exercise 29: Sorting Structure Data

Practice Problem: Define an Employee struct containing a name and a salary. Create an array of 3 employees and write a program to sort them in ascending order based on their salary. Print the list before and after sorting.

Exercise Purpose: In real-world applications, data is rarely just a list of numbers; it is usually a collection of related attributes (structs). This exercise teaches you how to apply sorting logic to specific fields within a complex data type while keeping the entire record intact.

Given Input:

  • Employee 1: {"John", 50000}
  • Employee 2: {"Alice", 75000}
  • Employee 3: {"Bob", 45000}

Expected Output:

Before Sorting (Salary):
John: 50000
Alice: 75000
Bob: 45000

After Sorting (Salary):
Bob: 45000
John: 50000
Alice: 75000
+ Hint
  • Use the Bubble Sort algorithm.
  • When comparing, only check the salary field, but when swapping, swap the entire Employee struct to ensure the name stays with the correct salary.
+ Show Solution
#include <stdio.h>
#include <string.h>

struct Employee {
    char name[20];
    float salary;
};

void sortEmployees(struct Employee emp[], int n) {
    for (int i = 0; i < n - 1; i++) {
        for (int j = 0; j < n - i - 1; j++) {
            if (emp[j].salary > emp[j + 1].salary) {
                struct Employee temp = emp[j];
                emp[j] = emp[j + 1];
                emp[j + 1] = temp;
            }
        }
    }
}

int main() {
    struct Employee staff[3] = {{"John", 50000}, {"Alice", 75000}, {"Bob", 45000}};

    printf("Before Sorting (Salary):\n");
    for(int i=0; i<3; i++) printf("%s: %.0f\n", staff[i].name, staff[i].salary);

    sortEmployees(staff, 3);

    printf("\nAfter Sorting (Salary):\n");
    for(int i=0; i<3; i++) printf("%s: %.0f\n", staff[i].name, staff[i].salary);
    
    return 0;
}Code language: C++ (cpp)
Run

Explanation of Solution:

  • if (emp[j].salary > emp[j + 1].salary): This targets only the numerical value for comparison.
  • struct Employee temp = emp[j]: By using the struct type for the temp variable, C automatically copies all internal fields (both name and salary) during the swap.
  • Bubble Sort Logic: The nested loops ensure that the “heaviest” salary floats to the end of the array in each pass.

Exercise 30: Second Largest Element in an Array

Practice Problem: Write a program to find the second largest integer in an array of size 5. The program must find this in a single pass (O(n)) without sorting the array.

Exercise Purpose: Sorting an array to find the second largest is O(n log n). This exercise teaches you how to optimize performance by maintaining two state variables (max and secondMax) simultaneously during a single traversal.

Given Input: Array: {12, 35, 1, 10, 34}

Expected Output:

Array: 12, 35, 1, 10, 34
Largest: 35 | Second Largest: 34
+ Hint
  • Initialize max and secondMax to a very small number.
  • If the current element is larger than max, update secondMax to be the old max, and then update max to the current element.
+ Show Solution
#include <stdio.h>
#include <limits.h>

int main() {
    int arr[5] = {12, 35, 1, 10, 34};
    int max = INT_MIN, secondMax = INT_MIN;

    printf("Array: 12, 35, 1, 10, 34\n");

    for (int i = 0; i < 5; i++) {
        if (arr[i] > max) {
            secondMax = max;
            max = arr[i];
        } else if (arr[i] > secondMax && arr[i] != max) {
            secondMax = arr[i];
        }
    }

    printf("Largest: %d | Second Largest: %d\n", max, secondMax);
    return 0;
}Code language: C++ (cpp)
Run

Explanation of Solution:

  • secondMax = max: Before we replace the “king” (max), we demote the current king to the “vice-king” (secondMax).
  • arr[i] != max: This check ensures that if the largest number appears twice in the array, the secondMax still looks for a value strictly smaller than the absolute max.
  • INT_MIN: This macro from <limits.h> represents the smallest possible integer, ensuring our comparisons work even if the array contains negative numbers.

Exercise 31: Merge Two Sorted Arrays

Practice Problem: Given two arrays that are already sorted, merge them into a third array such that the third array is also sorted. Print the two source arrays and the final merged result.

Exercise Purpose: This is the core logic behind the “Merge Sort” algorithm. It teaches you how to manage three different pointers simultaneously and how to handle “leftover” elements when one array is shorter than the other.

Given Input:

  • Array 1: {1, 3, 5}
  • Array 2: {2, 4, 6}

Expected Output:

Array 1: 1, 3, 5 | Array 2: 2, 4, 6
Merged: 1 2 3 4 5 6
+ Hint

Compare the current elements of both arrays. Pick the smaller one, put it in the third array, and move the pointer for that array forward.

+ Show Solution
#include <stdio.h>

int main() {
    int a[] = {1, 3, 5}, b[] = {2, 4, 6};
    int res[6], i = 0, j = 0, k = 0;

    printf("Array 1: 1, 3, 5 | Array 2: 2, 4, 6\n");

    while (i < 3 && j < 3) {
        if (a[i] < b[j]) res[k++] = a[i++];
        else res[k++] = b[j++];
    }

    while (i < 3) res[k++] = a[i++]; // Copy leftovers
    while (j < 3) res[k++] = b[j++];

    printf("Merged: ");
    for (int x = 0; x < 6; x++) printf("%d ", res[x]);
    return 0;
}Code language: C++ (cpp)
Run

Explanation of Solution:

  • while (i < 3 && j < 3): This loop runs until one of the two source arrays is completely exhausted.
  • res[k++] = a[i++]: This is a compact way to assign the value and then increment both the result index and the source index.
  • Leftover Loops: The final two while loops ensure that if one array (like {1, 2}) finishes but the other (like {10, 11, 12}) still has elements, those remaining elements are appended to the end.

Exercise 32: Remove Duplicate Elements

Practice Problem: Write a program to remove duplicate integers from a sorted array. The program should shift elements to the left to “delete” duplicates and return the new length of the array.

Exercise Purpose: This exercise teaches in-place array manipulation. Instead of creating a second “clean” array, you learn how to use a “slow-moving” pointer to overwrite the duplicates with unique values.

Given Input: Sorted Array: {1, 2, 2, 3, 4, 4, 5}

Expected Output:

Original: 1, 2, 2, 3, 4, 4, 5
Unique: 1 2 3 4 5
+ Hint
  • Keep a pointer j for unique elements.
  • Iterate through the array with i, and every time arr[i] is different from arr[j], increment j and copy arr[i] to arr[j].
+ Show Solution
#include <stdio.h>

int main() {
    int arr[] = {1, 2, 2, 3, 4, 4, 5};
    int n = 7, j = 0;

    printf("Original: 1, 2, 2, 3, 4, 4, 5\n");

    for (int i = 0; i < n - 1; i++) {
        if (arr[i] != arr[i + 1]) {
            arr[j++] = arr[i];
        }
    }
    arr[j++] = arr[n - 1]; // Handle the last element

    printf("Unique:   ");
    for (int i = 0; i < j; i++) printf("%d ", arr[i]);
    return 0;
}Code language: C++ (cpp)
Run

Explanation of Solution:

  • In-place logic: We are using the same arr to store the unique values, effectively overwriting the duplicates as we find them.
  • arr[i] != arr[i + 1]: Since the array is sorted, duplicates are guaranteed to be neighbors. If the next element is different, the current one is the last occurrence of that specific value.
  • Final Assignment: The loop stops before the last element, so we manually add arr[n-1] to the unique list.

Exercise 33: Most Frequent Character

Practice Problem: Write a program to find which character appears most often in a string. Print the string and the winning character with its count.

Exercise Purpose: This reinforces the “Frequency Array” or “Hashing” concept. It maps characters (keys) to their counts (values), which is an essential pattern for solving string-based optimization problems.

Given Input: String: "success"

Expected Output:

String: success
Most Frequent: 's' (appears 3 times)
+ Hint
  • Create an integer array of size 256.
  • Use each character’s ASCII value as an index to increment the count. Then, find the index with the highest value.
+ Show Solution
#include <stdio.h>
#include <string.h>

int main() {
    char str[] = "success";
    int freq[256] = {0}, maxCount = 0;
    char result;

    for (int i = 0; str[i] != '\0'; i++) {
        freq[(unsigned char)str[i]]++;
    }

    for (int i = 0; i < 256; i++) {
        if (freq[i] > maxCount) {
            maxCount = freq[i];
            result = (char)i;
        }
    }

    printf("String: %s\n", str);
    printf("Most Frequent: '%c' (appears %d times)\n", result, maxCount);
    return 0;
}Code language: C++ (cpp)
Run

Explanation:

  • freq[(unsigned char)str[i]]++: We cast to unsigned char to ensure characters outside the standard 127 ASCII range (like accented letters) don’t produce negative indices.
  • Two-Step Process: First, we build the “map” of counts. Second, we scan that map to find the “peak.” This is a highly efficient $O(n)$ solution.

Exercise 34: Fibonacci Series (Recursion)

Practice Problem: Generate the first 10 terms of the Fibonacci series using a recursive function. Print the series in a single line.

The Fibonacci sequence formula is a recursive rule: F(n) = F(n-1) + F(n-2), meaning each number is the sum of the two preceding ones, starting with base cases F(0) = 0 and F(1) = 1 (0, 1, 1, 2, 3, 5, 8…).

Exercise Purpose: This is the classic introduction to Recursion. It teaches the two requirements for a recursive function: the Base Case (to stop the loop) and the Recursive Step (the function calling itself).

Given Input: Terms to generate: 10

Expected Output:

Fibonacci Series (10 terms): 0 1 1 2 3 5 8 13 21 34
+ Hint

+ Show Solution
#include <stdio.h>

int fibonacci(int n) {
    if (n <= 1) return n; // Base case
    return fibonacci(n - 1) + fibonacci(n - 2); // Recursive call
}

int main() {
    int terms = 10;
    printf("Fibonacci Series (10 terms): ");
    for (int i = 0; i < terms; i++) {
        printf("%d ", fibonacci(i));
    }
    printf("\n");
    return 0;
}Code language: C++ (cpp)
Run

Explanation of Solution:

  • Base Case (n <= 1): Without this, the function would call itself infinitely until the program crashed.
  • Recursive Step: The function calculates a value by asking itself for the two previous values. This creates a “tree” of calls that eventually reaches the base cases.
  • Cost of Recursion: While elegant, this specific recursive method is actually slow for large numbers (O(2^n)) because it recalculates the same values many times. It serves as a great lesson on when to use recursion vs. iteration.

Exercise 35: Intersection of Two Arrays

Practice Problem: Write a program that finds the common elements between two integer arrays. The program should store these common elements in a third array and print the result.

Exercise Purpose: This problem introduces basic set theory in programming. It teaches you how to perform “membership testing” by comparing elements of one dataset against another, a fundamental task in database joins and data filtering.

Given Input:

  • Array 1: {1, 2, 3, 4, 5}
  • Array 2: {3, 4, 5, 6, 7}

Expected Output:

Array 1: {1, 2, 3, 4, 5}
Array 2: {3, 4, 5, 6, 7}
Intersection: 3 4 5
+ Hint
  • Use a nested loop to compare each element of the first array with every element of the second.
  • If a match is found, ensure you haven’t already added it to your “intersection” list to handle potential duplicates
+ Show Solution
#include <stdio.h>

int main() {
    int a1[] = {1, 2, 3, 4, 5}, a2[] = {3, 4, 5, 6, 7};
    int intersect[5], k = 0;

    printf("Array 1: {1, 2, 3, 4, 5}\nArray 2: {3, 4, 5, 6, 7}\n");

    for (int i = 0; i < 5; i++) {
        for (int j = 0; j < 5; j++) {
            if (a1[i] == a2[j]) {
                intersect[k++] = a1[i];
                break; // Move to next element in a1
            }
        }
    }

    printf("Intersection: ");
    for (int i = 0; i < k; i++) printf("%d ", intersect[i]);
    return 0;
}Code language: C++ (cpp)
Run

Explanation of Solution:

  • Nested Loops: The outer loop picks a number from a1, and the inner loop scans a2 to see if that number exists there.
  • break Statement: Once a match is found, we don’t need to keep checking the rest of a2 for that specific number, saving processing time.
  • Result Storage: The k variable acts as a cursor for the intersect array, only incrementing when a common element is actually found.

Exercise 36: Move Zeros to the End

Practice Problem: Write a program that takes an array containing some zeros and moves all those zeros to the end of the array without changing the relative order of the non-zero elements.

Exercise Purpose: This is a classic “array partitioning” problem. It teaches you how to maintain two pointers. one for the current scan and one for the “write position” allowing you to modify the array in a single pass (O(n)).

Given Input: Array: {0, 1, 0, 3, 12}

Expected Output:

Original: {0, 1, 0, 3, 12}
Modified: 1 3 12 0 0
+ Hint
  • Iterate through the array. If the current element is not zero, put it at the “non-zero” pointer position and increment that pointer.
  • After the loop, fill the remaining positions with zeros.
+ Show Solution
#include <stdio.h>

int main() {
    int arr[] = {0, 1, 0, 3, 12};
    int n = 5, count = 0; // count tracks position for non-zero elements

    printf("Original: {0, 1, 0, 3, 12}\n");

    for (int i = 0; i < n; i++) {
        if (arr[i] != 0) {
            arr[count++] = arr[i];
        }
    }

    // Fill the rest with zeros
    while (count < n) {
        arr[count++] = 0;
    }

    printf("Modified: ");
    for (int i = 0; i < n; i++) printf("%d ", arr[i]);
    return 0;
}Code language: C++ (cpp)
Run

Explanation of Solution:

  • First Pass: The loop effectively “squeezes” all non-zero numbers to the front of the array in their original order.
  • count++: The count variable always points to where the next non-zero number should go.
  • Final Padding: The while loop starts from wherever count left off and fills the rest of the array slots with zero.

Exercise 37: Sort Strings Alphabetically (Pointers)

Practice Problem: Create an array of strings (names) and sort them in alphabetical order using pointers and the strcmp function.

Exercise Purpose: This exercise teaches you how to handle “Arrays of Pointers.” Instead of moving large chunks of string data in memory, you simply swap the pointers (memory addresses), which is much faster.

Given Input: Strings: "Zebra", "Apple", "Mango"

Expected Output:

Before: Zebra, Apple, Mango
After: Apple, Mango, Zebra
+ Hint

Use an array of pointers char *arr[]. Use strcmp(s1, s2) which returns a positive value if string 1 is alphabetically “greater” than string 2.

+ Show Solution
#include <stdio.h>
#include <string.h>

void sortStrings(char *arr[], int n) {
    for (int i = 0; i < n - 1; i++) {
        for (int j = 0; j < n - i - 1; j++) {
            if (strcmp(arr[j], arr[j + 1]) > 0) {
                char *temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
}

int main() {
    char *fruits[] = {"Zebra", "Apple", "Mango"};

    printf("Before: Zebra, Apple, Mango\n");
    sortStrings(fruits, 3);
    
    printf("After:  %s, %s, %s\n", fruits[0], fruits[1], fruits[2]);
    return 0;
}Code language: C++ (cpp)
Run

Explanation of Solution:

  • char *temp: Notice we are swapping char* (pointers), not the actual characters. We are just rearranging the “labels” pointing to the words.
  • strcmp: This function compares ASCII values character by character. “Apple” is less than “Zebra” because ‘A’ (65) is less than ‘Z’ (90).
  • Efficiency: Swapping pointers takes the same amount of time regardless of how long the strings are (e.g., swapping pointers to “A” vs “Antidisestablishmentarianism”).

Exercise 38: Longest Word in a Sentence

Practice Problem: Write a program that identifies the longest word in a given sentence. If multiple words have the same maximum length, return the first one found.

Exercise Purpose: This combines string traversal with logic for tracking “local maxima.” It teaches how to use pointers to mark the start of a substring and calculate its length on the fly.

Given Input: Sentence: "I am learning Programming"

Expected Output:

Sentence: I am learning Programming
Longest Word: Programming
+ Hint

+ Show Solution
#include <stdio.h>
#include <string.h>
#include <ctype.h>

int main() {
    char str[] = "I am learning Programming";
    int maxLen = 0, currLen = 0, startIdx = 0, maxStart = 0;

    for (int i = 0; i <= strlen(str); i++) {
        if (isspace(str[i]) || str[i] == '\0') {
            if (currLen > maxLen) {
                maxLen = currLen;
                maxStart = startIdx;
            }
            currLen = 0;
            startIdx = i + 1;
        } else {
            currLen++;
        }
    }

    printf("Sentence: %s\n", str);
    printf("Longest Word: ");
    for (int i = maxStart; i < maxStart + maxLen; i++) printf("%c", str[i]);
    return 0;
}Code language: C++ (cpp)
Run

Explanation of Solution:

  • isspace or \0: This identifies the boundary of a word. When a boundary is hit, the program evaluates the word just finished.
  • maxStart = startIdx: We store the index where the longest word began so we can print it later.
  • currLen: This resets to 0 every time a space is encountered, beginning the count for a new word.

Exercise 39: Pascal’s Triangle

Practice Problem: Write a program to generate the first 5 rows of Pascal’s Triangle. Each number is the sum of the two numbers directly above it.

Exercise Purpose: This exercise teaches 2D array logic and combinatorial math. It is excellent for understanding how to use values from a previous state (the previous row) to calculate the current state.

Given Input: Rows: 5

Expected Output:

Pascal's Triangle (5 rows):
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
+ Hint
  • Use a 2D array where arr[i][j] = arr[i-1][j-1] + arr[i-1][j].
  • The edges of the triangle (where j=0 or j=i) are always 1.
+ Show Solution
#include <stdio.h>

int main() {
    int rows = 5, arr[5][5];

    printf("Pascal's Triangle (5 rows):\n");

    for (int i = 0; i < rows; i++) {
        // Print spaces for triangle shape
        for (int s = 0; s < rows - i; s++) printf(" ");

        for (int j = 0; j <= i; j++) {
            if (j == 0 || j == i)
                arr[i][j] = 1;
            else
                arr[i][j] = arr[i - 1][j - 1] + arr[i - 1][j];
            
            printf("%d ", arr[i][j]);
        }
        printf("\n");
    }
    return 0;
}Code language: C++ (cpp)
Run

Explanation of Solution:

  • arr[i][j] = arr[i-1][j-1] + arr[i-1][j]: This is the core Pascal logic. It looks at the row above (i-1) and adds the elements from the left and right positions.
  • Inner Loop j <= i: In row 0, we print 1 item; in row 1, we print 2 items, and so on. This creates the triangular structure.
  • Formatting: The rows - i loop prints leading spaces so the output looks like a centered pyramid rather than a left-aligned block.

Exercise 40: Hollow Triangle Pattern

Practice Problem: Write a program to print a hollow triangle pattern using stars (*). The triangle should have a solid base, but the interior should be empty.

Exercise Purpose: This exercise sharpens your logic regarding nested loops and conditional boundaries. You learn how to identify specific coordinates in a grid (the edges) while ignoring the “inner” coordinates.

Given Input: Height: 5 rows

Expected Output:

Generating Hollow Triangle (Height 5):
*
* *
* *
* *
*********
+ Hint

A star should only be printed if it is on the first column of a row, the last column of a row (j == i), or if it is the very last row. Otherwise, print a space.

+ Show Solution
#include <stdio.h>

int main() {
    int rows = 5;
    printf("Generating Hollow Triangle (Height %d):\n", rows);

    for (int i = 1; i <= rows; i++) {
        // Print leading spaces for alignment
        for (int j = i; j < rows; j++) printf(" ");

        for (int j = 1; j <= (2 * i - 1); j++) {
            // Print star only at boundaries or the last row
            if (j == 1 || j == (2 * i - 1) || i == rows) {
                printf("*");
            } else {
                printf(" ");
            }
        }
        printf("\n");
    }
    return 0;
}Code language: C++ (cpp)
Run

Explanation of Solution:

  • j == 1 || j == (2 * i - 1): This checks if the current position is the very first or very last star of the current row.
  • i == rows: This condition ensures that the bottom row is printed as a solid line of stars.
  • 2 * i - 1: This formula determines the total width of the triangle at any given row $i$ to keep it centered.

Exercise 41: Solid Diamond Pattern

Practice Problem: Write a program to print a solid diamond pattern. The program should handle the “growth” phase (top triangle) and the “shrink” phase (inverted triangle).

Exercise Purpose: This teaches you how to manage multiple loop structures that depend on the same variable. It is a great exercise for understanding symmetry in algorithms.

Given Input: Maximum Width: 9 stars (at the center)

Expected Output:

Generating Solid Diamond (Max Width 9):
*
***
*****
*******
*********
*******
*****
***
*
+ Hint
  • Split the problem into two parts.
  • The first part increases stars from 1 to N
  • The second part decreases stars from N-1 back to 1.
+ Show Solution
#include <stdio.h>

int main() {
    int n = 5; // Half of diamond height
    printf("Generating Solid Diamond (Max Width %d):\n", 2*n-1);

    // Top half
    for (int i = 1; i <= n; i++) {
        for (int j = i; j < n; j++) printf(" ");
        for (int j = 1; j <= (2 * i - 1); j++) printf("*");
        printf("\n");
    }
    // Bottom half
    for (int i = n - 1; i >= 1; i--) {
        for (int j = n; j > i; j--) printf(" ");
        for (int j = 1; j <= (2 * i - 1); j++) printf("*");
        printf("\n");
    }
    return 0;
}Code language: C++ (cpp)
Run

Explanation of Solution:

  • Two Main Loops: The first for loop handles the upper triangle. The second for loop is mirrored but starts from n-1 to avoid repeating the center line.
  • 2 * i - 1: This ensures an odd number of stars in every row, which is required for a centered, symmetrical diamond.

Exercise 42: Alternating Positive and Negative Order

Practice Problem: Given an array containing both positive and negative integers, rearrange them so that they alternate (e.g., Positive, Negative, Positive…). Do not use an extra array.

Exercise Purpose: This is an “In-Place” rearrangement problem. It teaches you how to swap elements based on index parity (even/odd) and search for the next available element that fits a specific criterion.

Given Input: Array: {-1, 2, -3, 4, 5, 6, -7, 8, 9}

Expected Output:

Original: -1, 2, -3, 4, 5, 6, -7, 8, 9
Rearranged: 2 -1 4 -3 5 -7 6 8 9
+ Hint

Iterate through the array. If an even index has a negative number, or an odd index has a positive number, find the next number of the opposite sign and swap them.

+ Show Solution
#include <stdio.h>

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

int main() {
    int arr[] = {-1, 2, -3, 4, 5, 6, -7, 8, 9};
    int n = 9;

    printf("Original: -1, 2, -3, 4, 5, 6, -7, 8, 9\n");

    for (int i = 0; i < n; i++) {
        // If index is even, we want a positive number
        if (i % 2 == 0 && arr[i] < 0) {
            for (int j = i + 1; j < n; j++) {
                if (arr[j] >= 0) { swap(&arr[i], &arr[j]); break; }
            }
        }
        // If index is odd, we want a negative number
        else if (i % 2 != 0 && arr[i] >= 0) {
            for (int j = i + 1; j < n; j++) {
                if (arr[j] < 0) { swap(&arr[i], &arr[j]); break; }
            }
        }
    }

    printf("Rearranged: ");
    for (int i = 0; i < n; i++) printf("%d ", arr[i]);
    return 0;
}Code language: C++ (cpp)
Run

Explanation of Solution:

  • i % 2 == 0: We use the modulo operator to determine if the current “slot” should be positive or negative.
  • Nested Search: When a “wrong” sign is found at index i, the inner loop searches the rest of the array for a “correct” sign to swap in.
  • In-Place: No extra memory is used; we only shift existing data within the array bounds.

Exercise 43: File Stats (Lines, Words, Characters)

Practice Problem: Write a program to read a text file and count three things: the total number of characters, the total number of words, and the total number of lines.

Exercise Purpose: This exercise is essentially building a simplified version of the Unix wc (word count) utility. It teaches state management within file streams.

Given Input:

  • File stats.txt with: Hello World Learning C is fun

Expected Output:

Characters: 29 | Words: 6 | Lines: 1
+ Hint
  • Characters are counted on every fgetc. Lines are counted on \n.
  • Words are counted when you transition from a space to a non-space character.
+ Show Solution
#include <stdio.h>
#include <ctype.h>

int main() {
    FILE *fptr = fopen("stats.txt", "r");
    if (!fptr) return 1;

    int chars = 0, words = 0, lines = 0, in_word = 0;
    char ch;

    while ((ch = fgetc(fptr)) != EOF) {
        chars++;
        if (ch == '\n') lines++;
        
        if (isspace(ch)) {
            in_word = 0;
        } else if (in_word == 0) {
            in_word = 1;
            words++;
        }
    }

    printf("Characters: %d | Words: %d | Lines: %d\n", chars, words, lines + 1);
    fclose(fptr);
    return 0;
}Code language: C++ (cpp)
Run

Explanation of Solution:

  • in_word Flag: This prevents double-counting words when there are multiple spaces. We only increment words when we hit the start of a word.
  • lines + 1: Since the last line might not end with a \n, we add 1 to the final line count to account for the content on the final line.

Exercise 44: Middle Element of a Linked List

Practice Problem: Implement a function that finds the middle element of a linked list in a single traversal using the “Slow and Fast Pointer” technique. Note: You need to create struct Node with int data and pointer to next node.

Exercise Purpose: This is a classic coding interview problem. It demonstrates how multiple pointers moving at different speeds can solve geometric problems in a data structure without knowing its total size.

Given Input:

  • List: 1 -> 2 -> 3 -> 4 -> 5 -> NULL

Expected Output:

List: 1 -> 2 -> 3 -> 4 -> 5
Middle Element: 3
+ Hint
  • Use two pointers, slow and fast.
  • For every one step the slow pointer takes, the fast pointer takes two.
  • When fast reaches the end, slow will be exactly at the middle.
+ Show Solution
#include <stdio.h>
#include <stdlib.h>

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

void findMiddle(struct Node *head) {
    struct Node *slow = head, *fast = head;

    while (fast != NULL && fast->next != NULL) {
        fast = fast->next->next; // Move 2 steps
        slow = slow->next;       // Move 1 step
    }
    printf("Middle Element: %d\n", slow->data);
}

int main() {
    struct Node *n5 = malloc(sizeof(struct Node)), *n4 = malloc(sizeof(struct Node)), 
                *n3 = malloc(sizeof(struct Node)), *n2 = malloc(sizeof(struct Node)), 
                *n1 = malloc(sizeof(struct Node));

    n1->data = 1; n1->next = n2; n2->data = 2; n2->next = n3; 
    n3->data = 3; n3->next = n4; n4->data = 4; n4->next = n5; 
    n5->data = 5; n5->next = NULL;

    printf("List: 1 -> 2 -> 3 -> 4 -> 5\n");
    findMiddle(n1);
    return 0;
}Code language: C++ (cpp)
Run

Explanation of Solution:

  • fast = fast->next->next: Because the fast pointer moves double the speed, it reaches the “finish line” twice as fast as the slow pointer.
  • Relative Distance: Because the finish line for fast is the end of the list ($L$), the slow pointer will have covered exactly $L/2$ distance.
  • Loop Condition: We check both fast and fast->next to ensure we don’t crash when jumping two nodes at a time in both even and odd-lengthed lists.

Filed Under: C Programming Exercises

Did you find this page helpful? Let others know about it. Sharing helps me continue to create free Python resources.

TweetF  sharein  shareP  Pin

About Vishal

I’m Vishal Hule, the Founder of PYnative.com. As a Python developer, I enjoy assisting students, developers, and learners. Follow me on Twitter.

Related Tutorial Topics:

C Programming Exercises

All Coding Exercises:

C Exercises
C++ Exercises
Python Exercises

Python Exercises and Quizzes

Free coding exercises and quizzes cover Python basics, data structure, data analytics, and more.

  • 15+ Topic-specific Exercises and Quizzes
  • Each Exercise contains 25+ questions
  • Each Quiz contains 25 MCQ
Exercises
Quizzes

Leave a Reply Cancel reply

your email address will NOT be published. all comments are moderated according to our comment policy.

Use <pre> tag for posting code. E.g. <pre> Your entire code </pre>

In: C Programming Exercises
TweetF  sharein  shareP  Pin

  C Exercises

  • All C Exercises
  • C Exercise for Beginners
  • Intermediate C Exercises
  • C Variable and Data Type Exercise
  • C Loops Exercise
  • C Functions Exercise
  • C Arrays Exercise
  • C String Exercise
  • C Pointers Exercise
  • C File Handling Exercise
  • C Structures and Unions Exercise

All Coding Exercises

C Exercises C++ Exercises Python Exercises

About PYnative

PYnative.com is for Python lovers. Here, You can get Tutorials, Exercises, and Quizzes to practice and improve your Python skills.

Follow Us

To get New Python Tutorials, Exercises, and Quizzes

  • Twitter
  • Facebook
  • Sitemap

Explore Python

  • Learn Python
  • Python Basics
  • Python Databases
  • Python Exercises
  • Python Quizzes
  • Online Python Code Editor
  • Python Tricks

Coding Exercises

  • C Exercises
  • C++ Exercises
  • Python Exercises

Legal Stuff

  • About Us
  • Contact Us

We use cookies to improve your experience. While using PYnative, you agree to have read and accepted our:

  • Terms Of Use
  • Privacy Policy
  • Cookie Policy

Copyright © 2018–2026 pynative.com