Lecture 1 / 16
Lecture 01 · Basics

Introduction & Setup

What is C?

C is a powerful general-purpose, procedural programming language developed by Dennis Ritchie at Bell Labs in 1972. It was designed to build the Unix operating system and has since become one of the most widely used and influential languages in computer science, serving as the foundation for languages like C++, Java, C#, and Python.

Key Features of C
  • Procedural Language: Instructions are executed step-by-step in a logical flow.
  • Close to Hardware: Direct memory manipulation through pointers.
  • Fast & Compact: Minimal runtime library, producing extremely fast machine code.
  • Portable: Code compiled on one system can run on another with minimal modifications.

Setting Up the GCC Compiler

To compile C programs, you need a C compiler. The most common compiler is **GCC** (GNU Compiler Collection), which runs natively on Linux, macOS (via Xcode Command Line Tools), and Windows (via MinGW).

Your First C Program

Here is a basic C program that outputs "Hello, World!" to the screen:

hello.c
#include <stdio.h>

int main() {
    printf("Hello, World!\n");
    return 0;
}
🎯 Exercise 1

Write, save, and compile hello.c in your terminal using the command gcc hello.c -o hello, and execute it by running ./hello.

Lecture 02 · Basics

Variables & Data Types

Variable Declarations

C requires all variables to be declared with their specific type before they are used. Variables reserve a specific size of memory depending on their data type.

Basic Data Types in C

  • int: Stores whole numbers (integers), e.g. 45.
  • float: Stores decimal numbers (single precision), e.g. 3.14f.
  • double: Stores larger decimals (double precision), e.g. 2.7182818.
  • char: Stores single characters enclosed in single quotes, e.g. 'X'.

Format Specifiers

C uses format specifiers within formatting strings to print or read data using printf and scanf: %d for int, %f for float, %lf for double, and %c for char.

variables.c
#include <stdio.h>

int main() {
    int age = 30;
    float price = 5.99f;
    char grade = 'A';

    printf("Age: %d, Price: %.2f, Grade: %c\n", age, price, grade);
    return 0;
}
🎯 Exercise 2

Write a program that prompts the user for their age using scanf, increments the age by 5, and prints the result.

Lecture 03 · Basics

Operators & Expressions

Types of Operators

  • Arithmetic: +, -, *, / (integer division truncates decimal values if both operands are integers), % (modulo).
  • Relational: Compare operands and return 1 (true) or 0 (false), e.g., ==, !=, <, >, <=, >=.
  • Logical: Combine relational conditions, e.g., && (AND), || (OR), ! (NOT).
  • Assignment: Assign value to variable: =, +=, -=, *=, /=.
operators.c
#include <stdio.h>

int main() {
    int x = 10;
    int y = 4;

    int div = x / y; // Output: 2 (truncated)
    int mod = x % y; // Output: 2 (remainder)

    int active = (x > 5) && (y < 10); // True (1)
    
    printf("Div: %d, Mod: %d, Active: %d\n", div, mod, active);
    return 0;
}
🎯 Exercise 3

Write a program that takes an integer and prints 1 if it is divisible by 5 and 7, and 0 otherwise.

Lecture 04 · Basics

Control Flow

Conditional Branching

Control flow allows execution branches based on true or false conditions. C uses if, else if, and else.

Switch Statement

The switch statement checks a variable against multiple integer or character values. Each case must terminate with a break statement to prevent falling through to the next case.

control.c
#include <stdio.h>

int main() {
    int rating = 4;

    if (rating >= 4) {
        printf("Excellent!\n");
    } else if (rating >= 2) {
        printf("Average.\n");
    } else {
        printf("Poor.\n");
    }
    return 0;
}
🎯 Exercise 4

Write a calculator program using a switch statement that reads two float numbers and a char operator (+, -, *, /), computes the operation, and prints the result.

Lecture 05 · Basics

Loops & Iteration

Loop Constructs

  • while: Evaluates condition before running the loop.
  • do-while: Runs loop body once, then evaluates condition. Always runs at least once.
  • for: Bundles init, condition, and increment step together. Good for counted loops.

Break & Continue

The break keyword terminates the current loop immediately. The continue keyword skips the remaining statements in the current iteration and jumps directly to the next loop evaluation.

loops.c
#include <stdio.h>

int main() {
    // For loop skipping 3
    for (int i = 1; i <= 5; i++) {
        if (i == 3) continue;
        printf("%d ", i); // Outputs: 1 2 4 5
    }
    printf("\n");

    // While loop with break
    int num = 0;
    while (1) {
        if (num >= 3) break;
        printf("%d\n", num);
        num++;
    }
    return 0;
}
🎯 Exercise 5

Write a program that uses a loop to print the multiplication table of a user-input integer up to 10.

Lecture 06 · Basics

Functions & Scope

Creating Functions

Functions divide a program into smaller, logical sub-units. A function must declare its return type, parameter list, and must return a matching value (unless the return type is void). If the function is defined after `main`, it must have a **forward declaration / prototype** at the top of the file.

Scope & Parameter Passing

In C, arguments are passed to functions **by value** (copies are made). Local variables are only visible inside their declared functions. Global variables are declared outside all functions and are accessible anywhere.

functions.c
#include <stdio.h>

// Prototype declaration
int get_square(int num);

int main() {
    int x = 4;
    int square = get_square(x);
    printf("Square of %d is %d\n", x, square); // 16
    return 0;
}

// Definition
int get_square(int num) {
    return num * num;
}
🎯 Exercise 6

Write a recursive function factorial(int n) that computes and returns the factorial of an integer. Test it inside your main function.

Lecture 07 · Memory & Structures

Arrays & Matrices

Single-Dimensional Arrays

An array is a fixed-size, sequential list of variables sharing the same type. C arrays are zero-indexed, meaning elements are accessed from 0 up to size - 1.

Multi-Dimensional Arrays (Matrices)

You can create multi-dimensional arrays, which are frequently used to represent mathematical grids or matrices (e.g., rows and columns).

arrays.c
#include <stdio.h>

int main() {
    // 1D Array
    int numbers[5] = {1, 2, 3, 4, 5};
    printf("Third element: %d\n", numbers[2]); // 3

    // 2D Matrix (2 rows, 3 columns)
    int matrix[2][3] = {
        {1, 2, 3},
        {4, 5, 6}
    };
    printf("Element at row 1, col 2: %d\n", matrix[1][2]); // 6
    return 0;
}
⚠️ Array Bounds checking

C does not perform runtime array bounds checking! Accessing indices outside the array bounds (e.g., index 5 on an array of size 5) leads to **undefined behavior** and can corrupt system memory.

🎯 Exercise 7

Write a program that declares a 3x3 matrix, prompts the user to input integer values for all elements, and then computes the sum of the primary diagonal elements.

Lecture 08 · Memory & Structures

Pointers & Memory

Pointers

A pointer is a variable that stores the memory address of another variable. We declare a pointer by appending * to its data type. We get the address of a variable using the address-of operator (&), and access the value at the address using the dereference operator (*).

Pointer Arithmetic & Array Links

The name of an array acts as a constant pointer to its first element. Pointers can be incremented (ptr++) to point to subsequent elements in memory.

pointers.c
#include <stdio.h>

int main() {
    int var = 10;
    int *ptr = &var; // ptr stores address of var

    printf("Address: %p\n", (void*)ptr);
    printf("Value: %d\n", *ptr); // 10

    *ptr = 20; // Modify var indirectly
    printf("Modified value: %d\n", var); // 20
    return 0;
}
🎯 Exercise 8

Write a function swap(int *a, int *b) that uses pointers to swap the values of two integers. Call it from your main function and print the results.

Lecture 09 · Memory & Structures

Strings & Character Arrays

Strings in C

C does not have a native string data type. Instead, strings are represented as 1D arrays of type char that terminate with a special null character ('\0'). Always reserve one extra space in character arrays for this null terminator.

Standard String Functions

The <string.h> library contains functions to manipulate strings:

  • strlen(str): Returns string length (excluding the null character).
  • strcpy(dest, src): Copies a source string to a destination buffer.
  • strcat(dest, src): Appends source string to destination.
  • strcmp(str1, str2): Compares strings alphabetically (returns 0 if equal).
strings.c
#include <stdio.h>
#include <string.h>

int main() {
    char greeting[20] = "Hello";
    char name[] = " Alice";

    strcat(greeting, name); // Concatenate
    printf("%s\n", greeting); // Outputs: Hello Alice
    printf("Length: %lu\n", strlen(greeting)); // 11
    return 0;
}
🎯 Exercise 9

Write a program that reads a string from the user using fgets, reverses it in place, and prints the reversed string.

Lecture 10 · Memory & Structures

Structures & Unions

Structures (struct)

A structure is a user-defined data type that allows you to group variables of different data types under a single name. Members are accessed using the dot (.) operator, or the arrow (->) operator when accessing structure variables through pointers.

Unions (union)

A union is similar to a structure, but it shares a single memory location for all its members. Only one member can hold a value at any given time.

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

struct Employee {
    char name[50];
    int id;
    float salary;
};

int main() {
    struct Employee emp1;
    emp1.id = 1001;
    emp1.salary = 75000.50f;
    strcpy(emp1.name, "Bob Smith");

    struct Employee *ptr = &emp1;
    printf("Name: %s, Salary: %.2f\n", ptr->name, ptr->salary);
    return 0;
}
🎯 Exercise 10

Define a structure called Point representing 2D space coordinates (x, y). Write a function that calculates the Euclidean distance between two Points passed to it, and call it from your main loop.

Lecture 11 · Memory & Structures

Dynamic Memory Allocation

Dynamic Memory Functions

C allocates variables automatically on the stack. For runtime-defined memory requirements, C lets you allocate memory from the heap dynamically using the <stdlib.h> library:

  • malloc(size): Allocates a block of raw uninitialized memory.
  • calloc(num, size): Allocates memory and initializes all bytes to zero.
  • realloc(ptr, size): Resizes previously allocated heap blocks.
  • free(ptr): Releases heap memory blocks back to the system.
dynamic_mem.c
#include <stdio.h>
#include <stdlib.h>

int main() {
    int *arr = (int*)malloc(3 * sizeof(int));
    if (arr == NULL) return 1; // Check allocation

    arr[0] = 10; arr[1] = 20; arr[2] = 30;
    printf("Second value: %d\n", arr[1]);

    free(arr); // Free memory block
    arr = NULL;
    return 0;
}
⚠️ Memory Leaks

Always release dynamically allocated memory blocks using free(ptr) when you are done with them. Forgetting to release memory creates **memory leaks**, which consume system resources and can crash the computer.

🎯 Exercise 11

Write a program that dynamically allocates an integer array of size input by the user, reads values for all indexes, prints them, and frees the memory block.

Lecture 12 · Advanced & Systems

Preprocessor Directives

Macros and File Inclusions

The preprocessor processes source code *before* it is passed to the compiler. Directives start with the hash character (#):

  • #include: Inserts header contents directly into the file.
  • #define: Declares macros (symbolic constants or inline replacement code).
  • #ifdef / #ifndef / #endif: Controls conditional compilation of blocks.
preprocessor.c
#include <stdio.h>

#define PI 3.14159
#define AREA(r) (PI * (r) * (r))

int main() {
    float r = 5.0f;
    printf("Area: %.4f\n", AREA(r));
    return 0;
}
🎯 Exercise 12

Write a macro MAX(a, b) that evaluates to the maximum of two numbers using the ternary conditional operator. Test it in a C program.

Lecture 13 · Advanced & Systems

File Handling

File Stream Operations

File handling in C is processed using the FILE structure defined in <stdio.h>. We manipulate files using file pointers (FILE *fp):

  • fopen(name, mode): Opens a file stream (modes: "r", "w", "a").
  • fclose(fp): Closes open file streams.
  • fprintf(fp, ...) / fscanf(fp, ...): Format print or write/read from file streams.
  • fgetc / fputc: Reads/writes single characters.
files.c
#include <stdio.h>

int main() {
    // Write
    FILE *fp = fopen("output.txt", "w");
    if (fp != NULL) {
        fprintf(fp, "C File Handling Example.\n");
        fclose(fp);
    }

    // Read
    fp = fopen("output.txt", "r");
    char buffer[100];
    if (fp != NULL) {
        while (fgets(buffer, sizeof(buffer), fp) != NULL) {
            printf("%s", buffer);
        }
        fclose(fp);
    }
    return 0;
}
🎯 Exercise 13

Write a program that copies the contents of a text file named source.txt to a new file named destination.txt character-by-character.

Lecture 14 · Advanced & Systems

Storage Classes

Variable Lifetimes

Storage classes define the scope, visibility, and lifetime of variables in C:

  • auto: Default storage class for local variables.
  • register: Requests the compiler to store variables in registers for speed.
  • static: Retains variable values across function calls; local to compile unit.
  • extern: Points to variables declared in other source files (global link).
storage.c
#include <stdio.h>

void counter_func() {
    static int count = 0; // Initialized once, retains value
    count++;
    printf("Count: %d\n", count);
}

int main() {
    counter_func(); // Count: 1
    counter_func(); // Count: 2
    counter_func(); // Count: 3
    return 0;
}
🎯 Exercise 14

Write a program with a static variable inside a recursive function to count the number of recursive cycles completed.

Lecture 15 · Advanced & Systems

Bitwise & Type Casting

Bitwise Operators

Bitwise operators perform calculations directly on the binary representation of integers:

  • & Bitwise AND, | Bitwise OR
  • ^ Bitwise XOR, ~ Bitwise NOT (One's Complement)
  • << Left Shift (multiplies by 2), >> Right Shift (divides by 2)

Type Casting

Type casting converts variables of one type to another. We perform manual typecasting by prefixing variable names with desired types inside parentheses (e.g. (float)val).

bitwise.c
#include <stdio.h>

int main() {
    // Bitwise shift
    unsigned char x = 5; // Binary: 00000101
    printf("5 << 1: %d\n", x << 1); // 10 (Binary: 00001010)

    // Type casting
    int sum = 17;
    int count = 5;
    double mean = (double)sum / count; // 3.4
    
    printf("Mean: %.1f\n", mean);
    return 0;
}
🎯 Exercise 15

Write a C program that checks if a user-input number is odd or even using the bitwise AND operator (&) instead of the modulus operator.

Lecture 16 · Advanced & Systems

Final Capstone Project

Project: Dynamic Inventory Database

Consolidate your C programming skills by building a command-line Inventory Records system. The project utilizes structures, pointers, dynamic memory allocation, files, and format parsing.

Project Requirements:

  1. Item Structure: Defines Item containing attributes: name, ID, price, and stock count.
  2. Dynamic List: Manage items inside a dynamically allocated array. Reallocate array sizes if new items exceed capacity (via realloc).
  3. File Operations: Load records from a text file (inventory.txt) on startup, and save all records to the file upon exiting.
  4. Interactive Menu: Provide options to:
    • Display all items
    • Add new item record
    • Update stock count of item
    • Save inventory data and Exit
  5. Safe operations: Perform input checking to handle null pointer errors or invalid menu option values.
project_stub.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct {
    int id;
    char name[30];
    float price;
    int stock;
} Item;

// Global list pointer and tracking variables
Item *inventory = NULL;
int item_count = 0;
int capacity = 2;

// Implement dynamic additions, displays, and main menu loop
🎯 Final Challenge

Write the complete script in a file named inventory.c. Compile, run, add several items, check if the save file persists correctly, and free all heap memory before exiting.