Logic Development & Problem Solving
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:
- Problem Definition: Specifying exact user requirements and boundary constraints.
- Input-Output Specification: Identifying what data is provided (inputs) and what results must be computed (outputs).
- Algorithm Design: Formulating an ordered, step-by-step procedure to transform inputs into outputs.
- 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
0and1. 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–9and lettersA–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.
/* 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
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.
C Fundamentals & Character Set
1. The C Character Set
The C language character set consists of valid characters recognized by the C compiler:
- Letters: Uppercase
A–Zand lowercasea–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, andTOTALare separate identifiers. - Cannot be a reserved C keyword.
- Must start with a letter (
- 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
/* * 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 }
gcc first_program.c -o first_program, four discrete phases take place:
- Preprocessing: Expands
#includeheaders and#definemacros. - Compilation: Translates preprocessed C code into target Assembly code.
- Assembly: Converts Assembly into binary machine Object code (
.o/.obj). - Linking: Links object code with C runtime libraries to create the final executable.
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.
Data Types, 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 |
|---|---|---|---|
char | 1 byte (8 bits) | -128 to 127 | %c |
unsigned char | 1 byte (8 bits) | 0 to 255 | %c |
short int | 2 bytes (16 bits) | -32,768 to 32,767 | %hd |
int | 4 bytes (32 bits) | -2,147,483,648 to 2,147,483,647 | %d or %i |
unsigned int | 4 bytes (32 bits) | 0 to 4,294,967,295 | %u |
long int | 4 or 8 bytes | -9 quintillion to 9 quintillion | %ld |
float | 4 bytes (32 bits) | ~1.2E-38 to ~3.4E+38 (6 decimal precision) | %f |
double | 8 bytes (64 bits) | ~2.3E-308 to ~1.7E+308 (15 decimal precision) | %lf |
void | 0 bytes | Valueless / 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
#definedirective or theconstqualifier:#define PI 3.14159(Replaced via textual substitution before compilation)const double GRAVITY = 9.80665;(Compiler-enforced read-only variable)
#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; }
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.
Arithmetic, Unary & Relational Operators
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.
- Prefix (
- 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)
#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; }
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.
Logical, Bitwise & Conditional Operators
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).
- In
A && B, ifAevaluates to 0 (False),Bis never evaluated because the outcome is guaranteed to be 0. - In
A || B, ifAevaluates to non-zero (True),Bis 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$).
#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; }
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 %.
Expressions, Precedence & Library Functions
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).
#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; }
Evaluate the value of result = 5 + 3 * 2 >= 10 && 4 / 2 == 2 step-by-step using ANSI C operator precedence and associativity rules.
Data Input & Output (Formatted & Unformatted)
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:
%5dprints an integer in a field of at least 5 columns, right-justified. - Precision Specifier:
%.2frounds floating-point numbers to exactly 2 decimal places.
B. scanf() — Formatted Input
scanf("Format string", &var1, &var2, ...);
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 unsafegets()).puts(str): Writes a string to the console followed automatically by a newline character.
#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; }
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).
Decision Making (If-Else & Switch)
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: Anifstatement inside anotheriforelsebody. else-ifLadder: 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
breakstatement terminates execution of the switch body. - Without
break, execution falls through to subsequent cases until a break or the closing brace is encountered. - The
defaultlabel executes when no case matches.
#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; }
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.
Looping Statements (While & Do-While)
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.
#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; }
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).
The For Loop & Nested Loops
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
}
- Initialization: Evaluated only once at loop entry.
- Condition Test: Evaluated before every iteration. If False (0), loop terminates immediately.
- Body Execution: Statements inside
{ }are executed. - 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:
#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; }
Write a program using nested for loops that prints a centered pyramid of asterisks (*) for N lines.
Jumping Statements (Break & Continue)
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");
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).
#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; }
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.
Functions & Modular Programming
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
- 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); - Function Definition: The actual body of the function containing local variables and logic:
int calculateSum(int a, int b) { return a + b; } - 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!
#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; }
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.
Recursion in C
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) |
|---|---|---|
| Termination | Base case reached | Loop condition evaluates to false |
| Memory Usage | Higher (allocates a new stack frame per call) | Minimal (reuses fixed loop variables) |
| Code Readability | Clean and mathematical for tree/divide-and-conquer structures | Longer for hierarchical or nested divide-and-conquer logic |
| Risk | Stack Overflow if base case is missing | Infinite loop hanging the CPU |
#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; }
Write a recursive function int sumOfDigits(int n) that computes the sum of the digits of a number (e.g. sumOfDigits(432) = 9).
Arrays (1D & Multidimensional)
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
0tosize - 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.
#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; }
Write a program that takes a 3x3 matrix and computes its Transpose (swapping row and column elements).
Strings & String Manipulation
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 buffer | strcpy(name, "Aman"); |
strcat(dest, src) | Concatenates src to the end of dest | strcat(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 characters | strncpy(d, s, sizeof(d)-1); |
#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; }
Write your own implementation of strlen() and strcpy() without including <string.h> by traversing characters until the null character '\0' is reached.
Storage Classes (Auto, Extern, Static, Register)
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!
#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; }
Explain why attempting to apply the address-of operator (®Var) on a variable declared with register int regVar; causes a compilation error in C.
Pointers — Fundamentals & Memory Addresses
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
#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 if no valid address is assigned immediately: int *p = NULL;. Dereferencing an uninitialized (wild) or dangling pointer causes crashes or severe memory corruption.
Write a program that uses pointers to calculate the sum of two integers without using the variable names directly in the addition expression.
Pointers, Arrays & 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 toarr[i].- Pointer Arithmetic: When a pointer is incremented (
ptr++), C does not increment the address by 1 byte. It scales the increment bysizeof(*ptr)bytes! (e.g. For anint*, 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:
#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; }
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.
Structures & User-Defined Data Types
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.
#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; }
Define a struct Date (day, month, year) nested inside a struct Employee (id, name, salary, joiningDate). Initialize an employee and print their complete profile.
Structures with Pointers & 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) |
|---|---|---|
| Keyword | struct | union |
| Memory Allocation | Each member has its own dedicated memory offset | All members share the same base memory address |
| Total Size | Sum of sizes of all members (+ alignment padding) | Size of the largest member only |
| Member Access | All members can be used simultaneously | Only one member holds a valid value at any instant |
#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; }
Write a function void printStudent(const Student *s) that accepts a pointer to a student structure and displays its contents using the arrow operator (->).
File Handling & File Operations
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 aFILE*handle, orNULLif 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 text | Opens from beginning | Returns NULL (Error) |
"w" | Write text | Overwrites / Truncates to 0 | Creates new file |
"a" | Append text | Opens with cursor at end | Creates new file |
"r+" | Read & Write | Opens without truncating | Returns NULL |
"w+" | Write & Read | Overwrites / Truncates | Creates new file |
"rb" / "wb" | Binary read / write | Binary stream mode | Standard binary behavior |
#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; }
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.
Processing Data Files (Text & Binary)
1. Text File Processing Functions
- Character I/O:
fgetc(fp)andfputc(ch, fp). End of file is signaled byEOF(-1). - Line I/O:
fgets(buffer, size, fp)andfputs(str, fp). - Formatted File I/O:
fscanf(fp, "%d %s", &id, name)andfprintf(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 tofseek(fp, 0L, SEEK_SET)).
#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; }
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.