Codcups
Progress: 0%

01. 🚀 C Programming Fundamentals

C is a powerful general-purpose programming language that has influenced many modern languages. It's known for its efficiency, portability, and low-level access to memory.

Why Learn C?

C is the foundation of modern computing. Operating systems (Linux, Windows), databases, embedded systems, and game engines are built with C. Learning C helps you understand how computers really work at a fundamental level.

Basic Structure of a C Program

  • Preprocessor Directives: #include, #define
  • Main Function: Program entry point
  • Variable Declarations: Memory allocation
  • Statements & Expressions: Program logic
  • Comments: // for single-line, /* */ for multi-line
Key Concept: C is a compiled language. Source code (.c files) is converted to machine code by a compiler before execution.

Example: Your First C Program

/* This is a multi-line comment
   First C Program - Hello World */

// Single line comment
#include <stdio.h>  // Standard Input Output header

int main() {
    // Print Hello World to console
    printf("Hello, World!\n");
    
    // Return 0 indicates successful execution
    return 0;
}
Note: Every C program must have a main() function. The \n is the newline character. Always include stdio.h for input/output operations.
Try It Yourself
#include <stdio.h>

int main() {
    printf("Welcome to C Programming!\n");
    printf("Let's learn together.\n");
    return 0;
}

02. 📊 Data Types & Variables

Data types define the type of data that can be stored in variables. C provides basic built-in data types that can be combined to form complex data structures.

Basic Data Types

  • int: Integer numbers (2 or 4 bytes)
  • float: Single precision floating point (4 bytes)
  • double: Double precision floating point (8 bytes)
  • char: Single character (1 byte)
  • void: No value/type

Memory Representation

int age = 25;
25
(4 bytes)
float price = 19.99;
19.99
(4 bytes)
char grade = 'A';
'A'
(1 byte)

Example: Variables and Data Types

#include <stdio.h>

int main() {
    // Integer types
    int age = 25;
    short smallNumber = 100;
    long bigNumber = 1000000L;
    unsigned int positiveOnly = 40000;
    
    // Floating point types
    float price = 19.99f;
    double pi = 3.14159265359;
    
    // Character type
    char grade = 'A';
    char symbol = '$';
    
    // Display values
    printf("Age: %d\n", age);
    printf("Price: %.2f\n", price);
    printf("PI: %.11f\n", pi);
    printf("Grade: %c\n", grade);
    
    // Size of data types
    printf("\nSize of int: %zu bytes\n", sizeof(int));
    printf("Size of float: %zu bytes\n", sizeof(float));
    printf("Size of double: %zu bytes\n", sizeof(double));
    printf("Size of char: %zu byte\n", sizeof(char));
    
    return 0;
}
Try It Yourself
#include <stdio.h>

int main() {
    // Declare variables
    int score = 95;
    float average = 85.5;
    char initial = 'J';
    
    // Modify and print
    score = score + 5;
    average = average * 1.1;
    
    printf("Score: %d\n", score);
    printf("Average: %.1f\n", average);
    printf("Initial: %c\n", initial);
    
    return 0;
}

03. ➕ Operators & Expressions

Operators are special symbols that perform operations on variables and values. C provides a rich set of operators for different purposes.

Types of Operators

  • Arithmetic: +, -, *, /, %
  • Relational: ==, !=, >, <, >=, <=
  • Logical: &&, ||, !
  • Bitwise: &, |, ^, ~, <<, >>
  • Assignment: =, +=, -=, *=, /=
  • Increment/Decrement: ++, --
  • Ternary: ? :
Key Concept: Operator precedence determines the order of operations. Use parentheses to make expressions clearer.

Example: Operators in Action

#include <stdio.h>

int main() {
    int a = 10, b = 3;
    
    // Arithmetic operators
    printf("a + b = %d\n", a + b);   // Addition
    printf("a - b = %d\n", a - b);   // Subtraction
    printf("a * b = %d\n", a * b);   // Multiplication
    printf("a / b = %d\n", a / b);   // Integer division
    printf("a %% b = %d\n", a % b);  // Modulus
    
    // Relational operators
    printf("\na == b: %d\n", a == b);  // 0 (false)
    printf("a != b: %d\n", a != b);    // 1 (true)
    printf("a > b: %d\n", a > b);      // 1 (true)
    
    // Logical operators
    int x = 1, y = 0;
    printf("\nx && y: %d\n", x && y);  // 0 (false)
    printf("x || y: %d\n", x || y);    // 1 (true)
    printf("!x: %d\n", !x);           // 0 (false)
    
    // Bitwise operators
    unsigned int num1 = 5;  // 0101 in binary
    unsigned int num2 = 3;  // 0011 in binary
    
    printf("\n5 & 3 = %u\n", num1 & num2);  // 0001 = 1
    printf("5 | 3 = %u\n", num1 | num2);    // 0111 = 7
    printf("5 ^ 3 = %u\n", num1 ^ num2);    // 0110 = 6
    
    // Assignment operators
    int c = 10;
    c += 5;  // c = c + 5
    printf("\nc = %d\n", c);
    
    // Increment/Decrement
    int d = 5;
    printf("\nOriginal d: %d\n", d);
    printf("Post-increment d++: %d\n", d++);  // Prints 5, then becomes 6
    printf("After increment: %d\n", d);
    printf("Pre-increment ++d: %d\n", ++d);   // Becomes 7, then prints 7
    
    // Ternary operator
    int max = (a > b) ? a : b;
    printf("\nMax of %d and %d is: %d\n", a, b, max);
    
    return 0;
}

04. 🎮 Control Structures

Control structures determine the flow of program execution. They allow you to make decisions, repeat code, and change the execution path.

Types of Control Structures

  • Conditional: if, if-else, switch-case
  • Loops: for, while, do-while
  • Jump: break, continue, goto, return

Loop Comparison

for loop: Used when number of iterations is known
while loop: Used when condition needs checking before each iteration
do-while loop: Used when loop must execute at least once

Example: Control Flow

#include <stdio.h>

int main() {
    // If-else statement
    int number = 15;
    
    if (number > 0) {
        printf("%d is positive\n", number);
    } else if (number < 0) {
        printf("%d is negative\n", number);
    } else {
        printf("Number is zero\n");
    }
    
    // Switch statement
    char grade = 'B';
    printf("\nGrade: ");
    
    switch (grade) {
        case 'A':
            printf("Excellent!\n");
            break;
        case 'B':
            printf("Good job!\n");
            break;
        case 'C':
            printf("Well done\n");
            break;
        case 'D':
            printf("You passed\n");
            break;
        case 'F':
            printf("Better try again\n");
            break;
        default:
            printf("Invalid grade\n");
    }
    
    // For loop
    printf("\nFor loop (1 to 5): ");
    for (int i = 1; i <= 5; i++) {
        printf("%d ", i);
    }
    printf("\n");
    
    // While loop
    printf("While loop (countdown): ");
    int count = 5;
    while (count > 0) {
        printf("%d ", count);
        count--;
    }
    printf("\n");
    
    // Do-while loop
    printf("Do-while loop: ");
    int input;
    do {
        printf("Enter positive number: ");
        scanf("%d", &input);
    } while (input <= 0);
    printf("Thank you! You entered: %d\n", input);
    
    // Break and continue
    printf("\nBreak example: ");
    for (int i = 1; i <= 10; i++) {
        if (i == 6) {
            break;  // Exit loop when i is 6
        }
        printf("%d ", i);
    }
    
    printf("\nContinue example: ");
    for (int i = 1; i <= 10; i++) {
        if (i % 2 == 0) {
            continue;  // Skip even numbers
        }
        printf("%d ", i);
    }
    printf("\n");
    
    return 0;
}
Try It Yourself
#include <stdio.h>

int main() {
    // FizzBuzz Challenge
    for (int i = 1; i <= 20; i++) {
        if (i % 3 == 0 && i % 5 == 0) {
            printf("FizzBuzz ");
        } else if (i % 3 == 0) {
            printf("Fizz ");
        } else if (i % 5 == 0) {
            printf("Buzz ");
        } else {
            printf("%d ", i);
        }
    }
    printf("\n");
    
    return 0;
}

05. 🛠️ Functions in C

Functions are reusable blocks of code that perform specific tasks. They help in modular programming, code reuse, and better organization.

Function Components

  • Function Declaration: Tells compiler about function
  • Function Definition: Actual implementation
  • Function Call: Using the function
  • Parameters: Input values
  • Return Value: Output result

Function Call Stack

main() calls calculate() calls power()
power() - Top
calculate() - Middle
main() - Bottom
Functions are added to call stack when called and removed when they return

Example: Functions Implementation

#include <stdio.h>

// Function declaration (prototype)
int add(int a, int b);
void greet();
int factorial(int n);
float average(int arr[], int size);

int main() {
    // Function calls
    greet();
    
    int sum = add(10, 20);
    printf("Sum: %d\n", sum);
    
    int num = 5;
    printf("Factorial of %d is %d\n", num, factorial(num));
    
    int numbers[] = {85, 90, 78, 92, 88};
    int size = sizeof(numbers) / sizeof(numbers[0]);
    printf("Average score: %.2f\n", average(numbers, size));
    
    return 0;
}

// Function definitions

// Simple void function (no return value)
void greet() {
    printf("Welcome to Functions in C!\n");
}

// Function with parameters and return value
int add(int a, int b) {
    return a + b;
}

// Recursive function
int factorial(int n) {
    if (n == 0 || n == 1) {
        return 1;
    } else {
        return n * factorial(n - 1);
    }
}

// Function with array parameter
float average(int arr[], int size) {
    int total = 0;
    for (int i = 0; i < size; i++) {
        total += arr[i];
    }
    return (float)total / size;
}

// Call by value vs Call by reference
void swapByValue(int a, int b) {
    int temp = a;
    a = b;
    b = temp;
    printf("Inside swapByValue: a=%d, b=%d\n", a, b);
}

void swapByReference(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
    printf("Inside swapByReference: a=%d, b=%d\n", *a, *b);
}

06. 📚 Arrays in C

Arrays are collections of elements of the same type stored in contiguous memory locations. They allow you to work with multiple values efficiently.

Array Types

  • One-dimensional arrays: Simple lists
  • Multi-dimensional arrays: Matrices, tables
  • Dynamic arrays: Size determined at runtime

Array Memory Layout

int numbers[5] = {10, 20, 30, 40, 50};
10
[0]
20
[1]
30
[2]
40
[3]
50
[4]

Contiguous memory blocks, each element accessible by index (0-based)

Example: Array Operations

#include <stdio.h>
#define SIZE 5

int main() {
    // Array declaration and initialization
    int numbers[SIZE] = {10, 20, 30, 40, 50};
    
    // Accessing array elements
    printf("First element: %d\n", numbers[0]);
    printf("Third element: %d\n", numbers[2]);
    printf("Last element: %d\n", numbers[SIZE - 1]);
    
    // Modifying array elements
    numbers[1] = 25;
    printf("\nAfter modification: ");
    for (int i = 0; i < SIZE; i++) {
        printf("%d ", numbers[i]);
    }
    printf("\n");
    
    // Array traversal
    printf("\nArray elements: ");
    for (int i = 0; i < SIZE; i++) {
        printf("numbers[%d] = %d\n", i, numbers[i]);
    }
    
    // Calculating sum
    int sum = 0;
    for (int i = 0; i < SIZE; i++) {
        sum += numbers[i];
    }
    printf("\nSum of array elements: %d\n", sum);
    printf("Average: %.2f\n", (float)sum / SIZE);
    
    // Finding maximum
    int max = numbers[0];
    for (int i = 1; i < SIZE; i++) {
        if (numbers[i] > max) {
            max = numbers[i];
        }
    }
    printf("Maximum value: %d\n", max);
    
    // 2D Arrays (Matrix)
    int matrix[3][3] = {
        {1, 2, 3},
        {4, 5, 6},
        {7, 8, 9}
    };
    
    printf("\n2D Array (Matrix):\n");
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            printf("%d ", matrix[i][j]);
        }
        printf("\n");
    }
    
    // Array of characters (string)
    char name[] = "C Programming";
    printf("\nCharacter array: %s\n", name);
    printf("Length: %zu\n", sizeof(name) - 1); // -1 for null terminator
    
    return 0;
}

07. 🎯 Pointers Deep Dive

Pointers are variables that store memory addresses. They are one of the most powerful but challenging features of C programming.

Pointer Basics

& (address of operator): Gets memory address of a variable
* (dereference operator): Accesses value at a memory address
Pointers enable dynamic memory allocation, efficient array access, and complex data structures.

Pointer Visualization

int x = 42;
int *ptr = &x;
42
x
Variable x
0x7fff...
Pointer ptr
ptr stores the memory address of x
*ptr gives the value 42

Example: Pointer Operations

#include <stdio.h>

int main() {
    // Basic pointer usage
    int num = 42;
    int *ptr = #
    
    printf("Value of num: %d\n", num);
    printf("Address of num: %p\n", &num);
    printf("Value of ptr: %p\n", ptr);
    printf("Value pointed by ptr: %d\n", *ptr);
    
    // Modifying value through pointer
    *ptr = 100;
    printf("\nAfter *ptr = 100:\n");
    printf("Value of num: %d\n", num);
    printf("Value pointed by ptr: %d\n", *ptr);
    
    // Pointer arithmetic
    int arr[] = {10, 20, 30, 40, 50};
    int *arrPtr = arr; // Points to first element
    
    printf("\nArray elements using pointer:\n");
    for (int i = 0; i < 5; i++) {
        printf("arr[%d] = %d at address %p\n", i, *(arrPtr + i), arrPtr + i);
    }
    
    // Pointers to pointers
    int value = 5;
    int *p = &value;
    int **pp = &p;
    
    printf("\nPointers to pointers:\n");
    printf("value = %d\n", value);
    printf("*p = %d\n", *p);
    printf("**pp = %d\n", **pp);
    
    // Pointer to function
    printf("\nFunction pointers:\n");
    int (*funcPtr)(int, int); // Declare function pointer
    
    // Pointer compatibility
    int a = 10;
    float b = 3.14;
    int *intPtr;
    float *floatPtr;
    
    intPtr = &a;
    floatPtr = &b;
    
    printf("\nType safety:\n");
    printf("Integer pointer points to %d\n", *intPtr);
    printf("Float pointer points to %.2f\n", *floatPtr);
    
    // NULL pointer
    int *nullPtr = NULL;
    if (nullPtr == NULL) {
        printf("\nnullPtr is NULL - safe to check\n");
    }
    
    return 0;
}

08. 📝 String Manipulation

In C, strings are arrays of characters terminated by a null character ('\0'). String manipulation is a fundamental skill for text processing.

String Functions (string.h)

  • strlen(): Get string length
  • strcpy(): Copy string
  • strcat(): Concatenate strings
  • strcmp(): Compare strings
  • strstr(): Find substring
  • strtok(): Tokenize string
Key Concept: Always ensure strings have enough space for the null terminator. Buffer overflows are common security vulnerabilities in C string handling.

Example: String Operations

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

int main() {
    // String declaration methods
    char str1[] = "Hello";
    char str2[20] = "World";
    char str3[] = {'C', ' ', 'P', 'r', 'o', 'g', 'r', 'a', 'm', '\0'};
    
    printf("String 1: %s\n", str1);
    printf("String 2: %s\n", str2);
    printf("String 3: %s\n", str3);
    
    // String length
    printf("\nLength of '%s' is %zu\n", str1, strlen(str1));
    
    // String copy
    char copy[20];
    strcpy(copy, str1);
    printf("Copied string: %s\n", copy);
    
    // String concatenation
    strcat(copy, " ");
    strcat(copy, str2);
    printf("Concatenated: %s\n", copy);
    
    // String comparison
    char password[] = "secret123";
    char input[] = "secret123";
    
    if (strcmp(password, input) == 0) {
        printf("\nPassword correct!\n");
    } else {
        printf("Password incorrect\n");
    }
    
    // String searching
    char text[] = "The quick brown fox jumps over the lazy dog";
    char search[] = "fox";
    
    char *result = strstr(text, search);
    if (result != NULL) {
        printf("\n'%s' found at position %ld\n", search, result - text);
    }
    
    // String tokenization
    char data[] = "apple,orange,banana,grape";
    char *token = strtok(data, ",");
    
    printf("\nTokens:\n");
    while (token != NULL) {
        printf("- %s\n", token);
        token = strtok(NULL, ",");
    }
    
    // Character functions
    char mixed[] = "Hello World 123!";
    printf("\nCharacter analysis:\n");
    
    for (int i = 0; mixed[i] != '\0'; i++) {
        printf("%c - ", mixed[i]);
        
        if (isalpha(mixed[i])) {
            printf("Alphabet");
            if (isupper(mixed[i])) {
                printf(" (Uppercase)");
            } else {
                printf(" (Lowercase)");
            }
        } else if (isdigit(mixed[i])) {
            printf("Digit");
        } else if (isspace(mixed[i])) {
            printf("Whitespace");
        } else {
            printf("Other");
        }
        printf("\n");
    }
    
    // String reversal
    char original[] = "Reverse Me";
    char reversed[20];
    int len = strlen(original);
    
    for (int i = 0; i < len; i++) {
        reversed[i] = original[len - 1 - i];
    }
    reversed[len] = '\0';
    
    printf("\nOriginal: %s\n", original);
    printf("Reversed: %s\n", reversed);
    
    return 0;
}

09. 🏗️ Structures & Unions

Structures allow you to combine different data types into a single type. Unions share memory between members, storing only one value at a time.

Structures vs Unions

Structure: Each member has separate memory space
Union: All members share the same memory space
Unions are memory-efficient but only one member can contain a value at any time.

Structure Memory Layout

struct Student {
int id; // 4 bytes
char name[20]; // 20 bytes
float gpa; // 4 bytes
}; // Total: ~28 bytes
id
name[20]
gpa

Example: Structures & Unions

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

// Structure definition
struct Student {
    int id;
    char name[50];
    int age;
    float gpa;
};

// Nested structure
struct Address {
    char street[50];
    char city[30];
    char state[20];
    int zipcode;
};

struct Person {
    char name[50];
    struct Address address;
};

// Union definition
union Data {
    int i;
    float f;
    char str[20];
};

// Typedef for type alias
typedef struct {
    char title[100];
    char author[50];
    int year;
    float price;
} Book;

int main() {
    // Structure variable declaration and initialization
    struct Student student1;
    
    // Assigning values
    student1.id = 101;
    strcpy(student1.name, "John Doe");
    student1.age = 20;
    student1.gpa = 3.8;
    
    printf("Student Information:\n");
    printf("ID: %d\n", student1.id);
    printf("Name: %s\n", student1.name);
    printf("Age: %d\n", student1.age);
    printf("GPA: %.2f\n", student1.gpa);
    
    // Structure initialization at declaration
    struct Student student2 = {102, "Jane Smith", 21, 3.9};
    
    // Array of structures
    struct Student class[3] = {
        {201, "Alice Johnson", 19, 3.7},
        {202, "Bob Williams", 20, 3.5},
        {203, "Charlie Brown", 21, 3.9}
    };
    
    printf("\nClass Roster:\n");
    for (int i = 0; i < 3; i++) {
        printf("%d. %s (GPA: %.2f)\n", 
               class[i].id, class[i].name, class[i].gpa);
    }
    
    // Nested structures
    struct Person person1;
    strcpy(person1.name, "Tom Smith");
    strcpy(person1.address.street, "123 Main St");
    strcpy(person1.address.city, "New York");
    strcpy(person1.address.state, "NY");
    person1.address.zipcode = 10001;
    
    printf("\nPerson Details:\n");
    printf("Name: %s\n", person1.name);
    printf("Address: %s, %s, %s %d\n", 
           person1.address.street, 
           person1.address.city,
           person1.address.state,
           person1.address.zipcode);
    
    // Union usage
    union Data data;
    
    data.i = 10;
    printf("\nUnion data.i = %d\n", data.i);
    
    data.f = 220.5;
    printf("Union data.f = %.2f\n", data.f);
    
    strcpy(data.str, "C Programming");
    printf("Union data.str = %s\n", data.str);
    
    // Typedef usage
    Book book1 = {"The C Programming Language", 
                  "Brian Kernighan & Dennis Ritchie", 
                  1978, 49.99};
    
    printf("\nBook Information:\n");
    printf("Title: %s\n", book1.title);
    printf("Author: %s\n", book1.author);
    printf("Year: %d\n", book1.year);
    printf("Price: $%.2f\n", book1.price);
    
    // Structure pointer
    struct Student *ptr = &student1;
    printf("\nAccessing via pointer:\n");
    printf("ID: %d\n", ptr->id);
    printf("Name: %s\n", ptr->name);
    printf("Age: %d\n", ptr->age);
    
    // Size of structure
    printf("\nSize of Student structure: %zu bytes\n", sizeof(struct Student));
    printf("Size of Data union: %zu bytes\n", sizeof(union Data));
    
    return 0;
}

10. 💾 File Input/Output

File I/O operations allow programs to read from and write to files, enabling persistent data storage.

File Operations

  • Opening files: fopen()
  • Closing files: fclose()
  • Reading: fgetc(), fgets(), fread(), fscanf()
  • Writing: fputc(), fputs(), fwrite(), fprintf()
  • Navigation: fseek(), ftell(), rewind()
Key Concept: Always check if file operations succeed and close files when done. Use error handling to prevent data corruption.

Example: File Operations

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

int main() {
    FILE *file;
    char ch;
    
    // Writing to a file
    file = fopen("example.txt", "w");
    
    if (file == NULL) {
        printf("Error creating file!\n");
        return 1;
    }
    
    fprintf(file, "This is line 1\n");
    fprintf(file, "This is line 2\n");
    fprintf(file, "Numbers: %d %.2f\n", 42, 3.14);
    
    fclose(file);
    printf("File written successfully.\n");
    
    // Reading from a file - character by character
    printf("\nReading file character by character:\n");
    file = fopen("example.txt", "r");
    
    if (file == NULL) {
        printf("Error opening file!\n");
        return 1;
    }
    
    while ((ch = fgetc(file)) != EOF) {
        putchar(ch);
    }
    fclose(file);
    
    // Reading line by line
    printf("\n\nReading file line by line:\n");
    file = fopen("example.txt", "r");
    
    char line[100];
    while (fgets(line, sizeof(line), file) != NULL) {
        printf("Line: %s", line);
    }
    fclose(file);
    
    // Binary file operations
    struct Student {
        int id;
        char name[50];
        float marks;
    };
    
    struct Student students[3] = {
        {1, "Alice", 85.5},
        {2, "Bob", 90.0},
        {3, "Charlie", 78.5}
    };
    
    // Write structures to binary file
    file = fopen("students.dat", "wb");
    if (file == NULL) {
        printf("Error creating binary file!\n");
        return 1;
    }
    
    fwrite(students, sizeof(struct Student), 3, file);
    fclose(file);
    
    // Read structures from binary file
    struct Student readStudents[3];
    
    file = fopen("students.dat", "rb");
    if (file == NULL) {
        printf("Error opening binary file!\n");
        return 1;
    }
    
    fread(readStudents, sizeof(struct Student), 3, file);
    fclose(file);
    
    printf("\n\nStudents from binary file:\n");
    for (int i = 0; i < 3; i++) {
        printf("ID: %d, Name: %s, Marks: %.2f\n",
               readStudents[i].id,
               readStudents[i].name,
               readStudents[i].marks);
    }
    
    // File positioning
    file = fopen("example.txt", "r");
    
    // Move to 10th byte from beginning
    fseek(file, 10, SEEK_SET);
    printf("\n\nFrom position 10: ");
    
    while ((ch = fgetc(file)) != '\n' && ch != EOF) {
        putchar(ch);
    }
    
    // Get current position
    long position = ftell(file);
    printf("\nCurrent position: %ld\n", position);
    
    // Go back to beginning
    rewind(file);
    printf("\nAfter rewind, first 5 chars: ");
    for (int i = 0; i < 5; i++) {
        ch = fgetc(file);
        putchar(ch);
    }
    
    fclose(file);
    
    // Append to file
    file = fopen("example.txt", "a");
    fprintf(file, "This is appended text\n");
    fclose(file);
    
    printf("\n\nFile operations completed successfully!\n");
    
    return 0;
}

11. 🧠 Memory Management

C provides manual memory management through dynamic allocation functions. Understanding this is crucial for efficient programs.

Memory Segments

Stack: Local variables, automatic allocation
Heap: Dynamic memory, manual allocation
Data Segment: Global and static variables
Code Segment: Program instructions

Memory Allocation Process

Stack
Local variables, function calls
Heap
Dynamic memory (malloc/calloc)
Data Segment
Global/static variables
Code Segment
Program instructions

Example: Dynamic Memory Allocation

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

int main() {
    // malloc - memory allocation
    int *ptr = (int *)malloc(5 * sizeof(int));
    
    if (ptr == NULL) {
        printf("Memory allocation failed!\n");
        return 1;
    }
    
    printf("malloc example:\n");
    for (int i = 0; i < 5; i++) {
        ptr[i] = i * 10;
        printf("ptr[%d] = %d\n", i, ptr[i]);
    }
    
    // Realloc - resize memory block
    ptr = (int *)realloc(ptr, 10 * sizeof(int));
    
    if (ptr == NULL) {
        printf("Memory reallocation failed!\n");
        return 1;
    }
    
    printf("\nAfter realloc (size 10):\n");
    for (int i = 5; i < 10; i++) {
        ptr[i] = i * 10;
    }
    
    for (int i = 0; i < 10; i++) {
        printf("ptr[%d] = %d\n", i, ptr[i]);
    }
    
    // Free memory
    free(ptr);
    ptr = NULL; // Good practice
    
    // calloc - contiguous allocation (initialized to 0)
    int *arr = (int *)calloc(5, sizeof(int));
    
    printf("\ncalloc example (initialized to 0):\n");
    for (int i = 0; i < 5; i++) {
        printf("arr[%d] = %d\n", i, arr[i]);
    }
    
    free(arr);
    
    // Dynamic array of structures
    struct Point {
        int x;
        int y;
    };
    
    int n;
    printf("\nEnter number of points: ");
    scanf("%d", &n);
    
    struct Point *points = (struct Point *)malloc(n * sizeof(struct Point));
    
    for (int i = 0; i < n; i++) {
        points[i].x = i * 2;
        points[i].y = i * 3;
    }
    
    printf("\nPoints:\n");
    for (int i = 0; i < n; i++) {
        printf("Point %d: (%d, %d)\n", i+1, points[i].x, points[i].y);
    }
    
    free(points);
    
    // Memory leak example (commented out - DON'T DO THIS!)
    /*
    printf("\nMemory leak example (commented out):\n");
    for (int i = 0; i < 1000; i++) {
        int *leak = (int *)malloc(1000 * sizeof(int));
        // Forgot to free - MEMORY LEAK!
    }
    */
    
    // Double free error (commented out - DON'T DO THIS!)
    /*
    int *error = (int *)malloc(sizeof(int));
    free(error);
    free(error); // ERROR: Double free
    */
    
    // Use after free error (commented out - DON'T DO THIS!)
    /*
    int *dangling = (int *)malloc(sizeof(int));
    *dangling = 42;
    free(dangling);
    printf("%d\n", *dangling); // ERROR: Use after free
    */
    
    printf("\nMemory management best practices:\n");
    printf("1. Always check if allocation succeeded\n");
    printf("2. Free memory when done\n");
    printf("3. Set pointer to NULL after freeing\n");
    printf("4. Don't access freed memory\n");
    printf("5. Don't free memory twice\n");
    
    return 0;
}

12. 🏛️ Data Structures in C

Data structures organize and store data efficiently. Implementing them in C teaches fundamental computer science concepts.

Common Data Structures

  • Linked Lists: Dynamic nodes connected by pointers
  • Stacks: LIFO (Last In First Out)
  • Queues: FIFO (First In First Out)
  • Trees: Hierarchical structures
  • Graphs: Nodes and edges
  • Hash Tables: Key-value pairs

Linked List Visualization

10
next
Head
20
next
30
NULL
Tail
Each node contains data and a pointer to the next node

Example: Linked List Implementation

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

// Node structure for linked list
struct Node {
    int data;
    struct Node* next;
};

// Function to create a new node
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;
}

// Function to insert at beginning
void insertAtBeginning(struct Node** head, int data) {
    struct Node* newNode = createNode(data);
    newNode->next = *head;
    *head = newNode;
}

// Function to insert at end
void insertAtEnd(struct Node** head, int data) {
    struct Node* newNode = createNode(data);
    
    if (*head == NULL) {
        *head = newNode;
        return;
    }
    
    struct Node* temp = *head;
    while (temp->next != NULL) {
        temp = temp->next;
    }
    temp->next = newNode;
}

// Function to display linked list
void displayList(struct Node* head) {
    struct Node* temp = head;
    printf("Linked List: ");
    while (temp != NULL) {
        printf("%d -> ", temp->data);
        temp = temp->next;
    }
    printf("NULL\n");
}

// Function to delete a node
void deleteNode(struct Node** head, int key) {
    struct Node *temp = *head, *prev = NULL;
    
    if (temp != NULL && temp->data == key) {
        *head = temp->next;
        free(temp);
        return;
    }
    
    while (temp != NULL && temp->data != key) {
        prev = temp;
        temp = temp->next;
    }
    
    if (temp == NULL) {
        printf("Key %d not found in list\n", key);
        return;
    }
    
    prev->next = temp->next;
    free(temp);
}

// Stack implementation using array
#define MAX 100

struct Stack {
    int arr[MAX];
    int top;
};

void initStack(struct Stack* s) {
    s->top = -1;
}

int isFull(struct Stack* s) {
    return s->top == MAX - 1;
}

int isEmpty(struct Stack* s) {
    return s->top == -1;
}

void push(struct Stack* s, int value) {
    if (isFull(s)) {
        printf("Stack Overflow!\n");
        return;
    }
    s->arr[++(s->top)] = value;
}

int pop(struct Stack* s) {
    if (isEmpty(s)) {
        printf("Stack Underflow!\n");
        return -1;
    }
    return s->arr[(s->top)--];
}

int peek(struct Stack* s) {
    if (isEmpty(s)) {
        printf("Stack is empty!\n");
        return -1;
    }
    return s->arr[s->top];
}

// Queue implementation
struct Queue {
    int arr[MAX];
    int front, rear;
};

void initQueue(struct Queue* q) {
    q->front = q->rear = -1;
}

int isQueueEmpty(struct Queue* q) {
    return q->front == -1;
}

int isQueueFull(struct Queue* q) {
    return (q->rear + 1) % MAX == q->front;
}

void enqueue(struct Queue* q, int value) {
    if (isQueueFull(q)) {
        printf("Queue is full!\n");
        return;
    }
    
    if (isQueueEmpty(q)) {
        q->front = q->rear = 0;
    } else {
        q->rear = (q->rear + 1) % MAX;
    }
    
    q->arr[q->rear] = value;
}

int dequeue(struct Queue* q) {
    if (isQueueEmpty(q)) {
        printf("Queue is empty!\n");
        return -1;
    }
    
    int value = q->arr[q->front];
    
    if (q->front == q->rear) {
        q->front = q->rear = -1;
    } else {
        q->front = (q->front + 1) % MAX;
    }
    
    return value;
}

// Binary Search Tree
struct TreeNode {
    int data;
    struct TreeNode* left;
    struct TreeNode* right;
};

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

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

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

int main() {
    printf("=== LINKED LIST DEMO ===\n");
    struct Node* head = NULL;
    
    insertAtEnd(&head, 10);
    insertAtEnd(&head, 20);
    insertAtBeginning(&head, 5);
    insertAtEnd(&head, 30);
    
    displayList(head);
    
    deleteNode(&head, 20);
    displayList(head);
    
    printf("\n=== STACK DEMO ===\n");
    struct Stack s;
    initStack(&s);
    
    push(&s, 10);
    push(&s, 20);
    push(&s, 30);
    
    printf("Top element: %d\n", peek(&s));
    printf("Popped: %d\n", pop(&s));
    printf("Top element after pop: %d\n", peek(&s));
    
    printf("\n=== QUEUE DEMO ===\n");
    struct Queue q;
    initQueue(&q);
    
    enqueue(&q, 10);
    enqueue(&q, 20);
    enqueue(&q, 30);
    
    printf("Dequeued: %d\n", dequeue(&q));
    printf("Dequeued: %d\n", dequeue(&q));
    
    printf("\n=== BINARY SEARCH TREE DEMO ===\n");
    struct TreeNode* root = NULL;
    
    root = insertBST(root, 50);
    insertBST(root, 30);
    insertBST(root, 20);
    insertBST(root, 40);
    insertBST(root, 70);
    insertBST(root, 60);
    insertBST(root, 80);
    
    printf("Inorder traversal (sorted): ");
    inorderTraversal(root);
    printf("\n");
    
    // Clean up linked list
    while (head != NULL) {
        struct Node* temp = head;
        head = head->next;
        free(temp);
    }
    
    printf("\nData structures implemented successfully!\n");
    
    return 0;
}
Final Challenge: Implement a Function
#include <stdio.h>

// TODO: Implement this function
// It should return 1 if number is prime, 0 otherwise
int isPrime(int n) {
    // Your code here
    if (n <= 1) return 0;
    for (int i = 2; i * i <= n; i++) {
        if (n % i == 0) return 0;
    }
    return 1;
}

int main() {
    printf("Prime numbers between 1 and 50:\n");
    
    for (int i = 1; i <= 50; i++) {
        if (isPrime(i)) {
            printf("%d ", i);
        }
    }
    printf("\n");
    
    return 0;
}