Unit 1: Introduction to C

📚 What is C Language?

C is a general-purpose, procedural programming language developed by Dennis Ritchie at Bell Labs in 1972. It was originally created to develop the UNIX operating system.

Why learn C?

Features of C: Structured language, rich library of built-in functions, portability (runs on any machine with a C compiler), extensible (you can add your own functions), case-sensitive (variable and Variable are different).

Structure of a C Program:

#include <stdio.h>    // Preprocessor directive - includes standard I/O library

int main() {           // main() - where program execution STARTS
    printf("Hello!");  // printf() - prints output to screen
    return 0;          // Returns 0 to OS - means "program ended successfully"
}

Explanation of each part:

Compilation Process:

Source Code (.c) → Preprocessor → Compiler → Assembler → Linker → Executable (.exe)

📚 Variables, Data Types & Operators

A variable is a named container that stores a value in memory. Think of it as a labeled box.

int age = 20;       // declares integer variable "age" with value 20
float gpa = 8.5;    // declares decimal variable "gpa"
char grade = 'A';   // declares character variable (single quotes!)

Rules for Variable Names:

Data Types (how much memory each uses):

Type Size Range (approximate) Format Specifier Use For
int 4 bytes -2 billion to +2 billion %d Whole numbers: age, count, marks
float 4 bytes ±3.4 × 10³⁸ (6-7 decimal digits) %f Decimals: price, temperature
double 8 bytes ±1.7 × 10³⁰⁸ (15-16 digits) %lf High precision decimals
char 1 byte -128 to 127 / 0 to 255 %c Single character: 'A', '9', '#'
short 2 bytes -32768 to 32767 %hd Small integers
long 8 bytes Very large numbers %ld Large integers

Input & Output:

int age;
printf("Enter your age: ");   // Display prompt to user
scanf("%d", &age);             // Read user input into variable 'age'
                                // & is REQUIRED - it gives the ADDRESS of variable
printf("You are %d years old\n", age);  // Display with variable value

// \n = newline (move to next line)
// \t = tab space
// \\ = print backslash
// \" = print quote mark

Operators (Complete List):

Category Operators Examples
Arithmetic + - * / % 10/3=3 (integer division!), 10%3=1 (remainder)
Relational == != < > <= >= 5==5 (true=1), 5!=3 (true=1), 5>3 (true=1)
Logical && || ! (5>3 && 2<4) = true, !(5>3) = false
Assignment = += -= *= /= %= x+=5 means x=x+5
Increment/Decrement ++ -- i++ (use then add), ++i (add then use)
Bitwise & | ^ ~ << >> 5&3=1, 5|3=7, 5^3=6
Ternary ? : result = (a>b) ? a : b; // if a>b then a, else b
sizeof sizeof() sizeof(int) = 4
Common mistake: = is ASSIGNMENT (x = 5 means "set x to 5"). == is COMPARISON (x == 5 means "is x equal to 5?"). Confusing these causes bugs!
Unit 2: Control Flow (Decision Making & Loops)

📚 Decision Making (if, else, switch)

Decision-making statements let your program choose which code to execute based on conditions.

1. if statement — run code only IF condition is true:

int marks = 85;
if (marks >= 90) {
    printf("Grade A");
} else if (marks >= 80) {    // checked only if above condition was false
    printf("Grade B");
} else if (marks >= 60) {
    printf("Grade C");
} else {                        // if none of the above are true
    printf("Grade F");
}
// Output: Grade B (because 85 >= 80 is true)

2. switch statement — choose from multiple fixed options:

int day = 3;
switch (day) {
    case 1: printf("Monday"); break;
    case 2: printf("Tuesday"); break;
    case 3: printf("Wednesday"); break;  // This executes!
    case 4: printf("Thursday"); break;
    default: printf("Invalid day");      // If no case matches
}
// Output: Wednesday
Important: Don't forget break; after each case! Without it, execution "falls through" to the next case.

📚 Loops (for, while, do-while)

Loops repeat a block of code multiple times until a condition becomes false.

1. for loop — when you know HOW MANY times to repeat:

//          init;     condition;  update
for (int i = 1; i <= 5; i++) {
    printf("%d ", i);
}
// Output: 1 2 3 4 5
// How it works: i starts at 1. Each time, check i<=5. If true, print and add 1 to i.

2. while loop — when you DON'T know how many times (check condition FIRST):

int i = 1;
while (i <= 5) {      // condition checked BEFORE each iteration
    printf("%d ", i);
    i++;                // must update manually or infinite loop!
}
// Output: 1 2 3 4 5

3. do-while loop — executes AT LEAST ONCE (checks condition AFTER):

int i = 10;
do {
    printf("%d ", i);   // This executes at least once, even though 10 > 5!
    i++;
} while (i <= 5);
// Output: 10  (executes once then stops because 11 <= 5 is false)

break vs continue:

// break - EXIT the loop immediately
for (int i = 1; i <= 10; i++) {
    if (i == 5) break;      // Stop when i reaches 5
    printf("%d ", i);
}
// Output: 1 2 3 4

// continue - SKIP current iteration, go to next
for (int i = 1; i <= 5; i++) {
    if (i == 3) continue;   // Skip when i is 3
    printf("%d ", i);
}
// Output: 1 2 4 5 (3 is skipped!)

Nested Loops (loop inside a loop):

// Multiplication Table
for (int i = 1; i <= 3; i++) {        // outer: rows
    for (int j = 1; j <= 3; j++) {    // inner: columns
        printf("%d*%d=%d\t", i, j, i*j);
    }
    printf("\n");
}
// Output:
// 1*1=1   1*2=2   1*3=3
// 2*1=2   2*2=4   2*3=6
// 3*1=3   3*2=6   3*3=9
Unit 3: Functions

📚 Functions — Reusable Blocks of Code

A function is a self-contained block of code that performs a specific task. Instead of writing the same code multiple times, you write it once in a function and call it whenever needed.

Why Functions?

Function Syntax:

// DECLARATION (tells compiler the function exists)
return_type function_name(parameter_type param1, parameter_type param2);

// DEFINITION (the actual code)
return_type function_name(parameter_type param1, parameter_type param2) {
    // body of code
    return value;   // send result back (skip if return_type is void)
}

// CALL (using the function)
result = function_name(arg1, arg2);

Complete Example:

#include <stdio.h>

// Declaration (prototype) — tells compiler about function before main()
int add(int a, int b);

int main() {
    int result = add(10, 20);       // CALL - passes 10 and 20 as arguments
    printf("Sum = %d\n", result);   // Output: Sum = 30
    return 0;
}

// Definition
int add(int a, int b) {    // a and b are PARAMETERS (receive values)
    return a + b;           // Sends result back to caller
}

Call by Value vs Call by Reference:

Call by Value Call by Reference (using pointers)
Copies of values are passed Addresses (pointers) are passed
Changes inside function DON'T affect original Changes inside function DO affect original
void swap(int a, int b) void swap(int *a, int *b)
// Call by Reference — ACTUALLY swaps values
void swap(int *a, int *b) {   // receives addresses
    int temp = *a;             // *a means "value at address a"
    *a = *b;
    *b = temp;
}

int main() {
    int x=10, y=20;
    swap(&x, &y);              // pass addresses using &
    printf("x=%d, y=%d", x, y); // Output: x=20, y=10 (actually swapped!)
}

Recursion — A function calling itself:

// Factorial: n! = n * (n-1)!
int factorial(int n) {
    if (n == 0 || n == 1)     // BASE CASE - stops the recursion
        return 1;
    return n * factorial(n-1); // RECURSIVE CALL
}
// factorial(5) = 5 * factorial(4) = 5 * 4 * factorial(3) = ... = 5*4*3*2*1 = 120
Every recursion MUST have a base case! Without it, the function calls itself forever and causes a Stack Overflow crash.
Unit 4: Arrays & Strings

📚 Arrays — Storing Multiple Values

An array is a collection of elements of the same data type stored in contiguous (next to each other) memory locations. Instead of creating 100 separate variables, you create one array of 100 elements.

// Declaration and initialization
int marks[5] = {85, 90, 78, 92, 88};  // Array of 5 integers

// Accessing elements (INDEX STARTS FROM 0, not 1!)
printf("%d", marks[0]);  // 85 (first element)
printf("%d", marks[4]);  // 88 (last element)
// marks[5] = ERROR! Out of bounds (valid indices: 0 to 4)

// Modifying an element
marks[2] = 95;  // Changes third element from 78 to 95

// Reading array from user
int arr[5];
for (int i = 0; i < 5; i++) {
    printf("Enter element %d: ", i+1);
    scanf("%d", &arr[i]);  // Note: &arr[i] not &arr
}

// Printing array
for (int i = 0; i < 5; i++) {
    printf("%d ", arr[i]);
}

2D Arrays (Matrix):

int matrix[3][3] = {
    {1, 2, 3},    // Row 0
    {4, 5, 6},    // Row 1
    {7, 8, 9}     // Row 2
};

printf("%d", matrix[1][2]);  // 6 (row 1, column 2)

// Traverse entire 2D array
for (int i = 0; i < 3; i++) {        // rows
    for (int j = 0; j < 3; j++) {    // columns
        printf("%d ", matrix[i][j]);
    }
    printf("\n");
}

📚 Strings — Array of Characters

In C, a string is simply an array of characters terminated by a null character '\0'. The null character marks where the string ends.

// Different ways to declare strings
char name[20] = "Sajid";   // Compiler adds '\0' automatically
char name[] = "Sajid";     // Size auto-determined (6: S,a,j,i,d,\0)
char name[] = {'S','a','j','i','d','\0'};  // Manual way

// Reading strings
char name[50];
scanf("%s", name);          // Reads until space (no & needed for arrays!)
gets(name);                  // Reads entire line including spaces
fgets(name, 50, stdin);     // Safer — prevents buffer overflow

// Printing strings
printf("%s", name);         // Prints until \0
puts(name);                 // Prints and adds newline

String Functions (from <string.h>):

Function Purpose Example
strlen(s) Returns length (excluding \0) strlen("Hello") = 5
strcpy(dest, src) Copies src into dest strcpy(name, "Sajid")
strcat(s1, s2) Appends s2 to end of s1 "Hello" + " World" = "Hello World"
strcmp(s1, s2) Compares: returns 0 if equal strcmp("abc","abc") = 0
strrev(s) Reverses the string "Hello" → "olleH"
strupr(s) Converts to uppercase "hello" → "HELLO"
strlwr(s) Converts to lowercase "HELLO" → "hello"
Unit 5: Pointers

📚 Pointers — Understanding Memory Addresses

Every variable in C is stored at a specific memory address (like a house number). A pointer is a variable that stores the address of another variable instead of storing data directly.

int age = 25;        // Normal variable - stores value 25
int *ptr = &age;     // Pointer variable - stores ADDRESS of age

// Two key operators:
// &  (address-of)    → &age gives the address where age is stored
// *  (dereference)   → *ptr gives the value AT the address ptr holds

printf("Value of age: %d\n", age);      // 25
printf("Address of age: %p\n", &age);    // e.g., 0x7ffd3c4b (memory address)
printf("ptr holds address: %p\n", ptr);  // Same address as &age
printf("Value at ptr: %d\n", *ptr);      // 25 (same as age!)

// Changing value through pointer
*ptr = 30;           // Changes value at that address
printf("%d\n", age); // 30! (age changed because ptr points to it)

Pointer Arithmetic:

int arr[] = {10, 20, 30, 40, 50};
int *p = arr;     // Points to first element (arr is itself an address)

printf("%d\n", *p);       // 10 (first element)
printf("%d\n", *(p+1));   // 20 (next element — moves by sizeof(int) bytes)
printf("%d\n", *(p+3));   // 40

// Traversing array with pointer
for (int i = 0; i < 5; i++) {
    printf("%d ", *(p + i));  // same as arr[i]
}

Dynamic Memory Allocation:

#include <stdlib.h>

// malloc — allocate memory at runtime
int *arr = (int*)malloc(5 * sizeof(int));  // allocates space for 5 integers
if (arr == NULL) {
    printf("Memory allocation failed!\n");
    return 1;
}

// Use it like a normal array
for (int i = 0; i < 5; i++) {
    arr[i] = (i+1) * 10;   // arr: 10, 20, 30, 40, 50
}

free(arr);      // ALWAYS free when done! Prevents memory leaks
arr = NULL;     // Good practice — prevents dangling pointer

// calloc — like malloc but initializes all to 0
int *arr2 = (int*)calloc(5, sizeof(int));  // all 5 elements = 0
Unit 6: Structures & File Handling

📚 Structures — Custom Data Types

A structure groups different data types under one name. Unlike arrays (same type), structures can hold mixed types.

// Define a structure
struct Student {
    char name[50];     // string
    int roll;          // integer
    float marks;       // decimal
    char grade;        // single character
};

int main() {
    // Create a variable of type Student
    struct Student s1;

    // Assign values using dot (.) operator
    strcpy(s1.name, "Sajid");  // Must use strcpy for strings
    s1.roll = 101;
    s1.marks = 87.5;
    s1.grade = 'A';

    printf("Name: %s\n", s1.name);
    printf("Roll: %d\n", s1.roll);
    printf("Marks: %.1f\n", s1.marks);

    // Array of structures
    struct Student class[60];  // 60 student records!
    class[0].roll = 1;
    strcpy(class[0].name, "Rahul");
}

typedef — Create alias for easier typing:

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

Person p1;  // Now you can use "Person" instead of "struct Person"
p1.age = 20;

📚 File Handling — Reading & Writing Files

Files let you store data permanently (variables are lost when program ends).

File Modes:

Mode Meaning If file doesn't exist
"r" Read only Error (returns NULL)
"w" Write only (overwrites!) Creates new file
"a" Append (add to end) Creates new file
"r+" Read + Write Error
"w+" Read + Write (overwrites!) Creates new file
#include <stdio.h>

int main() {
    FILE *fp;  // File pointer

    // WRITE to a file
    fp = fopen("data.txt", "w");    // Opens (or creates) file for writing
    if (fp == NULL) {                // Always check!
        printf("Error opening file!\n");
        return 1;
    }
    fprintf(fp, "Name: Sajid\n");    // fprintf writes to file (like printf)
    fprintf(fp, "Age: 20\n");
    fclose(fp);                       // ALWAYS close when done!

    // READ from a file
    char line[100];
    fp = fopen("data.txt", "r");
    while (fgets(line, 100, fp) != NULL) {  // Read line by line
        printf("%s", line);                  // Print each line
    }
    fclose(fp);

    // Other useful functions:
    // fgetc(fp)       — read one character
    // fputc('A', fp)  — write one character
    // fscanf(fp, "%d", &n)  — read formatted data
    // feof(fp)        — check if end of file reached

    return 0;
}
Golden Rules: 1) Always check if fopen returns NULL. 2) Always fclose when done. 3) "w" mode ERASES existing content — use "a" to append!