UNIT III: Recursion, Pointers & Memory 1. Recursion Definition: A function calling itself directly or indirectly. Essential components: base case (stopping condition) and recursive step. Example (Factorial): int factorial(int n) { if (n == 0) // Base case return 1; else // Recursive step return n * factorial(n - 1); } 2. Factorial Program (Recursive) #include <stdio.h> long int factorial(int n) { if (n >= 1) return n * factorial(n - 1); else return 1; } int main() { int num; printf("Enter a positive integer: "); scanf("%d", &num); printf("Factorial of %d = %ld\n", num, factorial(num)); return 0; } 3. Fibonacci Series (Recursive) #include <stdio.h> int fibonacci(int n) { if (n == 0) return 0; else if (n == 1) return 1; else return fibonacci(n - 1) + fibonacci(n - 2); } int main() { int n, i; printf("Enter the number of terms: "); scanf("%d", &n); printf("Fibonacci Series:\n"); for (i = 0; i < n; i++) { printf("%d ", fibonacci(i)); } printf("\n"); return 0; } 4. Pointer Arithmetic Operations allowed: addition/subtraction of integers, subtraction of two pointers of the same type, comparison. Rules: Adding $n$ to a pointer $p$: $p + n$ points to the $n$-th element after $p$. Address changes by $n \times \text{sizeof(*p)}$. Subtracting $n$ from $p$: $p - n$ points to the $n$-th element before $p$. Subtracting $p_1 - p_2$: gives the number of elements between $p_1$ and $p_2$. Example: int arr[] = {10, 20, 30, 40, 50}; int *ptr = arr; // ptr points to arr[0] printf("%d\n", *ptr); // Output: 10 printf("%d\n", *(ptr + 1)); // Output: 20 (ptr moves by sizeof(int)) ptr++; // ptr now points to arr[1] printf("%d\n", *ptr); // Output: 20 5. Dynamic Memory Allocation Allocating memory at runtime using functions from `stdlib.h`. Functions: malloc() : Allocates a block of specified size (in bytes) and returns a void pointer to the beginning of the block. Returns `NULL` on failure. calloc() : Allocates memory for an array of $n$ elements of specified size, initializes all bytes to zero, and returns a void pointer. realloc() : Changes the size of the memory block pointed to by `ptr` to the specified new size. free() : Deallocates the memory block previously allocated by `malloc`, `calloc`, or `realloc`. Syntax: ptr = (cast_type *) malloc(size_in_bytes); ptr = (cast_type *) calloc(num_elements, element_size_in_bytes); ptr = (cast_type *) realloc(ptr, new_size_in_bytes); free(ptr); 6. malloc() vs. calloc() Feature malloc() calloc() Arguments Takes one argument: size in bytes. E.g., malloc(10 * sizeof(int)) Takes two arguments: number of elements, size of each element. E.g., calloc(10, sizeof(int)) Initialization Allocated memory contains garbage values. Allocated memory is initialized to zero. Return Type void* (must be cast to desired pointer type) Purpose General-purpose memory allocation. Primarily for allocating memory for arrays and initializing. 7. Storage Classes Determine the scope, lifetime, initial value, and storage location of a variable. auto : Default for local variables. Scope: Local to the block. Lifetime: Until the block terminates. Initial value: Garbage. Storage: CPU register or RAM. register : Suggests compiler store variable in a CPU register for faster access. Scope: Local to the block. Lifetime: Until the block terminates. Initial value: Garbage. Storage: CPU register (if available), otherwise RAM. Can't take address of a register variable. static : Local static: Retains value between function calls. Scope: Local. Lifetime: Throughout program. Initial value: 0. Global static: Scope limited to the file where it's declared. Lifetime: Throughout program. Initial value: 0. Storage: Data segment of RAM. extern : Declares a global variable defined in another file or later in the same file. Scope: Global. Lifetime: Throughout program. Initial value: 0. Storage: Data segment of RAM. UNIT IV: Strings & Structures 1. String Handling Functions ( <string.h> ) strrev() : Reverses a string. With: strrev(str); Without: Loop from both ends, swapping characters until middle. strncpy(dest, src, n) : Copies $n$ characters of `src` to `dest`. With: strncpy(dest, src, 5); dest[5] = '\0'; (manual null termination often needed) Without: Loop $i$ from 0 to $n-1$, dest[i] = src[i]; , then null terminate. strcpy(dest, src) : Copies `src` string to `dest`, including null terminator. With: strcpy(dest, src); Without: Loop until `src[i] == '\0'`, copying `dest[i] = src[i];`, then `dest[i] = '\0';`. strcat(dest, src) : Appends `src` to `dest`. With: strcat(dest, src); Without: Find end of `dest`, then copy `src` from that point. strcmp(s1, s2) : Compares strings `s1` and `s2`. Returns 0 if equal, <0 if `s1` < `s2`, >0 if `s1` > `s2`. With: if (strcmp(s1, s2) == 0) Without: Loop, comparing `s1[i]` with `s2[i]`. If different, return difference. If one ends, return difference. strncat(dest, src, n) : Appends $n$ characters of `src` to `dest`. With: strncat(dest, src, 3); Without: Find end of `dest`, then copy up to $n$ characters from `src`. strlen(s) : Returns the length of string `s` (excluding null terminator). With: len = strlen(str); Without: Loop with a counter until `str[i] == '\0'`. strncmp(s1, s2, n) : Compares first $n$ characters of `s1` and `s2`. With: if (strncmp(s1, s2, 5) == 0) Without: Loop $i$ from 0 to $n-1$, comparing `s1[i]` with `s2[i]`. 6. Structures Definition: A user-defined data type that groups variables of different data types under a single name. Declaration: struct Student { char name[50]; int roll_no; float marks; }; Variable Declaration: struct Student s1; struct Student s2 = {"Alice", 101, 85.5}; Accessing Members: Using the dot operator ( . ) for structure variables, or arrow operator ( -> ) for pointers to structures. s1.roll_no = 10; printf("%s", s1.name); struct Student *ptr = &s1; ptr->marks = 90.0; 7. Passing Structure to Function (Pass by Value) When a structure is passed by value, a copy of the entire structure is made and passed to the function. Changes made to the structure within the function do not affect the original structure. Example: struct Point { int x, y; }; void printPoint(struct Point p) { printf("Point: (%d, %d)\n", p.x, p.y); p.x = 100; // This change is local to the function } int main() { struct Point p1 = {10, 20}; printPoint(p1); printf("Original Point: (%d, %d)\n", p1.x, p1.y); // Still (10, 20) return 0; } Pass by reference (using pointers) is generally preferred for efficiency, especially with large structures. 8. Structures vs. Arrays Feature Structure Array Data Types Can store elements of different data types. Stores elements of the same data type. Access Members accessed by name using . or -> . Elements accessed by index. Concept Collection of logically related data items. Collection of similar data items. Memory Members stored in contiguous memory, but padding might occur. Elements stored in contiguous memory locations. Size Size is sum of individual member sizes (plus padding). Size is number of elements * size of one element. 9. Structures vs. Unions Feature Structure Union Keyword struct union Memory Allocation Each member has its own unique memory location. Total size is sum of member sizes (plus padding). All members share the same memory location. Total size is the size of the largest member. Value Access All members can hold values simultaneously. Only one member can hold a value at any given time (the last one assigned). Purpose To group related data of different types. To save memory when only one member's value is needed at a time. 10. typedef and Unions typedef : Creates an alias (new name) for an existing data type. Improves readability. Syntax: typedef existing_type new_name; Example: typedef unsigned long int ULONG; ULONG distance = 1000000; typedef struct Student { char name[50]; int id; } Student_t; // New name for struct Student Student_t s1; // Now we can use Student_t instead of struct Student Unions: (See point 9 for detailed explanation) Used for memory optimization where different data types need to share the same memory space. Only one member can be active at a time. 11. Student Details Program (Structure) #include <stdio.h> #include <string.h> struct Student { char name[50]; int roll_no; float marks; }; void addStudent(struct Student *s) { printf("Enter student name: "); scanf("%s", s->name); printf("Enter roll number: "); scanf("%d", &s->roll_no); printf("Enter marks: "); scanf("%f", &s->marks); } void viewStudent(struct Student s) { printf("\n--- Student Details ---\n"); printf("Name: %s\n", s.name); printf("Roll No: %d\n", s.roll_no); printf("Marks: %.2f\n", s.marks); } int main() { struct Student s; addStudent(&s); // Pass by reference to modify original structure viewStudent(s); // Pass by value return 0; } UNIT V: Files, CLI, Search & Sort 1. Command Line Arguments Definition: Values passed to the main function when the program is executed from the command line. Accessed via main function parameters: int main(int argc, char *argv[]) argc (argument count): Number of arguments passed (program name + actual arguments). argv (argument vector): An array of strings, where each string is one argument. argv[0] is the program name. Sum Program: #include <stdio.h> #include <stdlib.h> // For atoi() int main(int argc, char *argv[]) { if (argc != 3) { printf("Usage: %s <num1> <num2>\n", argv[0]); return 1; // Indicate error } int num1 = atoi(argv[1]); // Convert string to integer int num2 = atoi(argv[2]); int sum = num1 + num2; printf("Sum of %d and %d is: %d\n", num1, num2, sum); return 0; } 2. File Modes in C Used with fopen() function. Text Modes: "r" : Read mode. File must exist. "w" : Write mode. Creates new file or truncates existing. "a" : Append mode. Creates new file or appends to existing. "r+" : Read and write mode. File must exist. "w+" : Read and write mode. Creates new file or truncates existing. "a+" : Read and append mode. Creates new file or appends to existing. Binary Modes: Add 'b' to text modes (e.g., "rb" , "wb" , "ab+" ). Used for non-text data, where no character translation occurs. 3. File Operations in C Opening: FILE *fp = fopen("filename.txt", "mode"); (Returns `NULL` on failure) Closing: fclose(fp); Reading Character: char ch = fgetc(fp); Writing Character: fputc(ch, fp); Reading String: fgets(buffer, size, fp); Writing String: fputs(buffer, fp); Formatted Read: fscanf(fp, "%d %s", &num, str); Formatted Write: fprintf(fp, "Name: %s, Age: %d", name, age); Random Access: fseek(fp, offset, origin); ( SEEK_SET , SEEK_CUR , SEEK_END ) ftell(fp); (Returns current file position) rewind(fp); (Sets file position to beginning) 4. Copy File Contents Program #include <stdio.h> #include <stdlib.h> int main() { FILE *source_fp, *dest_fp; char ch; source_fp = fopen("source.txt", "r"); if (source_fp == NULL) { printf("Error opening source file.\n"); return 1; } dest_fp = fopen("destination.txt", "w"); if (dest_fp == NULL) { printf("Error opening destination file.\n"); fclose(source_fp); return 1; } while ((ch = fgetc(source_fp)) != EOF) { fputc(ch, dest_fp); } printf("File copied successfully.\n"); fclose(source_fp); fclose(dest_fp); return 0; } 5. Print File Contents to Console Program #include <stdio.h> #include <stdlib.h> int main() { FILE *fp; char ch; fp = fopen("my_file.txt", "r"); if (fp == NULL) { printf("Error opening file.\n"); return 1; } printf("Contents of the file:\n"); while ((ch = fgetc(fp)) != EOF) { putchar(ch); } fclose(fp); return 0; } 6. Count Chars, Words, Lines in File Program #include <stdio.h> #include <ctype.h> // For isspace() int main() { FILE *fp; char ch; int chars = 0, words = 0, lines = 0; int in_word = 0; // Flag to track if currently inside a word fp = fopen("sample.txt", "r"); if (fp == NULL) { printf("Error opening file.\n"); return 1; } while ((ch = fgetc(fp)) != EOF) { chars++; if (ch == '\n') { lines++; } if (isspace(ch)) { in_word = 0; } else if (in_word == 0) { words++; in_word = 1; } } // If the file does not end with a newline, increment lines if (chars > 0 && ch == EOF) { lines++; } printf("Characters: %d\n", chars); printf("Words: %d\n", words); printf("Lines: %d\n", lines); fclose(fp); return 0; } 7. Linear Search Program #include <stdio.h> int linearSearch(int arr[], int n, int key) { for (int i = 0; i < n; i++) { if (arr[i] == key) { return i; // Key found at index i } } return -1; // Key not found } int main() { int arr[] = {10, 25, 4, 30, 8, 15}; int n = sizeof(arr) / sizeof(arr[0]); int key = 30; int result = linearSearch(arr, n, key); if (result != -1) { printf("Element %d found at index %d.\n", key, result); } else { printf("Element %d not found.\n", key); } return 0; } 8. Binary Search Program #include <stdio.h> int binarySearch(int arr[], int low, int high, int key) { while (low <= high) { int mid = low + (high - low) / 2; if (arr[mid] == key) { return mid; // Key found } if (arr[mid] < key) { low = mid + 1; // Search in right half } else { high = mid - 1; // Search in left half } } return -1; // Key not found } int main() { int arr[] = {4, 8, 10, 15, 25, 30}; // Array must be sorted for binary search int n = sizeof(arr) / sizeof(arr[0]); int key = 15; int result = binarySearch(arr, 0, n - 1, key); if (result != -1) { printf("Element %d found at index %d.\n", key, result); } else { printf("Element %d not found.\n", key); } return 0; } 9. Linear vs. Binary Search Methodology Linear Search: Method: Sequentially checks each element of the list until a match is found or the end of the list is reached. Requirement: No specific order needed for the list. Complexity: $O(n)$ in worst case. Binary Search: Method: Works on sorted arrays. It repeatedly divides the search interval in half. Compares the target value with the middle element. If they are not equal, the half in which the target cannot lie is eliminated, and the search continues on the remaining half. Requirement: The list *must* be sorted. Complexity: $O(\log n)$ in worst case. 10. Compare Linear and Binary Search Feature Linear Search Binary Search Pre-condition No requirement for data order. Data must be sorted. Efficiency Less efficient for large datasets. Much more efficient for large datasets. Time Complexity (Worst) $O(n)$ $O(\log n)$ Implementation Simple to implement. More complex to implement. Use Case Small, unsorted lists. Large, sorted lists. 11. Bubble Sort Program #include <stdio.h> void bubbleSort(int arr[], int n) { for (int i = 0; i < n - 1; i++) { for (int j = 0; j < n - i - 1; j++) { if (arr[j] > arr[j + 1]) { // Swap arr[j] and arr[j+1] int temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } } int main() { int arr[] = {53, 2, 5, 10, 9, 85, 43, 8, 129}; int n = sizeof(arr) / sizeof(arr[0]); bubbleSort(arr, n); printf("Sorted array: "); for (int i = 0; i < n; i++) { printf("%d ", arr[i]); } printf("\n"); return 0; } 12. Bubble Sort Step-by-Step Example Array: $[53, 2, 5, 10, 9, 85, 43, 8, 129]$ Pass 1: Compare adjacent elements and swap if out of order. Largest element bubbles to the end. $[2, 53, 5, 10, 9, 85, 43, 8, 129]$ (53 vs 2) $[2, 5, 53, 10, 9, 85, 43, 8, 129]$ (53 vs 5) ... $[2, 5, 9, 10, 43, 8, 53, 85, 129]$ (85 vs 43, 85 vs 8, 85 vs 129 - 129 is largest, so it's at the end) After Pass 1: $[2, 5, 9, 10, 43, 8, 53, 85, \underline{129}]$ Pass 2: Repeat, ignoring the last element (129). ... (85 bubbles to second last position) After Pass 2: $[2, 5, 9, 10, 8, 43, 53, \underline{85}, \underline{129}]$ ... (Continue for $n-1$ passes) Final Sorted Array: $[2, 5, 8, 9, 10, 43, 53, 85, 129]$ 13. Insertion Sort Step-by-Step Example Array: $[53, 2, 5, 10, 9, 85, 43, 8, 129]$ Start with the second element (2). Compare with elements to its left and insert in correct position. Initial: $[\underline{53}, 2, 5, 10, 9, 85, 43, 8, 129]$ (53 is sorted subarray) Step 1 (Element 2): 2 is smaller than 53. Shift 53 right, insert 2. $[2, \underline{53}, 5, 10, 9, 85, 43, 8, 129]$ -> $[2, 53, 5, 10, 9, 85, 43, 8, 129]$ Step 2 (Element 5): 5 is smaller than 53, but larger than 2. Shift 53 right, insert 5. $[2, 5, \underline{53}, 10, 9, 85, 43, 8, 129]$ Step 3 (Element 10): 10 is smaller than 53, larger than 5. Shift 53 right, insert 10. $[2, 5, 10, \underline{53}, 9, 85, 43, 8, 129]$ Step 4 (Element 9): 9 is smaller than 53, 10. Shift 53, 10 right, insert 9. $[2, 5, 9, 10, \underline{53}, 85, 43, 8, 129]$ Continue this process. For each element, shift larger elements to its right in the sorted subarray and insert it. Final Sorted Array: $[2, 5, 8, 9, 10, 43, 53, 85, 129]$ 14. Selection Sort Step-by-Step Example Array: $[53, 2, 5, 10, 9, 85, 43, 8, 129]$ Pass 1: Find the minimum element in the unsorted part and swap it with the first element of the unsorted part. Minimum in $[53, 2, 5, 10, 9, 85, 43, 8, 129]$ is 2. Swap 2 and 53: $[\underline{2}, 53, 5, 10, 9, 85, 43, 8, 129]$ Pass 2: Find the minimum in the remaining unsorted part (from index 1) and swap it with the element at index 1. Unsorted: $[53, 5, 10, 9, 85, 43, 8, 129]$. Minimum is 5. Swap 5 and 53: $[2, \underline{5}, 53, 10, 9, 85, 43, 8, 129]$ Pass 3: Find the minimum in the remaining unsorted part (from index 2) and swap it with the element at index 2. Unsorted: $[53, 10, 9, 85, 43, 8, 129]$. Minimum is 8. Swap 8 and 53: $[2, 5, \underline{8}, 10, 9, 85, 43, 53, 129]$ Continue this process for $n-1$ passes. Final Sorted Array: $[2, 5, 8, 9, 10, 43, 53, 85, 129]$ 15. Insertion Sort Program #include <stdio.h> void insertionSort(int arr[], int n) { int i, key, j; for (i = 1; i < n; i++) { key = arr[i]; // Element to be inserted j = i - 1; // Move elements of arr[0..i-1], that are greater than key, // to one position ahead of their current position while (j >= 0 && arr[j] > key) { arr[j + 1] = arr[j]; j = j - 1; } arr[j + 1] = key; // Insert key in its correct position } } int main() { int arr[] = {53, 2, 5, 10, 9, 85, 43, 8, 129}; int n = sizeof(arr) / sizeof(arr[0]); insertionSort(arr, n); printf("Sorted array: "); for (int i = 0; i < n; i++) { printf("%d ", arr[i]); } printf("\n"); return 0; } 16. Selection Sort Program #include <stdio.h> void selectionSort(int arr[], int n) { int i, j, min_idx; for (i = 0; i < n - 1; i++) { min_idx = i; // Assume current element is minimum for (j = i + 1; j < n; j++) { if (arr[j]