Lecture 1 / 22
Lecture 01 · Unit-I: Logic Development [CO1]

Logic Development & Problem Solving

[CO1] Course Outcome Problem Analysis Algorithms & Flowcharts

1. Problem Analysis & The Software Development Process

Before writing a single line of code in C, a programmer must rigorously analyze the computational problem. Problem Analysis consists of four distinct phases:

  1. Problem Definition: Specifying exact user requirements and boundary constraints.
  2. Input-Output Specification: Identifying what data is provided (inputs) and what results must be computed (outputs).
  3. Algorithm Design: Formulating an ordered, step-by-step procedure to transform inputs into outputs.
  4. Verification & Testing: Tracing the algorithm against edge cases before implementation.

2. Data Representation in Computing Systems

Computers store and process information in digital representations:

  • Binary (Base-2): Uses bits 0 and 1. Fundamental hardware language of CPU logic gates.
  • Octal (Base-8): Uses digits 0–7. Useful for compact byte representation (3 bits per octal digit). Prefix in C: 0 (e.g. 052).
  • Hexadecimal (Base-16): Uses digits 0–9 and letters A–F. Standard representation for memory addresses (4 bits per hex digit). Prefix in C: 0x (e.g. 0x2A).
  • ASCII Character Representation: 7-bit / 8-bit character encoding where characters map to numeric codes (e.g., 'A' = 65, 'a' = 97, '0' = 48).

3. Algorithms, Flowcharts & Pseudo-Code

An Algorithm is a finite, unambiguous sequence of well-defined computer-implementable instructions to solve a class of specific problems.

  • Pseudo-Code: A high-level, language-agnostic description of an algorithm combining natural language with programming constructs.
  • Flowcharts: Graphical representations of algorithmic logic using standard ANSI geometric symbols:
    • Oval (Terminal): Start and Stop endpoints.
    • Parallelogram (Input/Output): Reading input or printing results.
    • Rectangle (Process): Arithmetic computations and variable assignments.
    • Diamond (Decision): Conditional branching (True / False).
    • Circle (Connector): Connecting intersecting logic paths.
  • Decision Trees & Tables: Tabular models used to map complex combinatorial business conditions to concrete algorithmic actions.
algorithm_gcd.txt
/* Euclidean Algorithm for Greatest Common Divisor (GCD) */
Algorithm FindGCD(A, B):
    Input: Two non-zero positive integers A and B
    Output: Greatest common divisor
    Step 1: While B != 0 do:
    Step 2:     remainder = A % B
    Step 3:     A = B
    Step 4:     B = remainder
    Step 5: End While
    Step 6: Return A
🎯 Practice Challenge 0.1

Draw a flowchart and write pseudocode for an algorithm that checks whether a given year Y is a Leap Year according to the Gregorian calendar rules.

Lecture 02 · Unit-I: Fundamentals [CO1]

C Fundamentals & Character Set

[CO1] Course Outcome Tokens & Keywords C Syntax

1. The C Character Set

The C language character set consists of valid characters recognized by the C compiler:

  • Letters: Uppercase A–Z and lowercase a–z.
  • Digits: Decimal numerals 0–9.
  • Special Characters: , . : ; ? ' " ! | / \ ~ _ ^ % & * - + < > ( ) [ ] { } # $
  • White Space Characters: Blank space, horizontal tab (\t), newline (\n), vertical tab (\v), carriage return (\r).

2. Identifiers and Keywords

A C program is composed of tokens (the smallest individual elements):

  • Identifiers: User-defined names for variables, functions, and arrays.
    • Must start with a letter (a-z, A-Z) or an underscore (_).
    • Cannot contain spaces or special symbols.
    • Case-sensitive: Total, total, and TOTAL are separate identifiers.
    • Cannot be a reserved C keyword.
  • Keywords: 32 reserved words in ANSI C that have standardized meanings to the compiler:
    auto, break, case, char, const, continue, default, do, double, else, enum, extern, float, for, goto, if, int, long, register, return, short, signed, sizeof, static, struct, switch, typedef, union, unsigned, void, volatile, while.

3. Structure of a Canonical C Program

first_program.c
/* 
 * I.K. Gujral PTU BCA Curriculum - Programming in C
 * Canonical First Program Breakdown
 */
#include <stdio.h> // Preprocessor directive linking Standard I/O library

int main(void) {
    // Print string literal to standard output stream
    printf("Welcome to Problem Solving & Programming in C!\n");
    
    return 0; // Exit status 0 signifies successful execution
}
📘 Compilation Lifecycle in C
When you run gcc first_program.c -o first_program, four discrete phases take place:
  1. Preprocessing: Expands #include headers and #define macros.
  2. Compilation: Translates preprocessed C code into target Assembly code.
  3. Assembly: Converts Assembly into binary machine Object code (.o / .obj).
  4. Linking: Links object code with C runtime libraries to create the final executable.
🎯 Practice Challenge 1.1

Identify valid and invalid identifiers among: 2nd_val, _count, my-var, int, student_score$, TotalMarks. Explain why each is valid or invalid based on ANSI C rules.

Lecture 03 · Unit-I: Fundamentals [CO1]

Data Types, Constants & Variables

[CO1] Course Outcome Memory Allocation Constants & Variables

1. Primary Data Types in C

Data types specify how much memory to allocate and what kind of values can be stored in a variable:

Data Type Size (Typical 32/64-bit) Range Format Specifier
char1 byte (8 bits)-128 to 127%c
unsigned char1 byte (8 bits)0 to 255%c
short int2 bytes (16 bits)-32,768 to 32,767%hd
int4 bytes (32 bits)-2,147,483,648 to 2,147,483,647%d or %i
unsigned int4 bytes (32 bits)0 to 4,294,967,295%u
long int4 or 8 bytes-9 quintillion to 9 quintillion%ld
float4 bytes (32 bits)~1.2E-38 to ~3.4E+38 (6 decimal precision)%f
double8 bytes (64 bits)~2.3E-308 to ~1.7E+308 (15 decimal precision)%lf
void0 bytesValueless / generic pointer—

2. Variables and Symbolic Constants

  • Variables: Named memory locations holding data that can change during execution: int rollNumber = 101;
  • Literal Constants: Fixed values: integer constants (42, 052, 0x2A), real constants (3.14, 2.5e3), character constants ('A'), string constants ("C Language").
  • Symbolic Constants: Defined using the preprocessor #define directive or the const qualifier:
    • #define PI 3.14159 (Replaced via textual substitution before compilation)
    • const double GRAVITY = 9.80665; (Compiler-enforced read-only variable)
constants_variables.c
#include <stdio.h>

#define MAX_STUDENTS 60       // Symbolic constant via preprocessor
#define COLLEGE_NAME "IKGPTU"

int main(void) {
    const float PASSING_MARKS = 40.0f; // Symbolic constant via const
    int enrolled = 54;
    float averageScore = 78.5f;

    printf("Institution: %s\n", COLLEGE_NAME);
    printf("Capacity: %d | Enrolled: %d\n", MAX_STUDENTS, enrolled);
    printf("Passing Marks: %.1f | Class Average: %.2f\n", PASSING_MARKS, averageScore);

    return 0;
}
🎯 Practice Challenge 2.1

Write a program using sizeof operator to print the exact number of bytes allocated to char, short, int, long, float, and double on your machine.

Lecture 04 · Unit-I: Operations & Expressions [CO2]

Arithmetic, Unary & Relational Operators

[CO2] Course Outcome Arithmetic Unary & Relational

1. Arithmetic Operators

Arithmetic operators perform mathematical calculations on numerical operands:

  • + (Addition): a + b
  • - (Subtraction): a - b
  • * (Multiplication): a * b
  • / (Division): Integer division truncates towards zero (e.g. 7 / 2 = 3). Floating-point division preserves decimals (e.g. 7.0 / 2 = 3.5).
  • % (Modulus / Remainder): Requires integer operands (e.g. 7 % 3 = 1). The sign of the result matches the sign of the dividend.

2. Unary Operators in C

Unary operators operate on a single operand:

  • Unary Plus (+) & Unary Minus (-): Inverts algebraic sign: -x.
  • Increment (++) & Decrement (--):
    • Prefix (++x / --x): Increments/decrements the operand first, then yields the new value.
    • Postfix (x++ / x--): Yields the original value first, then increments/decrements the operand.
  • Address-of (&) & Indirection (*): Pointer operations (covered in Unit-IV).
  • Sizeof Operator (sizeof): Compile-time operator returning size in bytes: sizeof(int).

3. Relational Operators

Relational operators compare two values and produce an integer truth value: 1 for True, 0 for False.

  • == (Equal to) & != (Not equal to)
  • < (Less than) & > (Greater than)
  • <= (Less than or equal to) & >= (Greater than or equal to)
arithmetic_relational.c
#include <stdio.h>

int main(void) {
    int a = 10, b = 3;

    printf("Integer Division: %d / %d = %d\n", a, b, a / b);
    printf("Modulus Remainder: %d %% %d = %d\n", a, b, a % b);

    // Prefix vs Postfix Demonstration
    int x = 5, y = 5;
    printf("Postfix: x++ yields %d, then x becomes %d\n", x++, x);
    printf("Prefix:  ++y yields %d, and y is %d\n", ++y, y);

    // Relational truth value
    printf("Comparison (10 > 3): %d (True)\n", a > b);
    printf("Comparison (10 == 3): %d (False)\n", a == b);

    return 0;
}
🎯 Practice Challenge 3.1

Given int a = 4, b = 7; int c = ++a * b--; predict the values of a, b, and c. Write a C program to verify your calculation.

Lecture 05 · Unit-I: Operations & Expressions [CO2]

Logical, Bitwise & Conditional Operators

[CO2] Course Outcome Short-Circuit Logic Bitwise Operations

1. Logical Operators & Short-Circuit Evaluation

Logical operators combine relational expressions:

  • && (Logical AND): True if both operands evaluate to non-zero.
  • || (Logical OR): True if at least one operand evaluates to non-zero.
  • ! (Logical NOT): Inverts logical truth value (turns 0 to 1, non-zero to 0).
💡 Short-Circuit Evaluation Rule
In C, logical expressions evaluate from left to right:
  • In A && B, if A evaluates to 0 (False), B is never evaluated because the outcome is guaranteed to be 0.
  • In A || B, if A evaluates to non-zero (True), B is never evaluated because the outcome is guaranteed to be 1.

2. Assignment & Conditional (Ternary) Operators

  • Compound Assignment Operators: +=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>=.
  • Conditional (Ternary) Operator: The only three-operand operator in C:
    variable = (condition) ? value_if_true : value_if_false;

3. Bitwise Operators

Bitwise operators manipulate individual bits of integer data types directly:

  • & (Bitwise AND): Sets bit to 1 only if both corresponding bits are 1.
  • | (Bitwise OR): Sets bit to 1 if either bit is 1.
  • ^ (Bitwise XOR): Sets bit to 1 if bits are different.
  • ~ (Bitwise NOT / One's Complement): Inverts all bits.
  • << (Left Shift): Shifts bits left, filling with 0 (multiplies by $2^n$).
  • >> (Right Shift): Shifts bits right (divides by $2^n$).
bitwise_conditional.c
#include <stdio.h>

int main(void) {
    int n1 = 12; // binary: 00001100
    int n2 = 25; // binary: 00011001

    printf("Bitwise AND (12 & 25): %d\n", n1 & n2);   // 00001000 = 8
    printf("Bitwise OR  (12 | 25): %d\n", n1 | n2);   // 00011101 = 29
    printf("Bitwise XOR (12 ^ 25): %d\n", n1 ^ n2);   // 00010101 = 21
    printf("Left Shift  (12 << 1): %d\n", n1 << 1);   // 12 * 2 = 24

    // Conditional Operator
    int max = (n1 > n2) ? n1 : n2;
    printf("Maximum via Ternary: %d\n", max);

    return 0;
}
🎯 Practice Challenge 4.1

Using only the bitwise AND operator (&), write a C function to determine whether an integer n is even or odd without using the modulus operator %.

Lecture 06 · Unit-I: Operations & Expressions [CO2]

Expressions, Precedence & Library Functions

[CO2] Course Outcome Operator Precedence Standard Library

1. Operator Precedence and Associativity

When multiple operators appear in a single expression, precedence determines which operator is evaluated first. When operators have equal precedence, associativity dictates the direction of evaluation (Left-to-Right or Right-to-Left):

Precedence Level Operators Associativity
1 (Highest)() [] -> .Left to Right
2+ - (unary) ++ -- ! ~ * (deref) & (addr) sizeof (type)Right to Left
3* / %Left to Right
4+ - (binary)Left to Right
5<< >>Left to Right
6< <= > >=Left to Right
7== !=Left to Right
8& (bitwise AND)Left to Right
9^ (bitwise XOR)Left to Right
10| (bitwise OR)Left to Right
11&& (logical AND)Left to Right
12|| (logical OR)Left to Right
13?: (conditional)Right to Left
14= += -= *= /= %= &= |= ^= <<= >>=Right to Left
15 (Lowest), (comma operator)Left to Right

2. Type Conversions: Implicit & Explicit

  • Implicit Type Conversion (Type Promotion): The compiler automatically promotes lower types to higher types to prevent precision loss (e.g. int + float → float).
  • Explicit Type Casting: Forced conversion by the programmer using the cast operator: (target_type) expression.
    Example: float avg = (float) total / count;

3. Mathematical and Character Library Functions

Standard C library functions provide pre-compiled capabilities across common headers:

  • <math.h>: sqrt(x), pow(base, exp), abs(x), ceil(x), floor(x), sin(x), cos(x).
  • <ctype.h>: isalpha(c), isdigit(c), isalnum(c), isspace(c), toupper(c), tolower(c).
library_functions.c
#include <stdio.h>
#include <math.h>
#include <ctype.h>

int main(void) {
    double base = 5.0, exponent = 3.0;
    printf("pow(%.1f, %.1f) = %.2f\n", base, exponent, pow(base, exponent));
    printf("sqrt(144.0) = %.2f\n", sqrt(144.0));

    char ch = 'k';
    if (isalpha(ch)) {
        printf("'%c' is alphabetic. Uppercase: '%c'\n", ch, toupper(ch));
    }

    return 0;
}
🎯 Practice Challenge 5.1

Evaluate the value of result = 5 + 3 * 2 >= 10 && 4 / 2 == 2 step-by-step using ANSI C operator precedence and associativity rules.

Lecture 07 · Unit-II: Input & Output [CO3]

Data Input & Output (Formatted & Unformatted)

[CO3] Course Outcome Formatted I/O Character I/O

1. Formatted Input & Output Functions

Formatted I/O functions allow reading and writing data in user-specified styles, conversions, and field widths.

A. printf() — Formatted Output

printf("Format string", arg1, arg2, ...);

Common format specifiers: %d (integer), %f (float), %lf (double), %c (character), %s (string), %u (unsigned), %x (hexadecimal), %p (pointer address).

  • Width Specifier: %5d prints an integer in a field of at least 5 columns, right-justified.
  • Precision Specifier: %.2f rounds floating-point numbers to exactly 2 decimal places.

B. scanf() — Formatted Input

scanf("Format string", &var1, &var2, ...);
⚠️ The Address-Of Operator (&) in scanf
scanf() requires the memory address where the incoming input should be stored. Omitting the & symbol for non-pointer variables causes undefined behavior or segmentation faults! (Exception: Array names like strings decay into pointers, so & is omitted for string arrays).

2. Unformatted Input & Output Functions

Unformatted functions transfer single characters or raw string lines without format conversion:

  • getchar() / putchar(char c): Reads/writes a single character from standard input/output with buffering.
  • getch() / getche(): (Defined in <conio.h> on DOS/Windows) Reads a character immediately without waiting for the Enter key.
  • fgets(str, size, stdin): Safely reads an entire line of text including spaces until a newline or size limit. (Replaces unsafe gets()).
  • puts(str): Writes a string to the console followed automatically by a newline character.
formatted_unformatted.c
#include <stdio.h>

int main(void) {
    int rollNo;
    float marks;
    char grade;

    printf("Enter Roll Number and Marks (e.g. 101 88.5): ");
    scanf("%d %f", &rollNo, &marks);

    printf("\n--- Formatted Student Report ---\n");
    printf("Roll No: %05d\n", rollNo);      // Padded with leading zeros
    printf("Marks:   %7.2f%%\n", marks);    // Field width 7, 2 decimals

    return 0;
}
🎯 Practice Challenge 6.1

Write a program that uses getchar() inside a loop to count the total number of characters, spaces, and newline characters entered by the user until EOF (Ctrl+D / Ctrl+Z).

Lecture 08 · Unit-II: Control Statements [CO3]

Decision Making (If-Else & Switch)

[CO3] Course Outcome Conditional Branching Switch & Case

1. If, If-Else and Else-If Ladder

Conditional statements control branching based on truth evaluations (non-zero is True, 0 is False):

  • Simple if: Executes block if condition is true.
  • if-else: Chooses between two mutually exclusive blocks.
  • Nested if: An if statement inside another if or else body.
  • else-if Ladder: Evaluates conditions sequentially from top to bottom.

2. The Switch Statement

The switch statement tests an integral expression (int, char, or enum) against a series of constant case labels:

  • Case labels must be compile-time integer/character constants. Floating-point and string expressions are illegal in C switch.
  • The break statement terminates execution of the switch body.
  • Without break, execution falls through to subsequent cases until a break or the closing brace is encountered.
  • The default label executes when no case matches.
switch_menu.c
#include <stdio.h>

int main(void) {
    char op;
    double num1, num2;

    printf("Enter operator (+, -, *, /): ");
    scanf(" %c", &op);
    printf("Enter two numbers: ");
    scanf("%lf %lf", &num1, &num2);

    switch (op) {
        case '+':
            printf("Result: %.2f\n", num1 + num2);
            break;
        case '-':
            printf("Result: %.2f\n", num1 - num2);
            break;
        case '*':
            printf("Result: %.2f\n", num1 * num2);
            break;
        case '/':
            if (num2 != 0.0) {
                printf("Result: %.2f\n", num1 / num2);
            } else {
                printf("Error: Division by zero!\n");
            }
            break;
        default:
            printf("Error: Unrecognized operator '%c'\n", op);
    }

    return 0;
}
🎯 Practice Challenge 7.1

Using an else-if ladder, write a program that calculates electricity bill charges based on units consumed: first 100 units at Rs 3.00, next 200 units at Rs 4.50, and above 300 units at Rs 6.00.

Lecture 09 · Unit-II: Control Statements [CO3]

Looping Statements (While & Do-While)

[CO3] Course Outcome Iteration Constructs Entry vs Exit Controlled

1. The While Statement (Entry-Controlled Loop)

The while loop tests its condition before executing the loop body:

while (condition) {
    // statements executed as long as condition evaluates to non-zero
}

If the test condition is false at the very beginning, the body of a while loop is never executed.

2. The Do-While Statement (Exit-Controlled Loop)

The do-while loop tests its condition after executing the loop body:

do {
    // body of loop
} while (condition); // Note the required semicolon at the end!

Because the test occurs at the exit, a do-while loop is guaranteed to execute at least once. This makes it ideal for user-interactive menu driven programs.

loop_compare.c
#include <stdio.h>

int main(void) {
    // 1. Reverse an integer using while loop
    int num = 12345, reversed = 0, temp = num;
    while (temp > 0) {
        int digit = temp % 10;
        reversed = reversed * 10 + digit;
        temp /= 10;
    }
    printf("Original: %d | Reversed: %d\n", num, reversed);

    // 2. Menu-driven validation with do-while loop
    int choice;
    do {
        printf("\nMenu: 1. Start  2. Settings  3. Exit\n");
        printf("Enter your choice (1-3): ");
        scanf("%d", &choice);
    } while (choice < 1 || choice > 3);

    printf("Valid choice accepted: %d\n", choice);
    return 0;
}
🎯 Practice Challenge 8.1

Write a program using a while loop to determine whether an input integer N is a Palindrome (reads identically forwards and backwards, e.g. 12321).

Lecture 10 · Unit-II: Control Statements [CO3]

The For Loop & Nested Loops

[CO3] Course Outcome Count-Controlled Loop Nested Iterations

1. The For Loop

The for loop consolidates initialization, condition check, and loop variable update in a single compact header:

for (initialization; condition; update) {
    // loop body
}
📘 Lifecycle of a For Loop
  1. Initialization: Evaluated only once at loop entry.
  2. Condition Test: Evaluated before every iteration. If False (0), loop terminates immediately.
  3. Body Execution: Statements inside { } are executed.
  4. Update: Step increment/decrement occurs, then jumps back to Step 2.

2. Nested Loops in C

When a loop is placed inside the body of another loop, it is called a Nested Loop. For each single iteration of the outer loop, the inner loop executes through all its iterations:

nested_patterns.c
#include <stdio.h>

int main(void) {
    int rows = 5;

    printf("--- Floyd's Triangle Pattern ---\n");
    int counter = 1;
    for (int i = 1; i <= rows; i++) {
        for (int j = 1; j <= i; j++) {
            printf("%3d ", counter++);
        }
        printf("\n");
    }

    printf("\n--- Multiplication Table (1 to 5) ---\n");
    for (int r = 1; r <= 5; r++) {
        for (int c = 1; c <= 5; c++) {
            printf("%4d", r * c);
        }
        printf("\n");
    }

    return 0;
}
🎯 Practice Challenge 9.1

Write a program using nested for loops that prints a centered pyramid of asterisks (*) for N lines.

Lecture 11 · Unit-II: Control Statements [CO3]

Jumping Statements (Break & Continue)

[CO3] Course Outcome Unconditional Control Break, Continue & Goto

1. The Break Statement

The break statement causes an immediate exit from the innermost enclosing loop (for, while, do-while) or switch statement. Control passes to the statement directly following the loop.

2. The Continue Statement

The continue statement skips the remaining statements in the current iteration of a loop and passes control to the loop update/condition for the next iteration.

3. The Goto Statement & Labels

The goto statement performs an unconditional jump to a specified label within the same function:

goto error_handler;
...
error_handler:
    printf("Recovery routine executed.\n");
⚠️ Software Engineering Warning: Avoid Goto
Unrestricted use of goto creates spaghetti code that is difficult to trace, verify, and debug. Modern structured programming restricts goto strictly to exceptional error cleanup in low-level systems programming (e.g. Linux kernel drivers).
jumping_statements.c
#include <stdio.h>

int main(void) {
    printf("=== Prime Number Search using Break ===\n");
    int candidate = 29;
    int isPrime = 1;

    for (int i = 2; i * i <= candidate; i++) {
        if (candidate % i == 0) {
            isPrime = 0;
            break; // Found a divisor, terminate search immediately
        }
    }
    printf("%d is %s\n\n", candidate, isPrime ? "Prime" : "Not Prime");

    printf("=== Skipping Odd Numbers using Continue ===\n");
    for (int n = 1; n <= 10; n++) {
        if (n % 2 != 0) {
            continue; // Skip odd numbers
        }
        printf("Even number: %d\n", n);
    }

    return 0;
}
🎯 Practice Challenge 10.1

Write a program that prompts the user to enter 5 positive numbers. If a negative number is entered, use continue to skip it without adding to the running sum.

Lecture 12 · Unit-III: Functions [CO4]

Functions & Modular Programming

[CO4] Course Outcome Modular Design Function Prototypes

1. The Need for Functions

A function is a self-contained block of statements designed to perform a specific task. Functions divide a complex software application into smaller, manageable, testable, and reusable modules.

2. Function Elements in C

  1. Function Declaration (Prototype): Informs the compiler about the function name, return type, and argument parameter types before its invocation:
    int calculateSum(int a, int b);
  2. Function Definition: The actual body of the function containing local variables and logic:
    int calculateSum(int a, int b) {
        return a + b;
    }
  3. Function Call: Invokes the function by passing actual arguments:
    int total = calculateSum(10, 20);

3. Parameter Passing in C: Call by Value

In standard C, all scalar parameters are passed using Call by Value. A copy of the actual argument value is passed to the formal parameter inside the function's stack frame. Modifying formal parameters inside the function has no effect on the caller's variables!

function_modular.c
#include <stdio.h>

// Function Prototype
double computeSimpleInterest(double principal, double rate, int timeYears);

int main(void) {
    double p = 50000.0;
    double r = 7.5;
    int t = 3;

    double si = computeSimpleInterest(p, r, t);
    printf("Principal: Rs %.2f | Rate: %.1f%% | Time: %d years\n", p, r, t);
    printf("Calculated Simple Interest: Rs %.2f\n", si);

    return 0;
}

// Function Definition
double computeSimpleInterest(double principal, double rate, int timeYears) {
    return (principal * rate * timeYears) / 100.0;
}
🎯 Practice Challenge 11.1

Write a function int isPrime(int n) that returns 1 if n is a prime number and 0 otherwise. Use this function in main() to print all primes between 1 and 100.

Lecture 13 · Unit-III: Functions [CO4]

Recursion in C

[CO4] Course Outcome Recursive Functions Call Stack Tracing

1. Concept of Recursion

Recursion is a programming technique where a function calls itself directly or indirectly to solve a smaller sub-instance of the same problem. Every valid recursive function must have two components:

  • Base Case (Stopping Condition): The terminal state that returns a known value without making any further recursive calls, preventing infinite recursion and stack overflow.
  • Recursive Step: Reduces the problem closer to the base case.

2. Recursion vs. Iteration

Feature Recursion Iteration (Loops)
TerminationBase case reachedLoop condition evaluates to false
Memory UsageHigher (allocates a new stack frame per call)Minimal (reuses fixed loop variables)
Code ReadabilityClean and mathematical for tree/divide-and-conquer structuresLonger for hierarchical or nested divide-and-conquer logic
RiskStack Overflow if base case is missingInfinite loop hanging the CPU
recursion_demo.c
#include <stdio.h>

// Recursive Factorial Function
long long factorial(int n) {
    if (n <= 1) {
        return 1; // Base case
    }
    return n * factorial(n - 1); // Recursive step
}

// Recursive Fibonacci Function
int fibonacci(int term) {
    if (term <= 0) return 0;
    if (term == 1) return 1;
    return fibonacci(term - 1) + fibonacci(term - 2);
}

int main(void) {
    int num = 6;
    printf("Factorial of %d = %lld\n", num, factorial(num));

    printf("First 8 Fibonacci terms: ");
    for (int i = 0; i < 8; i++) {
        printf("%d ", fibonacci(i));
    }
    printf("\n");

    return 0;
}
🎯 Practice Challenge 12.1

Write a recursive function int sumOfDigits(int n) that computes the sum of the digits of a number (e.g. sumOfDigits(432) = 9).

Lecture 14 · Unit-III: Arrays [CO4]

Arrays (1D & Multidimensional)

[CO4] Course Outcome Homogeneous Storage 2D Matrices

1. One-Dimensional Arrays

An array is a collection of elements of the same data type stored in contiguous memory locations under a single variable name.

  • Declaration: int marks[5];
  • Zero-Based Indexing: Indices run from 0 to size - 1.
  • Initialization: int scores[5] = {85, 90, 78, 92, 88};

2. Passing Arrays to Functions

When an array is passed to a function, C does not copy the entire array. Instead, the array name decays into a pointer pointing to the first element (&arr[0]). Any modifications made inside the function directly alter the original array!

3. Multidimensional Arrays (2D Matrices)

A 2D array represents data in rows and columns: int matrix[3][3];. In C, elements are laid out in Row-Major Order in linear RAM.

array_matrix.c
#include <stdio.h>

// Function accepting 1D array and its size
int findArrayMax(const int arr[], int size) {
    int max = arr[0];
    for (int i = 1; i < size; i++) {
        if (arr[i] > max) {
            max = arr[i];
        }
    }
    return max;
}

int main(void) {
    int marks[5] = {45, 89, 92, 74, 83};
    printf("Maximum Marks: %d\n\n", findArrayMax(marks, 5));

    // 2D Matrix Addition
    int A[2][2] = {{1, 2}, {3, 4}};
    int B[2][2] = {{5, 6}, {7, 8}};
    int Sum[2][2];

    printf("--- Matrix Addition (A + B) ---\n");
    for (int i = 0; i < 2; i++) {
        for (int j = 0; j < 2; j++) {
            Sum[i][j] = A[i][j] + B[i][j];
            printf("%4d ", Sum[i][j]);
        }
        printf("\n");
    }

    return 0;
}
🎯 Practice Challenge 13.1

Write a program that takes a 3x3 matrix and computes its Transpose (swapping row and column elements).

Lecture 15 · Unit-III: Strings [CO3]

Strings & String Manipulation

[CO3] Course Outcome Null-Terminated Arrays string.h Library

1. Representation of Strings in C

In C, a string is not a separate primitive data type. Instead, it is an array of characters terminated by a special null character '\0' (ASCII value 0). The null character tells library functions where the string terminates in memory.

  • char str[6] = "Hello"; (Requires 6 bytes: 5 characters + 1 byte for '\0').
  • char str[] = {'H', 'i', '\0'};

2. Standard String Library Functions (<string.h>)

Function Description Example
strlen(s)Returns number of characters excluding '\0'strlen("BCA") → 3
strcpy(dest, src)Copies string src to destination bufferstrcpy(name, "Aman");
strcat(dest, src)Concatenates src to the end of deststrcat(s1, s2);
strcmp(s1, s2)Lexicographical comparison (<0 if s1<s2, 0 if equal, >0 if s1>s2)strcmp("A", "B") → -1
strncpy(dest, src, n)Safe copy up to n charactersstrncpy(d, s, sizeof(d)-1);
string_manipulation.c
#include <stdio.h>
#include <string.h>

int main(void) {
    char greeting[50] = "Hello";
    char university[] = " IKGPTU Jalandhar";

    printf("Original Length: %zu\n", strlen(greeting));

    // Concatenate
    strcat(greeting, university);
    printf("Concatenated: %s\n", greeting);

    // Comparison
    if (strcmp("Apple", "Banana") < 0) {
        printf(""Apple" comes alphabetically before "Banana"\n");
    }

    return 0;
}
🎯 Practice Challenge 14.1

Write your own implementation of strlen() and strcpy() without including <string.h> by traversing characters until the null character '\0' is reached.

Lecture 16 · Unit-III: Program Structure [CO3]

Storage Classes (Auto, Extern, Static, Register)

[CO3] Course Outcome Scope & Lifetime Storage Classes

1. What is a Storage Class?

A variable's storage class defines four critical attributes: its storage location (RAM stack, data segment, or CPU register), default initial value, scope (visibility), and lifetime (longevity).

2. Comparison of the 4 C Storage Classes

Storage Class Storage Location Default Initial Value Scope Lifetime
auto Stack RAM Garbage (unpredictable) Local to block Till block exits
register CPU Register Garbage Local to block Till block exits
static Data Segment (RAM) Zero (0) Local to block or file Throughout program execution
extern Data Segment (RAM) Zero (0) Global across files Throughout program execution

3. The Power of Static Variables

A local static variable inside a function is initialized only once and retains its value between successive calls to the function!

storage_classes.c
#include <stdio.h>

void counterFunction(void) {
    auto int autoVar = 1;      // Recreated and reinitialized on each call
    static int staticVar = 1;  // Initialized ONLY ONCE at startup

    printf("autoVar: %d | staticVar: %d\n", autoVar++, staticVar++);
}

int main(void) {
    printf("Call 1: "); counterFunction();
    printf("Call 2: "); counterFunction();
    printf("Call 3: "); counterFunction();

    return 0;
}
🎯 Practice Challenge 15.1

Explain why attempting to apply the address-of operator (&regVar) on a variable declared with register int regVar; causes a compilation error in C.

Lecture 17 · Unit-IV: Pointers [CO5]

Pointers — Fundamentals & Memory Addresses

[CO5] Course Outcome Memory Architecture Address & Dereference

1. Understanding Memory & Pointers

Computer memory (RAM) is organized as a sequential array of contiguous bytes, each with a unique hexadecimal memory address. A Pointer is a variable that stores the memory address of another variable.

2. Key Pointer Operators

  • Address-Of Operator (&): Unary operator that returns the memory address of its operand: &x.
  • Indirection / Dereference Operator (*): Unary operator that accesses or modifies the value stored at the memory address pointed to by the pointer: *ptr.

3. Declaration and Initialization

int count = 42;
int *ptr;       // Declaration: ptr is a pointer to an int
ptr = &count;   // Initialization: ptr stores the address of count
pointer_basics.c
#include <stdio.h>

int main(void) {
    int val = 500;
    int *ptr = &val; // ptr holds memory address of val

    printf("Direct Value of val:     %d\n", val);
    printf("Address of val (&val):    %p\n", (void*)&val);
    printf("Value of ptr:             %p\n", (void*)ptr);
    printf("Value at pointer (*ptr):  %d\n\n", *ptr);

    // Mutating variable indirectly through pointer
    *ptr = 999;
    printf("New Value of val after *ptr = 999: %d\n", val);

    return 0;
}
💡 NULL Pointers and Dangling Pointers
Always initialize pointers to NULL if no valid address is assigned immediately: int *p = NULL;. Dereferencing an uninitialized (wild) or dangling pointer causes crashes or severe memory corruption.
🎯 Practice Challenge 16.1

Write a program that uses pointers to calculate the sum of two integers without using the variable names directly in the addition expression.

Lecture 18 · Unit-IV: Pointers [CO5]

Pointers, Arrays & Call by Reference

[CO5] Course Outcome Pointer Arithmetic Call by Reference

1. The Intimate Relationship Between Pointers and Arrays

In C, the name of an array acts as a constant pointer to its first element: arr == &arr[0].

  • *(arr + i) is exactly identical to arr[i].
  • Pointer Arithmetic: When a pointer is incremented (ptr++), C does not increment the address by 1 byte. It scales the increment by sizeof(*ptr) bytes! (e.g. For an int*, incrementing advances the address by 4 bytes).

2. Simulating Call by Reference in C

Because C natively passes parameters by value, functions cannot modify the caller's variables unless the caller passes pointers (memory addresses) as arguments:

pointer_swap.c
#include <stdio.h>

// Simulating Call by Reference using pointers
void swapValues(int *x, int *y) {
    int temp = *x;
    *x = *y;
    *y = temp;
}

int main(void) {
    int a = 10, b = 20;
    printf("Before Swap: a = %d, b = %d\n", a, b);

    // Pass memory addresses of a and b
    swapValues(&a, &b);
    printf("After Swap:  a = %d, b = %d\n\n", a, b);

    // Traversing an array using a pointer
    int scores[4] = {88, 92, 79, 95};
    int *p = scores;

    printf("Array traversal using pointer arithmetic:\n");
    for (int i = 0; i < 4; i++) {
        printf("*(p + %d) at address %p = %d\n", i, (void*)(p + i), *(p + i));
    }

    return 0;
}
🎯 Practice Challenge 17.1

Write a function void findMinMax(int arr[], int size, int *min, int *max) that uses pointers to return both the minimum and maximum values of an array to the caller in a single function invocation.

Lecture 19 · Unit-IV: Structures & Unions [CO5]

Structures & User-Defined Data Types

[CO5] Course Outcome Heterogeneous Data typedef Keyword

1. Defining and Processing Structures

While arrays store homogeneous elements of the same data type, a Structure (struct) is a user-defined compound data type capable of packaging multiple related variables of different data types together under a single identifier.

2. Accessing Members with the Dot Operator (.)

Individual members of a structure variable are accessed using the member access operator (dot .): student1.gpa = 9.4;.

3. The Typedef Keyword

The typedef keyword creates a user-defined alias for existing types, allowing programmers to omit the redundant struct keyword during declarations.

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

// Define structure with typedef alias
typedef struct {
    int rollNo;
    char name[50];
    float gpa;
} Student;

int main(void) {
    // Array of 2 Student structures
    Student bcaClass[2];

    bcaClass[0].rollNo = 101;
    strcpy(bcaClass[0].name, "Harpreet Kaur");
    bcaClass[0].gpa = 9.2f;

    bcaClass[1].rollNo = 102;
    strcpy(bcaClass[1].name, "Rajesh Sharma");
    bcaClass[1].gpa = 8.6f;

    printf("=== BCA Student Records ===\n");
    for (int i = 0; i < 2; i++) {
        printf("Roll: %d | Name: %-15s | GPA: %.2f\n", 
               bcaClass[i].rollNo, bcaClass[i].name, bcaClass[i].gpa);
    }

    return 0;
}
🎯 Practice Challenge 18.1

Define a struct Date (day, month, year) nested inside a struct Employee (id, name, salary, joiningDate). Initialize an employee and print their complete profile.

Lecture 20 · Unit-IV: Structures & Unions [CO5]

Structures with Pointers & Unions

[CO5] Course Outcome Arrow Operator Structures vs Unions

1. Structures and Pointers (The Arrow Operator ->)

When working with a pointer to a structure, accessing its members through (*ptr).member can become verbose. C provides the Arrow Operator (->) as an elegant shorthand:

Student s;
Student *ptr = &s;
ptr->rollNo = 105; // Equivalent to (*ptr).rollNo = 105;

2. Passing Structures to Functions

  • Passing by Value: Passes a full copy of the structure. For large structures, this wastes memory and CPU copying time.
  • Passing by Pointer (Reference): Passes only the memory address (4 or 8 bytes) of the structure, maximizing performance.

3. Unions in C: Memory Sharing

A Union is declared similarly to a structure, but with a fundamental architectural difference: all members share the exact same memory space. The size of a union is simply the size of its largest member.

Feature Structure (struct) Union (union)
Keywordstructunion
Memory AllocationEach member has its own dedicated memory offsetAll members share the same base memory address
Total SizeSum of sizes of all members (+ alignment padding)Size of the largest member only
Member AccessAll members can be used simultaneouslyOnly one member holds a valid value at any instant
struct_union_demo.c
#include <stdio.h>

typedef struct {
    int id;
    float weight;
} StructItem;

typedef union {
    int id;
    float weight;
} UnionItem;

int main(void) {
    printf("Size of StructItem: %zu bytes (4 + 4)\n", sizeof(StructItem));
    printf("Size of UnionItem:  %zu bytes (shares 4 bytes)\n\n", sizeof(UnionItem));

    UnionItem u;
    u.id = 1001;
    printf("After writing u.id: id = %d\n", u.id);

    u.weight = 75.5f; // Overwrites the shared memory bytes!
    printf("After writing u.weight: weight = %.1f, id is corrupted = %d\n", u.weight, u.id);

    return 0;
}
🎯 Practice Challenge 19.1

Write a function void printStudent(const Student *s) that accepts a pointer to a student structure and displays its contents using the arrow operator (->).

Lecture 21 · Unit-IV: File Handling [CO5]

File Handling & File Operations

[CO5] Course Outcome Persistent Storage FILE Pointer

1. The Concept of Files in C

Standard program data in variables and arrays resides in volatile RAM and is lost when the program terminates. Files provide permanent secondary storage on hard disks.

2. The FILE Structure & Opening Modes

All file handling in C is coordinated through a pointer to the FILE structure defined in <stdio.h>: FILE *fp;.

  • fopen("filename", "mode"): Opens a file and returns a FILE* handle, or NULL if opening fails.
  • fclose(fp): Flushes buffers and closes the file stream.
File Mode Meaning If File Already Exists If File Does Not Exist
"r"Read textOpens from beginningReturns NULL (Error)
"w"Write textOverwrites / Truncates to 0Creates new file
"a"Append textOpens with cursor at endCreates new file
"r+"Read & WriteOpens without truncatingReturns NULL
"w+"Write & ReadOverwrites / TruncatesCreates new file
"rb" / "wb"Binary read / writeBinary stream modeStandard binary behavior
file_open_close.c
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    FILE *fp = fopen("test_output.txt", "w");
    if (fp == NULL) {
        perror("Failed to open file");
        return EXIT_FAILURE;
    }

    fprintf(fp, "I.K. Gujral PTU BCA Java & C Programming\n");
    fprintf(fp, "File operations verified successfully.\n");

    fclose(fp); // Crucial: flush data to disk
    printf("File written and closed successfully.\n");

    return EXIT_SUCCESS;
}
🎯 Practice Challenge 20.1

Write a program that attempts to open a file in "r" mode. If the file does not exist, display an error message using perror() and exit gracefully.

Lecture 22 · Unit-IV: File Handling [CO5]

Processing Data Files (Text & Binary)

[CO5] Course Outcome Formatted & Binary I/O Random Access Files

1. Text File Processing Functions

  • Character I/O: fgetc(fp) and fputc(ch, fp). End of file is signaled by EOF (-1).
  • Line I/O: fgets(buffer, size, fp) and fputs(str, fp).
  • Formatted File I/O: fscanf(fp, "%d %s", &id, name) and fprintf(fp, ...).

2. Binary File Processing (fread & fwrite)

Binary I/O transfers memory blocks directly to/from disk without ASCII text conversion:

fwrite(&record, sizeof(Student), 1, fp);
fread(&record, sizeof(Student), 1, fp);

3. Random Access File Functions

Normal file processing reads sequentially from beginning to end. C provides random access navigation:

  • fseek(fp, offset, origin): Moves file pointer. Origins:
    • SEEK_SET (0): Beginning of file.
    • SEEK_CUR (1): Current position.
    • SEEK_END (2): End of file.
  • ftell(fp): Returns current byte offset from file beginning.
  • rewind(fp): Resets pointer back to beginning (equivalent to fseek(fp, 0L, SEEK_SET)).
process_data_file.c
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    FILE *fp = fopen("students.dat", "w+"); // Open for reading and writing
    if (!fp) { return 1; }

    // Write structured records
    fprintf(fp, "101 Simran 92.5\n");
    fprintf(fp, "102 Vikram 84.0\n");
    fprintf(fp, "103 Ananya 95.0\n");

    // Reset pointer to beginning
    rewind(fp);

    printf("=== Reading Records from File ===\n");
    int id;
    char name[30];
    float marks;

    while (fscanf(fp, "%d %s %f", &id, name, &marks) == 3) {
        printf("ID: %d | Name: %-10s | Score: %.1f\n", id, name, marks);
    }

    // Query file size via fseek and ftell
    fseek(fp, 0L, SEEK_END);
    long fileSize = ftell(fp);
    printf("\nTotal File Size: %ld bytes\n", fileSize);

    fclose(fp);
    return 0;
}
🎯 Practice Challenge 21.1

Write a program that copies the contents of an existing file source.txt into a new file destination.txt character-by-character using fgetc() and fputc() until EOF.