C Programming Reference

Open-book midterm cheat sheet — all key topics with examples

🏗️

C Program Structure

Every program's skeleton — main, includes, comments

Minimal Valid Program
// Every C program MUST have main() — execution starts here
#include <stdio.h>   // include standard I/O library (gives printf, scanf)
#include <string.h>  // needed for strlen(), strcmp()

int main(void) {
    printf("Hello, world!\n");
    return 0;  // 0 = success; main() must return int
}

/* Multi-line comment
   stripped by preprocessor — zero effect on execution speed */
Rule: #include directives go at the top, before any code. Only main() is mandatory — #include is optional (but you'll almost always need stdio.h).
Escape Sequences
SequenceMeaning
\nnewline
\ttab
\\backslash
\"double quote
\'single quote
\0null terminator (ASCII 0)
Common Errors
  • Syntax error — bad grammar, caught at compile time (e.g. missing ;)
  • Semantic error — code compiles but logic is wrong (e.g. wrong operator)
  • Runtime error — crashes during execution
⚠ Common Exam Traps — Read First
TrapWrongRight
Assignment vs equalityif (x = 5) — always true!if (x == 5)
Missing & in scanfscanf("%d", x)scanf("%d", &x)
double in scanfscanf("%f", &d)scanf("%lf", &d)
Integer divisionint/int = int: 7/2 = 3(float)7 / 2 = 3.5
Chained comparison1 < x < 10 always truex > 1 && x < 10
Missing break in switchfalls through to next caseadd break; each case
String sizechar s[5] = "hello" — no room for \0!char s[6] for 5 chars
Compare stringsif (s == "hi") — compares addressesstrcmp(s, "hi") == 0
Return value usedvoid f() { return 5; }use int f()
Post vs pre-incrementprintf("%d", x++) prints OLD xprintf("%d", ++x) prints NEW x
📦

Data Types, Variables & I/O

printf, scanf, format specifiers, #define

Basic Types
TypeSizeExample valueprintf / scanf
int4 bytes42, -7%d
float4 bytes3.14f%f / %f
double8 bytes3.14%f / %lf
char1 byte'A', 65%c
char[]n bytes"hello"%s
scanf needs &: Always put & before scalar variables in scanf. Arrays (like char str[]) don't need it — the array name is already an address.
Format Specifiers — Full Reference
SpecifierUse forExample codeOutput
%dintprintf("%d", 42)42
%ffloat / double (printf)printf("%f", 3.14)3.140000
%.2ffloat, 2 decimal placesprintf("%.2f", 3.14159)3.14
%escientific notationprintf("%e", 31400.0)3.140000e+04
%lfdouble (scanf only)scanf("%lf", &d)
%csingle charprintf("%c", 'A')A
%d with charchar as integer (ASCII)printf("%d", 'A')65
%sstring (char array)printf("%s", "hi")hi
%%literal percent signprintf("100%%")100%
printf & scanf Examples
int x;
scanf("%d", &x);          // & required — passes address of x
printf("%d\n", x);        // no & — just pass the value

double d;
scanf("%lf", &d);         // %lf for double in scanf
printf("%.3f\n", d);      // %f is fine for double in printf

char name[50];
scanf("%s", name);         // no & — array name IS an address
printf("Hello, %s!\n", name);

char c = 'A';
printf("%c\n", c);         // prints: A
printf("%d\n", c);         // prints: 65  (ASCII value)

// Calculations inside printf — totally valid
printf("%d\n", 5 + 4);     // prints: 9
#define and sizeof
// #define — preprocessor macro, replaced BEFORE compilation
// No type, no memory allocated, no semicolon
#define MAX 100
#define PI  3.14159

int arr[MAX];         // becomes: int arr[100]

// sizeof — returns size in bytes at compile time
sizeof(int)           // → 4
sizeof(double)        // → 8
char arr[10] = "hi";
sizeof(arr)           // → 10  (allocated size)
strlen(arr)           // → 2   (string length, not counting \0)
Variable names: letters, digits, underscores only. Cannot start with a digit. Cannot be a keyword (int, float, etc.). Underscores ARE allowed.
char Arithmetic — ASCII Tricks
// Key ASCII values to memorise:
//   '0' = 48,  '9' = 57   (digits are sequential)
//   'A' = 65,  'Z' = 90   (uppercase sequential)
//   'a' = 97,  'z' = 122  (lowercase sequential, 'a'-'A' = 32)

// Digit character → integer value
char ch = '7';
int  n  = ch - '0';    // → 7    ('7'=55, '0'=48, 55-48=7)

// Integer value → digit character
int  n  = 4;
char ch = n + '0';     // → '4'

// Uppercase → lowercase
char upper = 'G';
char lower = upper + 32;     // → 'g'  (or use  upper + ('a'-'A'))

// Lowercase → uppercase
char lower = 'g';
char upper = lower - 32;     // → 'G'

// Check type of character
if (ch >= 'A' && ch <= 'Z') { /* uppercase */ }
if (ch >= 'a' && ch <= 'z') { /* lowercase */ }
if (ch >= '0' && ch <= '9') { /* digit     */ }

// Letter position (A=0, B=1 ... Z=25)
int pos = upper - 'A';       // 'G'-'A' = 6
Why it works: In C, char is just a small integer. Characters in a range ('A'–'Z', '0'–'9') are guaranteed to be consecutive, so arithmetic on them is portable.
printf Width & Padding
// %[flags][width][.precision]specifier

printf("%4d\n",   42);   // "  42"   right-align in 4-char field
printf("%-4d|\n", 42);   // "42  |"  left-align (- flag)
printf("%04d\n",  42);   // "0042"   zero-pad to width 4
printf("%8.2f\n", 3.14159); // "    3.14"  width 8, 2 decimal places
printf("%.4f\n",  3.14159); // "3.1416"    4 decimal places, no width
printf("%-10s|\n", "hi");   // "hi        |"  left-align string

// Useful for tables — line up columns:
for (int i = 1; i <= 5; i++)
    printf("%-3d %4d\n", i, i * i);
// 1     1
// 2     4
// 3     9
// 4    16
// 5    25
scanf Edge Cases
// %s stops at whitespace — can't read a sentence:
scanf("%s", name);        // reads one word only ("John" from "John Smith")

// fgets reads the whole line including spaces:
fgets(name, sizeof(name), stdin); // reads "John Smith\n" — watch for \n at end!

// Remove the newline fgets leaves in the buffer:
name[strcspn(name, "\n")] = '\0';  // replaces \n with null terminator

// Reading a char AFTER an int — newline gets stuck in buffer:
int n;
char c;
scanf("%d", &n);
scanf("%c", &c);    // BUG: c gets '\n' left from previous scanf!
scanf(" %c", &c);  // FIX: space before %c skips all whitespace/newlines

// Reading multiple values on one scanf:
scanf("%d %d", &x, &y);   // fine — whitespace in format matches any whitespace
Rule of thumb: After reading an int or float with scanf, if you next read a char, add a space before %c to flush the leftover newline.
⚙️

Operators

Arithmetic, logical, increment/decrement, precedence

Arithmetic Precedence (high → low)
OperatorsDirection
*  /  %left → right
+  -left → right
// 3 * 3 % 6 + 4 * 5
// = (3*3) % 6 + (4*5)     ← * and % left to right
// = 9 % 6 + 20
// = 3 + 20 = 23
Logical & Comparison
OpMeaning
&&logical AND (both true)
||logical OR (either true)
!logical NOT
&bitwise AND (different!)
==equality check
!=not equal
Increment / Decrement
int x = 4;

// Pre-increment: changes value BEFORE use
printf("%d", ++x);   // prints 5, x is now 5

// Post-increment: uses current value FIRST, then increments
printf("%d", x++);   // prints 5, x becomes 6 after

// Classic trap: int x=4, y=4; printf("%d,%d", ++x, y--);
// ++x → 5 (pre), y-- → 4 printed then y becomes 3
// Output: 5,4
Chained comparison gotcha: 4 > y > 1 does NOT work as math in C.
It evaluates as (4 > y) > 10 or 1 > 1 → always false.
Use: (4 > y && y > 1)
Type Casting
// (type)expression — convert to a different type before the operation
int a = 7, b = 2;

(double)a / b          // → 3.5   cast BEFORE dividing ✓
(double)(a / b)        // → 3.0   integer division happens FIRST, then cast ✗

(int)3.9               // → 3     truncates toward zero (NOT rounded)
(int)-3.9              // → -3    also truncates toward zero

// Classic trap — average of two ints:
int sum = 7;
float avg = (float)sum / 2;   // → 3.5  ✓  cast numerator first
float bad = sum / 2;           // → 3.0  ✗  integer division, then implicit cast
Rule: Cast the numerator to float/double BEFORE the /. If you cast after (double)(a/b), integer division already discarded the remainder.
Full Operator Precedence (High → Low)
LevelOperatorsDirection
1 (highest)( )  [ ]  ->  .left→right
2!  ++  --  (cast)  *  &  sizeof  (all unary)right→left
3*  /  %left→right
4+  -left→right
5<<  >>   (bit shift)left→right
6<  <=  >  >=left→right
7==  !=left→right
8&   (bitwise AND)left→right
9^   (bitwise XOR)left→right
10|   (bitwise OR)left→right
11&&   (logical AND)left→right
12||   (logical OR)left→right
13?:   (ternary)right→left
14 (lowest)=  +=  -=  *=  /=  %=  etc.right→left
Key traps: Bitwise & / | have LOWER precedence than == / !=. So x & 1 == 0 parses as x & (1 == 0) — always parenthesize bitwise ops. Also: && beats || in precedence.
🔀

Conditionals

if/else, switch, ternary

if / else if / else
if (x > 0) {
    printf("positive\n");
} else if (x < 0) {
    printf("negative\n");
} else {
    printf("zero\n");
}
Key: Once the first true condition is found, the rest of the chain is skipped entirely — even if later conditions would also be true.
Ternary Operator
// condition ? value_if_true : value_if_false
int max = (a > b) ? a : b;

int x = 5;
int y = (x > 3) ? 10 : 20;
// 5 > 3 is true → y = 10

printf((n == 1) ? "one" : "other");
switch Statement
switch (a) {
    case 0:
        printf("zero\n");
        break;              // REQUIRED to stop fall-through
    case 5:
        printf("five\n");
        break;
    case 10:               // no break → falls through to next case
    case 11:
        printf("ten or eleven\n");
        break;
    default:
        printf("other\n");
}
Fall-through: Without break, execution continues into the next case. This is usually a bug — always add break unless you intentionally want fall-through.
🔁

Loops

for, while, do-while — with tracing examples, break & continue

for Loop — Anatomy
//  ┌─ init ──┐  ┌── condition ──┐  ┌─ update ─┐
for (int i = 0;   i < 5;            i++) {
    printf("%d ", i);
}
// Output: 0 1 2 3 4
// init runs once; condition checked before each iteration;
// update runs after each iteration

// Count DOWN:
for (int i = 20; i >= 2; i -= 2) {
    printf("%d ", i);
}
// Output: 20 18 16 14 12 10 8 6 4 2

// for (int i = 1; i < 100; ++i)
// runs for i = 1, 2, 3, ... 99   (NOT 0, NOT 100)
while Loop
// Checks condition FIRST
// May execute 0 times
int i = 1;
while (i <= 3) {
    printf("%d\n", i);
    i++;
}
// i=1 → prints 1, i becomes 2
// i=2 → prints 2, i becomes 3
// i=3 → prints 3, i becomes 4
// i=4, 4 <= 3 false → STOP
// Output: 1 2 3
do-while Loop
// Executes body FIRST, then checks
// ALWAYS runs at least once
int i = 1, sum = 0;
do {
    sum += i;
    i++;
} while (i <= 3);
// i=1: sum=1, i→2, check 2≤3 ✓
// i=2: sum=3, i→3, check 3≤3 ✓
// i=3: sum=6, i→4, check 4≤3 ✗ STOP
// Output: sum = 6
break and continue
// break — exits the INNERMOST loop immediately
for (int i = 0; i < 10; i++) {
    if (i == 5) break;
    printf("%d ", i);
}
// Output: 0 1 2 3 4    (stops at 5, never prints 5)

// continue — skips REST of current iteration, goes to next
for (int i = 0; i < 6; i++) {
    if (i % 2 == 0) continue;  // skip even
    printf("%d ", i);
}
// Output: 1 3 5

// break in NESTED loops — only exits the INNERMOST loop
for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 3; j++) {
        if (j == 1) break;       // exits inner loop only
        printf("%d,%d ", i, j);
    }
    // outer loop continues normally
}
// Output: 0,0  1,0  2,0
Loop Choice Guide
UseWhen
forYou know the number of iterations in advance
whileRepeat until a condition changes; may need 0 iterations
do-whileBody must run at least once (e.g. menu that shows before first input)
Nested for Loops — How They Work
// Outer loop runs N times.
// For EACH outer iteration, inner loop runs M times fully.
// Total iterations = N × M

for (int i = 1; i <= 3; i++) {       // outer: runs 3 times
    for (int j = 1; j <= 4; j++) {   // inner: runs 4 times per outer
        printf("(%d,%d) ", i, j);
    }
    printf("\n");                      // newline after each row
}
// (1,1) (1,2) (1,3) (1,4)
// (2,1) (2,2) (2,3) (2,4)
// (3,1) (3,2) (3,3) (3,4)
// Total: 3×4 = 12 prints
Nested Loops — Multiplication Table
#include <stdio.h>

int main(void) {
    for (int i = 1; i <= 5; i++) {
        for (int j = 1; j <= 5; j++) {
            printf("%4d", i * j);  // %4d = right-align in 4-char field
        }
        printf("\n");
    }
}
//    1   2   3   4   5
//    2   4   6   8  10
//    3   6   9  12  15
//    4   8  12  16  20
//    5  10  15  20  25
Rule: inner loop variable j resets to 1 every time outer loop advances i.
Nested Loops — Triangle Patterns
// ── Right triangle (row i prints i stars) ──
for (int i = 1; i <= 5; i++) {
    for (int j = 1; j <= i; j++)   // inner limit = i (grows!)
        printf("* ");
    printf("\n");
}
// *
// * *
// * * *
// * * * *
// * * * * *

// ── Upside-down triangle (row i prints 5-i+1 stars) ──
for (int i = 5; i >= 1; i--) {
    for (int j = 1; j <= i; j++)
        printf("* ");
    printf("\n");
}
// * * * * *
// * * * *
// * * *
// * *
// *

// ── Number triangle ──
for (int i = 1; i <= 4; i++) {
    for (int j = 1; j <= i; j++)
        printf("%d ", j);
    printf("\n");
}
// 1
// 1 2
// 1 2 3
// 1 2 3 4
Key trick: when the inner loop bound uses i (the outer variable), the shape grows or shrinks per row.
Nested Loops — Iterating a 2D Array
#include <stdio.h>

int main(void) {
    int grid[3][4];

    /* Fill: grid[r][c] = r*10 + c */
    for (int r = 0; r < 3; r++)
        for (int c = 0; c < 4; c++)
            grid[r][c] = r * 10 + c;

    /* Print row by row */
    for (int r = 0; r < 3; r++) {
        for (int c = 0; c < 4; c++)
            printf("%4d", grid[r][c]);
        printf("\n");
    }
}
//    0   1   2   3
//   10  11  12  13
//   20  21  22  23

/* Sum all elements with nested loops */
int sum = 0;
for (int r = 0; r < 3; r++)
    for (int c = 0; c < 4; c++)
        sum += grid[r][c];
// sum = 0+1+2+3 + 10+11+12+13 + 20+21+22+23 = 138
Nested Loops — Find All Pairs / Search
// ── Find every pair (i,j) that sums to a target ──
int nums[] = {1, 4, 3, 2};
int target = 5;

for (int i = 0; i < 4; i++) {
    for (int j = i + 1; j < 4; j++) {  // j starts at i+1 — no repeats
        if (nums[i] + nums[j] == target)
            printf("(%d, %d)\n", nums[i], nums[j]);
    }
}
// (1, 4)
// (3, 2)

// ── Print only UPPER triangle of a grid (i ≤ j) ──
for (int i = 1; i <= 4; i++) {
    for (int j = 1; j <= 4; j++) {
        if (j < i) printf("    ");    // blank space for lower triangle
        else       printf("%4d", i*j);
    }
    printf("\n");
}
//    1   2   3   4
//        4   6   8
//            9  12
//               16
Nested Loops — Tracing Practice
// What does this print? Trace step by step.
int count = 0;
for (int i = 1; i <= 3; i++) {
    for (int j = 1; j <= i; j++) {
        count++;
    }
}
printf("%d\n", count);
Trace:
i=1 → inner runs 1 time → count=1
i=2 → inner runs 2 times → count=3
i=3 → inner runs 3 times → count=6
Output: 6  (pattern: 1+2+3 = n(n+1)/2 for n=3)
// What does this print?
for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 3; j++) {
        if (i == j) printf("%d ", i);
    }
}
// Only prints when row == col (the diagonal)
// Output: 0 1 2
🔧

Functions

Prototypes, pass-by-value, void, static, scope — with many examples

Prototype → Definition → Call
// 1. PROTOTYPE (declaration) — tells compiler the signature
//    Return type, name, parameter types. Names are optional.
double mySqrt(int x);     // prototype at top of file
void   greet(void);       // void param = NO arguments
int    add(int, int);    // parameter names optional in prototype

// 2. DEFINITION — actual implementation
double mySqrt(int x) {
    return x * x;           // return type must match prototype
}

void greet(void) {
    printf("Hello!\n");
    return;                 // bare return OK in void function
}                           // (or just fall off the end)

// 3. CALL
double result = mySqrt(5);
greet();
Mandatory in a prototype: return type + function name. Parameter names are optional. Even the parameter list can be omitted (int foo(); is valid).
Pass-by-Value — C ALWAYS copies arguments
// The function gets a COPY — changing it does NOT affect the original
void addTen(int n) {
    n = n + 10;            // only changes the local copy
}

int main() {
    int x = 5;
    addTen(x);
    printf("%d\n", x);    // still 5! addTen had no effect on x
}

// To modify the original, pass a POINTER:
void addTenPtr(int *n) {
    *n = *n + 10;          // dereference to modify original
}
addTenPtr(&x);
printf("%d\n", x);        // now 15
Return Values & Unreachable Code
int show() {
    printf("3");
    return 5;              // function exits HERE
    printf("4");           // NEVER reached — dead code after return
}

int main() {
    int a = show();        // show() prints "3", returns 5
    printf("%d", a);       // prints 5
}
// Total output: 35
static Local Variables — Persist Between Calls
void counter() {
    static int count = 0;   // initialized ONCE, then remembered
    count++;
    printf("%d\n", count);
}

counter();  // prints 1
counter();  // prints 2
counter();  // prints 3

// Regular local: created fresh each call, destroyed on return
// Static local:  created once, lives for the entire program
Scope: Local vs Global
int globalVar = 100;        // global — accessible from any function

void funcA() {
    int local = 5;           // local — only exists inside funcA
    printf("%d %d", local, globalVar);
}

void funcB() {
    // local from funcA is NOT accessible here
    printf("%d", globalVar); // global IS accessible
}
Local variable scope = within the function where declared. Not in other functions, not in main (unless main IS the function).
void foo() vs void foo(void)
void foo(void);    // C: explicitly NO parameters
void foo();        // C: unspecified parameter list (old style — avoid)
// In C++, both mean the same thing.
// In C, foo() means "I haven't told you what parameters it takes".
Functions — Complete Example
#include <stdio.h>

// Prototypes
int   add(int a, int b);
float average(int *arr, int n);
void  printArray(int *arr, int n);

int main(void) {
    int nums[] = {10, 20, 30, 40};
    printArray(nums, 4);
    printf("Avg: %.1f\n", average(nums, 4));
    printf("Sum first two: %d\n", add(nums[0], nums[1]));
    return 0;
}

int add(int a, int b) {
    return a + b;
}

float average(int *arr, int n) {
    int sum = 0;
    for (int i = 0; i < n; i++) sum += arr[i];
    return (float)sum / n;
}

void printArray(int *arr, int n) {
    for (int i = 0; i < n; i++)
        printf("%d ", arr[i]);
    printf("\n");
}
📋

Arrays

Declaration, initialization, access, sizeof, 2D arrays

Declaration & Initialization
int arr[5];                           // uninitialized (garbage values)
int arr[5] = {1, 2, 3, 4, 5};        // fully initialized
int arr[5] = {1, 2};                  // rest = 0 → {1,2,0,0,0}
int arr[5] = {0};                    // all zeros → {0,0,0,0,0}
int arr[] = {10, 20, 30};            // size inferred: 3 elements

// Access — 0-indexed!
arr[0] = 100;   // first element
arr[4] = 500;   // last element of arr[5]
// arr[5] is OUT OF BOUNDS — undefined behavior!
Partial initialization: If you initialize any element, all unspecified elements are set to 0. This is guaranteed by the C standard.
sizeof vs strlen for Arrays
char str[10] = "hello";
sizeof(str)    // → 10   (total allocated bytes — always fixed)
strlen(str)    // → 5    (characters before \0 — the string length)

int nums[5] = {1,2,3,4,5};
sizeof(nums)           // → 20   (5 ints × 4 bytes each)
sizeof(nums) / sizeof(int)  // → 5  (number of elements)
LEN Macro — Count Array Elements
// Divide total byte-size by size of one element
#define LEN(arr) (sizeof(arr) / sizeof((arr)[0]))

int nums[] = {10, 20, 30, 40, 50};
int n = LEN(nums);               // → 5  (works for any type)
// Equivalent to: sizeof(nums) / sizeof(int)  → 20/4 = 5

// Safe loop — no magic number:
for (int i = 0; i < LEN(nums); i++)
    printf("%d ", nums[i]);
Caution: LEN() only works where the array was declared with a size. Once passed to a function as a pointer, sizeof returns the pointer size (4 or 8 bytes), not the array size. Always pass n as a separate parameter.
2D Arrays
int grid[3][4];       // 3 rows, 4 columns = 12 total elements

int grid[3][4] = {
    {1, 2, 3, 4},     // row 0
    {5, 6, 7, 8},     // row 1
    {9, 10, 11, 12}   // row 2
};

grid[0][0]   // → 1   (row 0, col 0)
grid[2][3]   // → 12  (row 2, col 3)

// Iterate with nested loops:
for (int r = 0; r < 3; r++)
    for (int c = 0; c < 4; c++)
        printf("%d ", grid[r][c]);
🔗

Pointers

& address-of, * dereference, arithmetic, const, arrays and pointers

Pointer Basics
int x = 42;
int *p = &x;     // p holds the ADDRESS of x  (& = address-of)

printf("%d\n", x);    // 42    — the value
printf("%p\n", p);    // 0x... — the address stored in p
printf("%d\n", *p);   // 42    — dereference: value AT the address

*p = 100;             // dereference to WRITE: changes x to 100
printf("%d\n", x);    // 100

// & = "address of" (use to create a pointer to something)
// * = "dereference" (use to access what a pointer points to)
Arrays and Pointers — Equivalences
int arr[] = {10, 20, 30, 40};
int *p = arr;          // array name decays to &arr[0]

// These are ALL equivalent:
arr[2]                  // → 30
*(arr + 2)              // → 30  (arr[i] == *(arr+i))
p[2]                    // → 30
*(p + 2)                // → 30

// Stepping through with pointer arithmetic:
for (int i = 0; i < 4; i++)
    printf("%d ", *(p + i));
// Output: 10 20 30 40

// p+2 gives the ADDRESS of arr[2]
// *(p+2) gives the VALUE at arr[2] = 30
const Pointer Variants
// Read right-to-left to decode:

const int *p = &x;    // pointer to const int
                       // CAN change where p points
                       // CANNOT change *p (the value)
*p = 5;               // ERROR — value is read-only through p
p = &y;               // OK — p can point elsewhere

int * const p = &x;  // const pointer to int
                       // CANNOT change where p points
                       // CAN change *p (the value)
*p = 5;               // OK
p = &y;               // ERROR — pointer is locked
Memory trick: The const modifies whatever is immediately to its right. const int *p → const applies to int (value is locked). int * const p → const applies to p (pointer is locked).
Type Mismatch — Common Bug
int *p;
double x = 3.14;
p = &x;               // ERROR: assigning double* to int*
                       // p would misread x's bytes as an int
                       // Always match pointer type to variable type
📝

Strings

char arrays, null terminator, strlen, char[] vs char*

How Strings Work in C
// A string is a char array ending in '\0' (null terminator)
char str[] = "hello";
// Memory layout: ['h']['e']['l']['l']['o']['\0']
//                  0    1    2    3    4    5
// sizeof(str) = 6   (6 bytes including \0)
// strlen(str) = 5   (5 characters NOT counting \0)

// Manual construction:
char str[6];
str[0] = 'h'; str[1] = 'i'; str[2] = '\0';
printf("%s\n", str);  // "hi"
char[] vs char* — Critical Difference
char arr[] = "hello";  // COPIES literal into stack array → MODIFIABLE
arr[0] = 'H';           // OK — arr is a local copy

char *p = "hello";     // POINTS to read-only string literal → DO NOT MODIFY
*p = 'H';              // UNDEFINED BEHAVIOR — usually crashes (segfault)

// Passing strings to functions — use char* parameter:
void printStr(char *str) {      // correct: char* not char
    printf("%s\n", str);
}
void printStr(char str) {       // WRONG: single char, not string
    printf("%s\n", str);         // bug — line A in quiz Q4
}
strlen needs <string.h>. Remember to #include <string.h> when using strlen, strcmp, strcpy, etc.
string.h — All Common Functions
FunctionWhat it does
strlen(s)Length — NOT counting \0
strcpy(dest, src)Copy src into dest (dest must be big enough)
strncpy(dest, src, n)Copy at most n chars (safer)
strcat(dest, src)Append src to end of dest
strncat(dest, src, n)Append at most n chars
strcmp(a, b)Compare: 0=equal, <0=a before b, >0=a after b
strncmp(a, b, n)Compare first n chars only
strchr(s, c)Pointer to first c in s, NULL if not found
strstr(hay, needle)Pointer to first substring, NULL if not found
strcspn(s, delim)Index of first char in delim found in s
// ─── Use strcmp to compare strings — NEVER use == ───────────────
char a[] = "hello", b[] = "hello";
if (a == b) { }                      // WRONG — compares addresses
if (strcmp(a, b) == 0) { }           // CORRECT — compares contents

// ─── Build a string with strcpy + strcat ────────────────────────
char full[100];
strcpy(full, "Hello");
strcat(full, ", World!");
printf("%s\n", full);                // Hello, World!

// ─── fgets leaves '\n' at end — remove it with strcspn ──────────
char line[80];
fgets(line, sizeof(line), stdin);
line[strcspn(line, "\n")] = '\0';    // replace first \n with \0
// strcspn returns the index of the first '\n', so we zero it out

// ─── strchr — find a char in a string ───────────────────────────
char *pos = strchr("hello", 'l');   // points to first 'l'
if (pos != NULL)
    printf("found at index %ld\n", pos - "hello"); // pointer subtraction
Remember: strcmp returns 0 for equal strings — so the condition to check equality is strcmp(a,b) == 0, NOT strcmp(a,b) (which would be truthy for UNequal strings).
📂

File I/O & Command-Line Args

fopen, fclose, reading & writing, argc/argv, standard streams

fopen Modes — Quick Reference
ModeActionFile must exist?Truncates?
"r"Read onlyYes → NULL if notNo
"w"Write (create/overwrite)No — creates itYes
"a"Append (write to end)No — creates itNo (preserves content)
"r+"Read + writeYes → NULL if notNo
"rb", "wb"Binary read/writesame as abovesame
Always check return value! If fopen fails, it returns NULL. Using a NULL file pointer causes a crash. Always if (fp == NULL) { ... } after opening.
Reading a File — Complete Pattern
#include <stdio.h>

int main(void) {
    FILE *fp;                          // FILE* — not int*, not FILE
    fp = fopen("data.txt", "r");

    if (fp == NULL) {                  // ALWAYS check for failure
        printf("Error opening file\n");
        return 1;
    }

    // Method 1: read line by line with fgets
    char line[100];
    while (fgets(line, sizeof(line), fp) != NULL) {
        printf("%s", line);            // fgets keeps the \n
    }

    // Method 2: read character by character
    rewind(fp);                        // go back to start
    int c;
    while ((c = getc(fp)) != EOF) {
        putc(c, stdout);
    }

    // Method 3: read formatted data
    int n;
    rewind(fp);
    while (fscanf(fp, "%d", &n) == 1) {
        printf("%d\n", n);
    }

    fclose(fp);                        // always close!
    return 0;
}
Writing to a File
FILE *fp = fopen("out.txt", "w");    // "w" creates or truncates
if (fp == NULL) return 1;

fprintf(fp, "Value: %d\n", 42);       // like printf but to file
fputs("A line of text\n", fp);        // write a string
putc('!', fp);                        // write a single char

fclose(fp);

// Append mode — preserves existing content:
FILE *log = fopen("log.txt", "a");
fprintf(log, "New entry\n");           // added to the END
fclose(log);
Standard File Pointers (auto-opened)
PointerRefers toExample use
stdinKeyboard inputfscanf(stdin, "%d", &x) = scanf("%d", &x)
stdoutScreen outputfprintf(stdout, "Hi") = printf("Hi")
stderrError outputfprintf(stderr, "Error!\n")
stdall does NOT exist. There are exactly three standard file pointers: stdin, stdout, stderr.
printf(ch) vs printf("%c", ch) — Common Bug
char ch = '$';
putc(ch, stdout);              // prints: $  ✓

// These are all equivalent to the above:
printf("%c", ch);              // ✓ correct
fprintf(stdout, "%c", ch);    // ✓ correct
printf("$");                   // ✓ correct (hardcoded)

printf(ch);                    // ✗ WRONG — first arg must be a format
                               //   string literal, not a variable.
                               //   Also a security vulnerability!
argc and argv — Command-Line Arguments
int main(int argc, char *argv[]) {
    // argc = total argument count (INCLUDES program name)
    // argv[0] = program name  ("./test")
    // argv[1] = first arg     ("a1")
    // argv[2] = second arg    ("b2")
    // argv[3] = third arg     ("c3")

    printf("argc = %d\n", argc);
    for (int i = 0; i < argc; i++)
        printf("argv[%d] = %s\n", i, argv[i]);
}

// Run: ./test a1 b2 c3
//   argc  = 4       (4 tokens total)
//   argv[0] = "./test"
//   argv[1] = "a1"   ← first argument
//   argv[2] = "b2"
//   argv[3] = "c3"

// Open file named by first argument:
FILE *fp = fopen(argv[1], "r");
if (fp == NULL) {
    fprintf(stderr, "Cannot open %s\n", argv[1]);
    return 1;
}
fscanf vs scanf
scanf("%d", &x);               // reads from keyboard
fscanf(stdin, "%d", &x);       // identical to scanf — stdin = keyboard
fscanf(fp, "%d", &x);          // reads from file fp

// fscanf() CAN read from console — False that it "cannot get input from terminal"
🔢

math.h & stdlib.h Quick Reference

Common library functions — remember to #include and link -lm for math

math.h Functions (link with -lm)
FunctionReturns
sqrt(x)square root
pow(a, b)a raised to power b
fabs(x)absolute value (double)
round(x)nearest integer (as double)
floor(x)round down → floor(3.9) = 3.0
ceil(x)round up  → ceil(3.1) = 4.0
llround(x)nearest integer (as long long)
log(x)natural log (base e)
log10(x)base-10 log
sin(x) cos(x)trig (radians)
stdlib.h & stdio.h Extras
FunctionDoes
atoi("42")string → int
atof("3.14")string → double
abs(-5)absolute value (int)
EXIT_SUCCESS0 — clean exit
EXIT_FAILURE1 — error exit
getchar()read one char from stdin (returns int)
putchar(c)write one char to stdout
puts(s)print string + automatic \n
sprintf(buf,fmt,...)printf into a string buffer
perror("msg")print system error message
Floating-Point Money Trap — Use llround()
// Float arithmetic is imprecise — can't represent 0.1 + 0.2 exactly:
printf("%.17f\n", 0.1 + 0.2);       // 0.30000000000000004 !

// For money: convert to integer CENTS using llround() to avoid drift
double price = 3.14;
long long cents = llround(price * 100.0);   // 314 — exact

// Math examples:
printf("%.2f\n", sqrt(16.0));    // 4.00
printf("%.2f\n", pow(2.0, 8.0)); // 256.00
printf("%.2f\n", fabs(-9.5));   // 9.50
printf("%.0f\n", ceil(3.1));    // 4
printf("%.0f\n", floor(3.9));   // 3

// sprintf — build a string from formatted data
char buf[50];
sprintf(buf, "Score: %d / %d", 8, 10);
printf("%s\n", buf);              // Score: 8 / 10

Compile Commands & Flags

gcc options you need — especially -lm for math.h

Common gcc Commands
# Basic compilation
gcc file.c -o file

# Recommended — C11 standard with all warnings
gcc file.c -std=c11 -Wall -Wextra -o file

# With math.h (sqrt, pow, fabs, etc.) — MUST add -lm
gcc file.c -std=c11 -Wall -lm -o file

# Multiple source files
gcc main.c other.c -std=c11 -Wall -lm -o program

# With debug symbols (for gdb / debugger)
gcc file.c -std=c11 -Wall -g -o file
FlagWhat it does
-std=c11Use C11 standard (allows int i in for-loop init, VLAs, etc.)
-WallEnable most useful warnings
-WextraExtra warnings beyond -Wall
-lmLink math library — required for sqrt, pow, fabs, llround, etc.
-o nameName the output executable
-gInclude debug symbols (for stepping through with gdb)
Linker error "undefined reference to sqrt"? You forgot -lm. It must come AFTER the source files on the command line.
🧪

Practice Problems & Solutions

Lab-style questions with full working code — loops and functions covered most thoroughly

📦 Basics & I/O
Problem 1 — Read and Display Student Info
Task: Read a student's name, age, and GPA from the user. Print a formatted summary line. Also print what their age will be in 5 years.
#include <stdio.h>

int main(void) {
    char name[50];
    int  age;
    float gpa;

    printf("Name:  "); scanf("%s",  name);
    printf("Age:   "); scanf("%d",  &age);
    printf("GPA:   "); scanf("%f",  &gpa);

    printf("--- Summary ---\n");
    printf("Student: %s\n",    name);
    printf("Age now: %d, in 5 years: %d\n", age, age + 5);
    printf("GPA:     %.2f\n",  gpa);
    return 0;
}
Sample output
Student: Alice Age now: 20, in 5 years: 25 GPA: 3.75
Key: scanf needs & for int/float but NOT for char arrays. Use %.2f to print exactly 2 decimal places.
Problem 2 — Operator Precedence & Increment Traps
Task: Predict then verify: (a) what is 3 * 3 % 6 + 4 * 5? (b) given int x=4, y=4, what does printf("%d,%d", ++x, y--) print?
#include <stdio.h>

int main(void) {
    /* (a) Left-to-right for *, %, * then +
       3*3 = 9  →  9%6 = 3  →  4*5 = 20  →  3+20 = 23 */
    printf("%d\n", 3 * 3 % 6 + 4 * 5);   /* 23 */

    /* (b) ++x: pre-increment — x becomes 5 BEFORE printf
           y--: post-decrement — y is still 4 WHEN printed, then y becomes 3 */
    int x = 4, y = 4;
    printf("%d,%d\n", ++x, y--);          /* 5,4 */

    /* (c) Chained comparison TRAP */
    int v = 2;
    if (4 > v > 1)                        /* evaluates as (4>v)>1 = 1>1 = false! */
        printf("math works\n");
    if (4 > v && v > 1)                   /* correct way */
        printf("logic works\n");
    return 0;
}
Output
23 5,4 logic works
🔁 Flow Control & Loops
Problem 3 — Sum 1 to N (for loop)
Task: Read a positive integer N from the user. Print the sum of all integers from 1 to N inclusive.
#include <stdio.h>

int main(void) {
    int n, sum = 0;
    printf("Enter N: ");
    scanf("%d", &n);

    for (int i = 1; i <= n; i++) {
        sum += i;       /* same as: sum = sum + i */
    }

    printf("Sum(1 to %d) = %d\n", n, sum);
    return 0;
}

/* Trace for N=4:
   i=1: sum=1
   i=2: sum=3
   i=3: sum=6
   i=4: sum=10
   i=5: 5<=4 false, loop ends.  Output: 10 */
Sample output (N=4)
Sum(1 to 4) = 10
Problem 4 — FizzBuzz
Task: Print 1 to 20. For multiples of 3 print "Fizz", multiples of 5 print "Buzz", multiples of both print "FizzBuzz".
#include <stdio.h>

int main(void) {
    for (int i = 1; i <= 20; i++) {
        /* check BOTH first — order matters! */
        if      (i % 3 == 0 && i % 5 == 0) printf("FizzBuzz\n");
        else if (i % 3 == 0)                printf("Fizz\n");
        else if (i % 5 == 0)                printf("Buzz\n");
        else                                printf("%d\n", i);
    }
    return 0;
}
Partial output
1 2 Fizz 4 Buzz Fizz 7 ... FizzBuzz ← (15) ...
Gotcha: Check divisibility by both first. If you check % 3 first and hit break/else, you'll never reach the "FizzBuzz" case for 15.
Problem 5 — Multiplication Table (Nested Loops)
Task: Print a 5×5 multiplication table using nested for loops.
#include <stdio.h>

int main(void) {
    for (int row = 1; row <= 5; row++) {
        for (int col = 1; col <= 5; col++) {
            printf("%4d", row * col);   /* %4d = right-align in 4 chars */
        }
        printf("\n");                   /* newline after each row */
    }
    return 0;
}
Output
1 2 3 4 5 2 4 6 8 10 3 6 9 12 15 4 8 12 16 20 5 10 15 20 25
Problem 6 — Input Validation with do-while
Task: Keep asking the user for a number between 1 and 10 until they enter a valid one. A do-while guarantees the prompt shows at least once.
#include <stdio.h>

int main(void) {
    int n;
    do {
        printf("Enter a number between 1 and 10: ");
        scanf("%d", &n);
        if (n < 1 || n > 10)
            printf("Invalid! Try again.\n");
    } while (n < 1 || n > 10);    /* keep looping while invalid */

    printf("You entered: %d\n", n);
    return 0;
}
Why do-while? We must show the prompt before the first check. A while loop would need to prime the loop with a dummy value first.
🔧 Functions
Problem 7 — Factorial (Function + Loop)
Task: Write a function long factorial(int n) that returns n! using a loop. Call it from main for a user-supplied n.
#include <stdio.h>

long factorial(int n);          /* prototype */

int main(void) {
    int n;
    printf("Enter n: ");
    scanf("%d", &n);
    printf("%d! = %ld\n", n, factorial(n));
    return 0;
}

long factorial(int n) {         /* definition */
    long result = 1;
    for (int i = 2; i <= n; i++)
        result *= i;            /* result = result * i */
    return result;
}

/* Trace for n=5:
   i=2: result=2
   i=3: result=6
   i=4: result=24
   i=5: result=120  → returns 120 */
Sample output (n=5)
5! = 120
Problem 8 — Pass-by-Value Trap vs Pointer Fix
Task: Show that addTen(x) does NOT modify x. Then fix it using a pointer parameter.
#include <stdio.h>

void addTen_byval(int n);       /* modifies a COPY — does nothing to caller */
void addTen_ptr(int *n);        /* modifies the ORIGINAL via pointer */

int main(void) {
    int x = 5;

    addTen_byval(x);
    printf("After byval: %d\n", x);   /* still 5 — x unchanged */

    addTen_ptr(&x);                   /* pass ADDRESS of x */
    printf("After ptr:   %d\n", x);   /* now 15 */

    return 0;
}

void addTen_byval(int n) {
    n = n + 10;     /* only changes the local copy of n */
}

void addTen_ptr(int *n) {
    *n = *n + 10;   /* dereference: changes the value at the address */
}
Output
After byval: 5 After ptr: 15
Problem 9 — Static Call Counter
Task: Write a function that prints how many times it has been called, using a static local variable.
#include <stdio.h>

void counter(void);

int main(void) {
    counter();
    counter();
    counter();
    return 0;
}

void counter(void) {
    static int calls = 0;   /* initialized ONCE — survives between calls */
    calls++;
    printf("Called %d time(s)\n", calls);
}
Output
Called 1 time(s) Called 2 time(s) Called 3 time(s)
Without static: calls would be re-initialized to 0 on every call, always printing "Called 1 time(s)".
Problem 10 — Grade Classifier Function
Task: Write a function char getGrade(int score) that returns 'A' (≥90), 'B' (≥80), 'C' (≥70), 'D' (≥60), or 'F'. Test with several scores.
#include <stdio.h>

char getGrade(int score);

int main(void) {
    int scores[] = {95, 82, 71, 65, 50};
    int n = sizeof(scores) / sizeof(int);   /* number of elements */

    for (int i = 0; i < n; i++)
        printf("Score %d → Grade %c\n", scores[i], getGrade(scores[i]));

    return 0;
}

char getGrade(int score) {
    if      (score >= 90) return 'A';
    else if (score >= 80) return 'B';
    else if (score >= 70) return 'C';
    else if (score >= 60) return 'D';
    else                  return 'F';
}
Output
Score 95 → Grade A Score 82 → Grade B Score 71 → Grade C Score 65 → Grade D Score 50 → Grade F
🔗 Arrays & Pointers
Problem 11 — Find Max in Array
Task: Write a function int findMax(int *arr, int n) that returns the largest value in an integer array.
#include <stdio.h>

int findMax(int *arr, int n);

int main(void) {
    int arr[] = {3, 17, 5, 42, 8, 1, 99, 7};
    int n = sizeof(arr) / sizeof(int);      /* 8 elements */

    printf("Max = %d\n", findMax(arr, n));
    return 0;
}

int findMax(int *arr, int n) {
    int max = arr[0];           /* start with first element */
    for (int i = 1; i < n; i++)
        if (arr[i] > max)
            max = arr[i];
    return max;
}
Output
Max = 99
Problem 12 — Reverse Array In-Place (Pointer Arithmetic)
Task: Write void reverse(int *arr, int n) that reverses the array in-place using only pointer arithmetic (two-pointer technique). Print the array before and after.
#include <stdio.h>

void reverse(int *arr, int n);
void printArray(int *arr, int n);

int main(void) {
    int arr[] = {1, 2, 3, 4, 5};
    int n = sizeof(arr) / sizeof(int);

    printf("Before: "); printArray(arr, n);
    reverse(arr, n);
    printf("After:  "); printArray(arr, n);
    return 0;
}

void reverse(int *arr, int n) {
    int *left  = arr;           /* pointer to first element */
    int *right = arr + n - 1;  /* pointer to last element  */

    while (left < right) {
        int temp = *left;       /* swap values at left and right */
        *left    = *right;
        *right   = temp;
        left++;                 /* move pointers inward */
        right--;
    }
}

void printArray(int *arr, int n) {
    for (int i = 0; i < n; i++)
        printf("%d ", *(arr + i));  /* pointer arithmetic: same as arr[i] */
    printf("\n");
}
Output
Before: 1 2 3 4 5 After: 5 4 3 2 1
Key: arr[i] and *(arr+i) are identical. Two-pointer swap: start one pointer at the left end and one at the right, swap and move inward until they meet.
Problem 13 — Swap Two Variables Using Pointers
Task: Write a void swap(int *a, int *b) function that swaps two integers. Demonstrate that the values in main() are actually changed.
#include <stdio.h>

void swap(int *a, int *b);

int main(void) {
    int x = 10, y = 20;
    printf("Before: x=%d, y=%d\n", x, y);
    swap(&x, &y);               /* pass ADDRESSES */
    printf("After:  x=%d, y=%d\n", x, y);
    return 0;
}

void swap(int *a, int *b) {
    int temp = *a;   /* save value at a */
    *a = *b;         /* write value at b into a */
    *b = temp;       /* write saved value into b */
}
Output
Before: x=10, y=20 After: x=20, y=10
📝 Strings
Problem 14 — Count Character Occurrences (Pointer Traversal)
Task: Write int countChar(char *str, char target) that walks through a string using pointer arithmetic and counts how many times target appears.
#include <stdio.h>

int countChar(char *str, char target);

int main(void) {
    char sentence[] = "hello world";
    printf("'l' appears %d time(s)\n", countChar(sentence, 'l'));  /* 3 */
    printf("'o' appears %d time(s)\n", countChar(sentence, 'o'));  /* 2 */
    printf("'z' appears %d time(s)\n", countChar(sentence, 'z'));  /* 0 */
    return 0;
}

int countChar(char *str, char target) {
    int count = 0;
    while (*str != '\0') {      /* walk until null terminator */
        if (*str == target)
            count++;
        str++;                  /* advance pointer to next char */
    }
    return count;
}
Output
'l' appears 3 time(s) 'o' appears 2 time(s) 'z' appears 0 time(s)
Problem 15 — Student Average (Arrays + Functions)
Task: Read N scores into an array. Write float average(int *scores, int n). Print the average and how many students scored above it.
#include <stdio.h>

float average(int *scores, int n);

int main(void) {
    int n;
    printf("How many students? ");
    scanf("%d", &n);

    int scores[n];
    for (int i = 0; i < n; i++) {
        printf("Score %d: ", i + 1);
        scanf("%d", &scores[i]);
    }

    float avg = average(scores, n);
    printf("Average: %.2f\n", avg);

    int above = 0;
    for (int i = 0; i < n; i++)
        if (scores[i] > avg) above++;

    printf("%d student(s) scored above average\n", above);
    return 0;
}

float average(int *scores, int n) {
    int sum = 0;
    for (int i = 0; i < n; i++)
        sum += scores[i];
    return (float)sum / n;      /* cast to float before division! */
}
Sample (3 students: 70, 85, 90)
Average: 81.67 2 student(s) scored above average
Cast is essential: sum / n would be integer division (truncates). (float)sum / n forces floating-point division.
Problem 16 — Vowel Counter (switch/case Fall-Through)
Task: Write a program that reads a string and counts how many of each vowel (a, e, i, o, u) appear, case-insensitive. Use switch with fall-through case labels.
#include <stdio.h>

int main(void) {
    char str[100];
    int a=0, e=0, i=0, o=0, u=0;

    printf("Enter a string: ");
    fgets(str, sizeof(str), stdin);

    for (int j = 0; str[j] != '\0'; j++) {
        switch (str[j]) {
            case 'a': case 'A': a++; break;
            case 'e': case 'E': e++; break;
            case 'i': case 'I': i++; break;
            case 'o': case 'O': o++; break;
            case 'u': case 'U': u++; break;
            /* non-vowels: no case match, nothing happens */
        }
    }

    printf("a=%d  e=%d  i=%d  o=%d  u=%d\n", a, e, i, o, u);
    return 0;
}
Sample (input: "Hello World")
a=0 e=1 i=0 o=2 u=0
Key idea: Multiple case labels before one break = fall-through. case 'a': case 'A': both route to the same a++. The loop condition str[j] != '\0' stops at the null terminator.
📂 File I/O
Problem 17 — Write Grades to File, Read Average Back
Task: Write 5 scores to "grades.txt" using fprintf. Then re-open for reading, read scores with fscanf, compute and print the average.
#include <stdio.h>

int main(void) {
    int scores[] = {85, 92, 78, 95, 88};
    int n = 5;

    /* --- Write --- */
    FILE *fp = fopen("grades.txt", "w");
    if (fp == NULL) { printf("Cannot create file\n"); return 1; }

    for (int i = 0; i < n; i++)
        fprintf(fp, "%d\n", scores[i]);
    fclose(fp);
    printf("Wrote %d scores to grades.txt\n", n);

    /* --- Read back --- */
    fp = fopen("grades.txt", "r");
    if (fp == NULL) { printf("Cannot open file\n"); return 1; }

    int total = 0, count = 0, val;
    while (fscanf(fp, "%d", &val) == 1) {   /* returns 1 while reading OK */
        total += val;
        count++;
    }
    fclose(fp);

    printf("Average from file: %.2f\n", (float)total / count);
    return 0;
}
Output
Wrote 5 scores to grades.txt Average from file: 87.60
Problem 18 — Command-Line File Line Counter (argc/argv)
Task: Accept a filename as argv[1]. Open it for reading (print error + exit if it fails). Count lines using fgets(), then print the total.
#include <stdio.h>

int main(int argc, char *argv[]) {
    /* argc = number of tokens including program name
       argv[0] = "./counter",  argv[1] = "myfile.txt" */
    if (argc != 2) {
        printf("Usage: %s <filename>\n", argv[0]);
        return 1;
    }

    FILE *fp = fopen(argv[1], "r");
    if (fp == NULL) {
        printf("Error: cannot open '%s'\n", argv[1]);
        return 1;                   /* non-zero = failure */
    }

    int lines = 0;
    char buf[256];
    while (fgets(buf, sizeof(buf), fp) != NULL)
        lines++;

    fclose(fp);
    printf("'%s' contains %d line(s)\n", argv[1], lines);
    return 0;
}

/* Run: ./counter grades.txt
   argv[0]="./counter", argv[1]="grades.txt", argc=2 */
Sample run
./counter grades.txt → 'grades.txt' contains 5 line(s)
Problem 19 — Copy File Character by Character
Task: Open a source file for reading and a destination file for writing. Copy every character using getc/putc.
#include <stdio.h>

int main(void) {
    FILE *src  = fopen("grades.txt", "r");
    FILE *dest = fopen("backup.txt", "w");

    if (src == NULL || dest == NULL) {
        printf("File error\n");
        return 1;
    }

    int c;
    while ((c = getc(src)) != EOF)   /* read one char; EOF signals end */
        putc(c, dest);               /* write that char to destination */

    fclose(src);
    fclose(dest);
    printf("File copied successfully\n");
    return 0;
}
Note: getc returns an int (not char) because EOF is typically -1, which doesn't fit in a char. Always use int c.
🔍 Trace the Code — Predict the Output
Trace 1 — switch with Fall-Through
What does this print?
#include <stdio.h>
int main(void) {
    int a = 5;
    switch (a) {
        case 0:  printf("a"); break;
        case 5:  printf("b"); break;   /* matches here */
        case 10: printf("c");          /* no break — but never reached */
    }
    return 0;
}
Answer
b
Why: a=5 matches case 5, prints "b", then break exits the switch. case 10 is never reached so its missing break doesn't matter.
Trace 2 — do-while Step-by-Step
What does this print?
#include <stdio.h>
int main(void) {
    int i = 1, sum = 0;
    do {
        sum += i;
        i++;
    } while (i <= 3);
    printf("%d\n", sum);
    return 0;
}
Trace then answer
Iteration 1: sum=0+1=1, i→2, check 2≤3 ✓ continue Iteration 2: sum=1+2=3, i→3, check 3≤3 ✓ continue Iteration 3: sum=3+3=6, i→4, check 4≤3 ✗ stop Answer: 6
Trace 3 — Pass-by-Value: show() Function
What does this print?
#include <stdio.h>
int show();
void main() {
    int a;
    a = show();
    printf("%d", a);
}
int show() {
    printf("3");
    return 5;
    printf("4");    /* unreachable — after return */
}
Answer
35
Why: show() prints "3", then returns 5. The second printf after return is dead code — never executes. main() stores 5 in a and prints it with %d.
Trace 4 — if/else Chain: First True Wins
What does this print?
#include <stdio.h>
void main() {
    int x = 5;
    if (x > 1)
        printf("a");
    else if (x == 5)
        printf("b");
    else
        printf("c");
}
Answer
a
Why: x > 1 is true (5 > 1), so "a" prints and the ENTIRE if-else chain exits. Even though x == 5 is also true, it's in an else if — it's only reached if all previous conditions were false.
Trace 5 — Pre vs Post Increment
What does this print?
#include <stdio.h>
int main() {
    int x = 4, y = 4;
    printf("%d,%d\n", ++x, y--);
    printf("%d,%d\n", x, y);
    return 0;
}
Answer
5,4 5,3
Why: ++x increments x to 5 BEFORE use → prints 5. y-- uses y's current value 4 THEN decrements → prints 4, but y is now 3. Second printf confirms x stayed 5, y is now 3.
⚡ Part 2 — Basic Function & Calculation Questions
Calc 1 — Temperature Conversion
Task: Write a function double celsiusToFahrenheit(double c). Formula: F = C × 9/5 + 32. Read Celsius from user, print Fahrenheit.
#include <stdio.h>

double celsiusToFahrenheit(double c);

int main(void) {
    double c;
    printf("Enter Celsius: ");
    scanf("%lf", &c);
    printf("%.1f C = %.1f F\n", c, celsiusToFahrenheit(c));
    return 0;
}

double celsiusToFahrenheit(double c) {
    return c * 9.0 / 5.0 + 32.0;   /* use 9.0 not 9 to avoid int division */
}
Sample (input: 100)
100.0 C = 212.0 F
Common mistake: Writing c * 9 / 5 — if c is int this truncates. Use 9.0 / 5.0 or cast.
Calc 2 — Circle Area & Perimeter
Task: Write functions double area(double r) and double perimeter(double r) for a circle. Use #define PI 3.14159.
#include <stdio.h>
#define PI 3.14159

double area(double r);
double perimeter(double r);

int main(void) {
    double r;
    printf("Radius: ");
    scanf("%lf", &r);
    printf("Area:      %.2f\n", area(r));
    printf("Perimeter: %.2f\n", perimeter(r));
    return 0;
}

double area(double r)      { return PI * r * r; }
double perimeter(double r) { return 2 * PI * r; }
Sample (input: 5)
Area: 78.54 Perimeter: 31.42
Tip: #define PI 3.14159 at the top makes PI available everywhere — no need to pass it as a parameter.
Calc 3 — Power Function (loop, no math.h)
Task: Write double power(double base, int exp) that computes baseexp using a loop (handles exp ≥ 0). Test with several pairs.
#include <stdio.h>

double power(double base, int exp);

int main(void) {
    printf("2^10  = %.0f\n", power(2, 10));   /* 1024 */
    printf("3^4   = %.0f\n", power(3, 4));    /* 81   */
    printf("5^0   = %.0f\n", power(5, 0));    /* 1    */
    return 0;
}

double power(double base, int exp) {
    double result = 1.0;
    for (int i = 0; i < exp; i++)
        result *= base;     /* multiply by base exp times */
    return result;
}
Output
2^10 = 1024 3^4 = 81 5^0 = 1
Why start at 1.0? Any number to the power 0 = 1. If exp=0, the loop body never runs, so result stays 1 — correct!
Calc 4 — Max / Min of Two Numbers
Task: Write int maxOf(int a, int b) and int minOf(int a, int b). Read two integers and print both results.
#include <stdio.h>

int maxOf(int a, int b);
int minOf(int a, int b);

int main(void) {
    int x, y;
    printf("Enter two integers: ");
    scanf("%d %d", &x, &y);
    printf("Max: %d\n", maxOf(x, y));
    printf("Min: %d\n", minOf(x, y));
    return 0;
}

int maxOf(int a, int b) { return (a > b) ? a : b; }
int minOf(int a, int b) { return (a < b) ? a : b; }
Sample (input: 7 3)
Max: 7 Min: 3
Ternary operator: (condition) ? value_if_true : value_if_false — a compact single-expression if/else.
Calc 5 — Absolute Value & Odd/Even Checker
Task: Write int absVal(int n) and int isEven(int n) (returns 1 if even, 0 if odd). Test both.
#include <stdio.h>

int absVal(int n);
int isEven(int n);

int main(void) {
    int x;
    printf("Enter an integer: ");
    scanf("%d", &x);
    printf("Absolute value: %d\n", absVal(x));
    printf("%d is %s\n", x, isEven(x) ? "even" : "odd");
    return 0;
}

int absVal(int n) { return (n < 0) ? -n : n; }
int isEven(int n) { return (n % 2 == 0) ? 1 : 0; }
Sample (input: -7)
Absolute value: 7 -7 is odd
Calc 6 — Sum of Digits
Task: Write int sumDigits(int n) that returns the sum of all digits. E.g. 1234 → 1+2+3+4 = 10. Use modulo and division.
#include <stdio.h>

int sumDigits(int n);

int main(void) {
    int n;
    printf("Enter a positive integer: ");
    scanf("%d", &n);
    printf("Sum of digits of %d = %d\n", n, sumDigits(n));
    return 0;
}

int sumDigits(int n) {
    int sum = 0;
    while (n > 0) {
        sum += n % 10;   /* last digit */
        n   /= 10;       /* remove last digit */
    }
    return sum;
}
Sample (input: 1234)
Sum of digits of 1234 = 10
Pattern: n % 10 extracts the rightmost digit. n /= 10 (integer division) chops it off. Repeat until n = 0.
Calc 7 — Simple Interest Calculator
Task: Write double simpleInterest(double p, double r, int t) → SI = P × R × T / 100. Read P, R, T and print the interest and total amount.
#include <stdio.h>

double simpleInterest(double p, double r, int t);

int main(void) {
    double p, r;
    int t;
    printf("Principal: "); scanf("%lf", &p);
    printf("Rate (%%):  "); scanf("%lf", &r);
    printf("Time (yrs):"); scanf("%d",  &t);

    double si = simpleInterest(p, r, t);
    printf("Interest: $%.2f\n", si);
    printf("Total:    $%.2f\n", p + si);
    return 0;
}

double simpleInterest(double p, double r, int t) {
    return (p * r * t) / 100.0;
}
Sample (P=1000, R=5, T=3)
Interest: $150.00 Total: $1150.00
Calc 8 — Is Prime?
Task: Write int isPrime(int n) that returns 1 if n is prime, 0 otherwise. Print all primes from 2 to 50.
#include <stdio.h>

int isPrime(int n);

int main(void) {
    printf("Primes 2-50: ");
    for (int i = 2; i <= 50; i++)
        if (isPrime(i))
            printf("%d ", i);
    printf("\n");
    return 0;
}

int isPrime(int n) {
    if (n < 2) return 0;
    for (int i = 2; i * i <= n; i++)   /* only need to check up to sqrt(n) */
        if (n % i == 0) return 0;       /* found a factor — not prime */
    return 1;
}
Output
Primes 2-50: 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47
Efficiency trick: Check divisors only up to √n (i.e. i * i <= n). If n has a factor larger than √n, the corresponding smaller factor would already have been found.
📋 Copy-Paste Templates
Template A — Function that Returns a Value
Use when: function computes and gives back a single result (int, double, char, etc.)
/* 1. Prototype — goes above main */
returnType functionName(paramType param1, paramType param2);

int main(void) {
    returnType result = functionName(arg1, arg2);
    printf("Result: ...", result);
    return 0;
}

/* 2. Definition — goes after main */
returnType functionName(paramType param1, paramType param2) {
    /* ... your logic ... */
    return someValue;
}
Fill in: replace returnType with int, double, char, etc. Replace paramType with the type of each input.
Template B — Void Function (no return value)
Use when: function just prints or does an action but doesn't send a value back.
void functionName(paramType param);

int main(void) {
    functionName(arg);   /* no assignment — void returns nothing */
    return 0;
}

void functionName(paramType param) {
    /* do stuff — print, modify local things, etc. */
    /* no return statement needed (or just: return;) */
}
Template C — Function that Modifies Caller's Variable (Pointer)
Use when: you need to change a variable that lives in main(). Pass its address with &.
void doubleIt(int *n);   /* * in prototype = pointer parameter */

int main(void) {
    int x = 5;
    doubleIt(&x);           /* & sends the ADDRESS of x */
    printf("%d\n", x);     /* x is now 10 */
    return 0;
}

void doubleIt(int *n) {
    *n = *n * 2;           /* * dereferences: changes the value AT that address */
}
Rule: to READ a pointer: *n. To WRITE through it: *n = .... To pass an address from main: &variable.
Template D — Function with Array Parameter
Use when: processing a whole array (find sum, max, average, etc.). Arrays are always passed by reference — no & needed.
void processArray(int *arr, int n);   /* or int arr[] — identical */

int main(void) {
    int data[] = {10, 20, 30};
    int n = sizeof(data) / sizeof(int);  /* number of elements */
    processArray(data, n);               /* no & — array name is already an address */
    return 0;
}

void processArray(int *arr, int n) {
    for (int i = 0; i < n; i++) {
        /* arr[i] accesses each element */
    }
}
Template E — Accumulator / Running Total Pattern
Use when: summing, multiplying, counting, or finding max/min over a loop.
/* Sum */
int sum = 0;
for (int i = 0; i < n; i++)
    sum += arr[i];

/* Product */
long product = 1;          /* start at 1, not 0! */
for (int i = 1; i <= n; i++)
    product *= i;

/* Count (how many satisfy a condition) */
int count = 0;
for (int i = 0; i < n; i++)
    if (arr[i] > threshold) count++;

/* Running max */
int max = arr[0];          /* initialize to FIRST element */
for (int i = 1; i < n; i++)
    if (arr[i] > max) max = arr[i];
Key: Initialize sum/count to 0. Initialize product/max/min to sensible starting values (1, first element). Start loop at correct index.
Template F — Read N Values, Process, Print
Use when: reading an unknown-at-compile-time number of values from the user.
#include <stdio.h>

int main(void) {
    int n;
    printf("How many values? ");
    scanf("%d", &n);

    int arr[n];                      /* VLA — valid in C99+ */
    for (int i = 0; i < n; i++) {
        printf("Value %d: ", i + 1);
        scanf("%d", &arr[i]);
    }

    /* --- process --- */
    int sum = 0;
    for (int i = 0; i < n; i++)
        sum += arr[i];

    printf("Sum: %d\n", sum);
    printf("Avg: %.2f\n", (float)sum / n);
    return 0;
}
↑ Top